mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03:00
Resurrect heap_deformtuple(), this time implemented as a singly nested
loop over the fields instead of a loop around heap_getattr. This is considerably faster (O(N) instead of O(N^2)) when there are nulls or varlena fields, since those prevent use of attcacheoff. Replace loops over heap_getattr with heap_deformtuple in situations where all or most of the fields have to be fetched, such as printtup and tuptoaster. Profiling done more than a year ago shows that this should be a nice win for situations involving many-column tables.
This commit is contained in:
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.90 2004/04/01 21:28:43 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.91 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* The old interface functions have been converted to macros
|
* The old interface functions have been converted to macros
|
||||||
@ -37,10 +37,10 @@
|
|||||||
*/
|
*/
|
||||||
Size
|
Size
|
||||||
ComputeDataSize(TupleDesc tupleDesc,
|
ComputeDataSize(TupleDesc tupleDesc,
|
||||||
Datum *value,
|
Datum *values,
|
||||||
char *nulls)
|
char *nulls)
|
||||||
{
|
{
|
||||||
uint32 data_length = 0;
|
Size data_length = 0;
|
||||||
int i;
|
int i;
|
||||||
int numberOfAttributes = tupleDesc->natts;
|
int numberOfAttributes = tupleDesc->natts;
|
||||||
Form_pg_attribute *att = tupleDesc->attrs;
|
Form_pg_attribute *att = tupleDesc->attrs;
|
||||||
@ -51,7 +51,7 @@ ComputeDataSize(TupleDesc tupleDesc,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
data_length = att_align(data_length, att[i]->attalign);
|
data_length = att_align(data_length, att[i]->attalign);
|
||||||
data_length = att_addlength(data_length, att[i]->attlen, value[i]);
|
data_length = att_addlength(data_length, att[i]->attlen, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data_length;
|
return data_length;
|
||||||
@ -59,19 +59,20 @@ ComputeDataSize(TupleDesc tupleDesc,
|
|||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* DataFill
|
* DataFill
|
||||||
|
*
|
||||||
|
* Load data portion of a tuple from values/nulls arrays
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
DataFill(char *data,
|
DataFill(char *data,
|
||||||
TupleDesc tupleDesc,
|
TupleDesc tupleDesc,
|
||||||
Datum *value,
|
Datum *values,
|
||||||
char *nulls,
|
char *nulls,
|
||||||
uint16 *infomask,
|
uint16 *infomask,
|
||||||
bits8 *bit)
|
bits8 *bit)
|
||||||
{
|
{
|
||||||
bits8 *bitP = 0;
|
bits8 *bitP;
|
||||||
int bitmask = 0;
|
int bitmask;
|
||||||
Size data_length;
|
|
||||||
int i;
|
int i;
|
||||||
int numberOfAttributes = tupleDesc->natts;
|
int numberOfAttributes = tupleDesc->natts;
|
||||||
Form_pg_attribute *att = tupleDesc->attrs;
|
Form_pg_attribute *att = tupleDesc->attrs;
|
||||||
@ -81,11 +82,19 @@ DataFill(char *data,
|
|||||||
bitP = &bit[-1];
|
bitP = &bit[-1];
|
||||||
bitmask = CSIGNBIT;
|
bitmask = CSIGNBIT;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* just to keep compiler quiet */
|
||||||
|
bitP = NULL;
|
||||||
|
bitmask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
|
*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
|
||||||
|
|
||||||
for (i = 0; i < numberOfAttributes; i++)
|
for (i = 0; i < numberOfAttributes; i++)
|
||||||
{
|
{
|
||||||
|
Size data_length;
|
||||||
|
|
||||||
if (bit != NULL)
|
if (bit != NULL)
|
||||||
{
|
{
|
||||||
if (bitmask != CSIGNBIT)
|
if (bitmask != CSIGNBIT)
|
||||||
@ -112,33 +121,33 @@ DataFill(char *data,
|
|||||||
if (att[i]->attbyval)
|
if (att[i]->attbyval)
|
||||||
{
|
{
|
||||||
/* pass-by-value */
|
/* pass-by-value */
|
||||||
store_att_byval(data, value[i], att[i]->attlen);
|
store_att_byval(data, values[i], att[i]->attlen);
|
||||||
data_length = att[i]->attlen;
|
data_length = att[i]->attlen;
|
||||||
}
|
}
|
||||||
else if (att[i]->attlen == -1)
|
else if (att[i]->attlen == -1)
|
||||||
{
|
{
|
||||||
/* varlena */
|
/* varlena */
|
||||||
*infomask |= HEAP_HASVARWIDTH;
|
*infomask |= HEAP_HASVARWIDTH;
|
||||||
if (VARATT_IS_EXTERNAL(value[i]))
|
if (VARATT_IS_EXTERNAL(values[i]))
|
||||||
*infomask |= HEAP_HASEXTERNAL;
|
*infomask |= HEAP_HASEXTERNAL;
|
||||||
if (VARATT_IS_COMPRESSED(value[i]))
|
if (VARATT_IS_COMPRESSED(values[i]))
|
||||||
*infomask |= HEAP_HASCOMPRESSED;
|
*infomask |= HEAP_HASCOMPRESSED;
|
||||||
data_length = VARATT_SIZE(DatumGetPointer(value[i]));
|
data_length = VARATT_SIZE(DatumGetPointer(values[i]));
|
||||||
memcpy(data, DatumGetPointer(value[i]), data_length);
|
memcpy(data, DatumGetPointer(values[i]), data_length);
|
||||||
}
|
}
|
||||||
else if (att[i]->attlen == -2)
|
else if (att[i]->attlen == -2)
|
||||||
{
|
{
|
||||||
/* cstring */
|
/* cstring */
|
||||||
*infomask |= HEAP_HASVARWIDTH;
|
*infomask |= HEAP_HASVARWIDTH;
|
||||||
data_length = strlen(DatumGetCString(value[i])) + 1;
|
data_length = strlen(DatumGetCString(values[i])) + 1;
|
||||||
memcpy(data, DatumGetPointer(value[i]), data_length);
|
memcpy(data, DatumGetPointer(values[i]), data_length);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* fixed-length pass-by-reference */
|
/* fixed-length pass-by-reference */
|
||||||
Assert(att[i]->attlen > 0);
|
Assert(att[i]->attlen > 0);
|
||||||
data_length = att[i]->attlen;
|
data_length = att[i]->attlen;
|
||||||
memcpy(data, DatumGetPointer(value[i]), data_length);
|
memcpy(data, DatumGetPointer(values[i]), data_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
data += data_length;
|
data += data_length;
|
||||||
@ -160,12 +169,13 @@ heap_attisnull(HeapTuple tup, int attnum)
|
|||||||
if (attnum > (int) tup->t_data->t_natts)
|
if (attnum > (int) tup->t_data->t_natts)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
if (attnum > 0)
|
||||||
|
{
|
||||||
if (HeapTupleNoNulls(tup))
|
if (HeapTupleNoNulls(tup))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (attnum > 0)
|
|
||||||
return att_isnull(attnum - 1, tup->t_data->t_bits);
|
return att_isnull(attnum - 1, tup->t_data->t_bits);
|
||||||
else
|
}
|
||||||
|
|
||||||
switch (attnum)
|
switch (attnum)
|
||||||
{
|
{
|
||||||
case TableOidAttributeNumber:
|
case TableOidAttributeNumber:
|
||||||
@ -202,6 +212,8 @@ heap_attisnull(HeapTuple tup, int attnum)
|
|||||||
* perform well for queries which hit large #'s of tuples. After
|
* perform well for queries which hit large #'s of tuples. After
|
||||||
* you cache the offsets once, examining all the other tuples using
|
* you cache the offsets once, examining all the other tuples using
|
||||||
* the same attribute descriptor will go much quicker. -cim 5/4/91
|
* the same attribute descriptor will go much quicker. -cim 5/4/91
|
||||||
|
*
|
||||||
|
* NOTE: if you need to change this code, see also heap_deformtuple.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
@ -536,53 +548,18 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
|
|||||||
memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len);
|
memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NOT_USED
|
|
||||||
/* ----------------
|
|
||||||
* heap_deformtuple
|
|
||||||
*
|
|
||||||
* the inverse of heap_formtuple (see below)
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
heap_deformtuple(HeapTuple tuple,
|
|
||||||
TupleDesc tdesc,
|
|
||||||
Datum *values,
|
|
||||||
char *nulls)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int natts;
|
|
||||||
|
|
||||||
Assert(HeapTupleIsValid(tuple));
|
|
||||||
|
|
||||||
natts = tuple->t_natts;
|
|
||||||
for (i = 0; i < natts; i++)
|
|
||||||
{
|
|
||||||
bool isnull;
|
|
||||||
|
|
||||||
values[i] = heap_getattr(tuple,
|
|
||||||
i + 1,
|
|
||||||
tdesc,
|
|
||||||
&isnull);
|
|
||||||
if (isnull)
|
|
||||||
nulls[i] = 'n';
|
|
||||||
else
|
|
||||||
nulls[i] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* heap_formtuple
|
* heap_formtuple
|
||||||
*
|
*
|
||||||
* constructs a tuple from the given *value and *nulls arrays
|
* construct a tuple from the given values[] and nulls[] arrays
|
||||||
*
|
*
|
||||||
* Null attributes are indicated by a 'n' in the appropriate byte
|
* Null attributes are indicated by a 'n' in the appropriate byte
|
||||||
* of *nulls. Non-null attributes are indicated by a ' ' (space).
|
* of nulls[]. Non-null attributes are indicated by a ' ' (space).
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
HeapTuple
|
HeapTuple
|
||||||
heap_formtuple(TupleDesc tupleDescriptor,
|
heap_formtuple(TupleDesc tupleDescriptor,
|
||||||
Datum *value,
|
Datum *values,
|
||||||
char *nulls)
|
char *nulls)
|
||||||
{
|
{
|
||||||
HeapTuple tuple; /* return tuple */
|
HeapTuple tuple; /* return tuple */
|
||||||
@ -621,7 +598,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
|
|||||||
|
|
||||||
hoff = len = MAXALIGN(len); /* align user data safely */
|
hoff = len = MAXALIGN(len); /* align user data safely */
|
||||||
|
|
||||||
len += ComputeDataSize(tupleDescriptor, value, nulls);
|
len += ComputeDataSize(tupleDescriptor, values, nulls);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate and zero the space needed. Note that the tuple body and
|
* Allocate and zero the space needed. Note that the tuple body and
|
||||||
@ -651,7 +628,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
|
|||||||
|
|
||||||
DataFill((char *) td + hoff,
|
DataFill((char *) td + hoff,
|
||||||
tupleDescriptor,
|
tupleDescriptor,
|
||||||
value,
|
values,
|
||||||
nulls,
|
nulls,
|
||||||
&td->t_infomask,
|
&td->t_infomask,
|
||||||
(hasnull ? td->t_bits : NULL));
|
(hasnull ? td->t_bits : NULL));
|
||||||
@ -664,68 +641,59 @@ heap_formtuple(TupleDesc tupleDescriptor,
|
|||||||
*
|
*
|
||||||
* forms a new tuple from an old tuple and a set of replacement values.
|
* forms a new tuple from an old tuple and a set of replacement values.
|
||||||
* returns a new palloc'ed tuple.
|
* returns a new palloc'ed tuple.
|
||||||
|
*
|
||||||
|
* XXX it is misdesign that this is passed a Relation and not just a
|
||||||
|
* TupleDesc to describe the tuple structure.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
HeapTuple
|
HeapTuple
|
||||||
heap_modifytuple(HeapTuple tuple,
|
heap_modifytuple(HeapTuple tuple,
|
||||||
Relation relation,
|
Relation relation,
|
||||||
Datum *replValue,
|
Datum *replValues,
|
||||||
char *replNull,
|
char *replNulls,
|
||||||
char *repl)
|
char *replActions)
|
||||||
{
|
{
|
||||||
|
TupleDesc tupleDesc = RelationGetDescr(relation);
|
||||||
|
int numberOfAttributes = tupleDesc->natts;
|
||||||
int attoff;
|
int attoff;
|
||||||
int numberOfAttributes;
|
Datum *values;
|
||||||
Datum *value;
|
|
||||||
char *nulls;
|
char *nulls;
|
||||||
bool isNull;
|
|
||||||
HeapTuple newTuple;
|
HeapTuple newTuple;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sanity checks
|
* allocate and fill values and nulls arrays from either the tuple or
|
||||||
*/
|
|
||||||
Assert(HeapTupleIsValid(tuple));
|
|
||||||
Assert(RelationIsValid(relation));
|
|
||||||
Assert(PointerIsValid(replValue));
|
|
||||||
Assert(PointerIsValid(replNull));
|
|
||||||
Assert(PointerIsValid(repl));
|
|
||||||
|
|
||||||
numberOfAttributes = RelationGetForm(relation)->relnatts;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* allocate and fill *value and *nulls arrays from either the tuple or
|
|
||||||
* the repl information, as appropriate.
|
* the repl information, as appropriate.
|
||||||
|
*
|
||||||
|
* NOTE: it's debatable whether to use heap_deformtuple() here or
|
||||||
|
* just heap_getattr() only the non-replaced colums. The latter could
|
||||||
|
* win if there are many replaced columns and few non-replaced ones.
|
||||||
|
* However, heap_deformtuple costs only O(N) while the heap_getattr
|
||||||
|
* way would cost O(N^2) if there are many non-replaced columns, so it
|
||||||
|
* seems better to err on the side of linear cost.
|
||||||
*/
|
*/
|
||||||
value = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
|
values = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
|
||||||
nulls = (char *) palloc(numberOfAttributes * sizeof(char));
|
nulls = (char *) palloc(numberOfAttributes * sizeof(char));
|
||||||
|
|
||||||
|
heap_deformtuple(tuple, tupleDesc, values, nulls);
|
||||||
|
|
||||||
for (attoff = 0; attoff < numberOfAttributes; attoff++)
|
for (attoff = 0; attoff < numberOfAttributes; attoff++)
|
||||||
{
|
{
|
||||||
if (repl[attoff] == ' ')
|
if (replActions[attoff] == 'r')
|
||||||
{
|
{
|
||||||
value[attoff] = heap_getattr(tuple,
|
values[attoff] = replValues[attoff];
|
||||||
AttrOffsetGetAttrNumber(attoff),
|
nulls[attoff] = replNulls[attoff];
|
||||||
RelationGetDescr(relation),
|
|
||||||
&isNull);
|
|
||||||
nulls[attoff] = (isNull) ? 'n' : ' ';
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (repl[attoff] == 'r')
|
else if (replActions[attoff] != ' ')
|
||||||
{
|
elog(ERROR, "unrecognized replace flag: %d",
|
||||||
value[attoff] = replValue[attoff];
|
(int) replActions[attoff]);
|
||||||
nulls[attoff] = replNull[attoff];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elog(ERROR, "unrecognized replace flag: %d", (int) repl[attoff]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create a new tuple from the *values and *nulls arrays
|
* create a new tuple from the values and nulls arrays
|
||||||
*/
|
*/
|
||||||
newTuple = heap_formtuple(RelationGetDescr(relation),
|
newTuple = heap_formtuple(tupleDesc, values, nulls);
|
||||||
value,
|
|
||||||
nulls);
|
|
||||||
|
|
||||||
pfree(value);
|
pfree(values);
|
||||||
pfree(nulls);
|
pfree(nulls);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -735,12 +703,96 @@ heap_modifytuple(HeapTuple tuple,
|
|||||||
newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
|
newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
|
||||||
newTuple->t_self = tuple->t_self;
|
newTuple->t_self = tuple->t_self;
|
||||||
newTuple->t_tableOid = tuple->t_tableOid;
|
newTuple->t_tableOid = tuple->t_tableOid;
|
||||||
if (relation->rd_rel->relhasoids)
|
if (tupleDesc->tdhasoid)
|
||||||
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
|
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
|
||||||
|
|
||||||
return newTuple;
|
return newTuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* heap_deformtuple
|
||||||
|
*
|
||||||
|
* Given a tuple, extract data into values/nulls arrays; this is
|
||||||
|
* the inverse of heap_formtuple.
|
||||||
|
*
|
||||||
|
* Storage for the values/nulls arrays is provided by the caller;
|
||||||
|
* it should be sized according to tupleDesc->natts not tuple->t_natts.
|
||||||
|
*
|
||||||
|
* Note that for pass-by-reference datatypes, the pointer placed
|
||||||
|
* in the Datum will point into the given tuple.
|
||||||
|
*
|
||||||
|
* When all or most of a tuple's fields need to be extracted,
|
||||||
|
* this routine will be significantly quicker than a loop around
|
||||||
|
* heap_getattr; the loop will become O(N^2) as soon as any
|
||||||
|
* noncacheable attribute offsets are involved.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
heap_deformtuple(HeapTuple tuple,
|
||||||
|
TupleDesc tupleDesc,
|
||||||
|
Datum *values,
|
||||||
|
char *nulls)
|
||||||
|
{
|
||||||
|
HeapTupleHeader tup = tuple->t_data;
|
||||||
|
Form_pg_attribute *att = tupleDesc->attrs;
|
||||||
|
int tdesc_natts = tupleDesc->natts;
|
||||||
|
int natts; /* number of atts to extract */
|
||||||
|
int attnum;
|
||||||
|
char *tp; /* ptr to tuple data */
|
||||||
|
long off; /* offset in tuple data */
|
||||||
|
bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */
|
||||||
|
bool slow = false; /* can we use/set attcacheoff? */
|
||||||
|
|
||||||
|
natts = tup->t_natts;
|
||||||
|
/* This min() operation is pure paranoia */
|
||||||
|
natts = Min(natts, tdesc_natts);
|
||||||
|
|
||||||
|
tp = (char *) tup + tup->t_hoff;
|
||||||
|
|
||||||
|
off = 0;
|
||||||
|
|
||||||
|
for (attnum = 0; attnum < natts; attnum++)
|
||||||
|
{
|
||||||
|
if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp))
|
||||||
|
{
|
||||||
|
values[attnum] = (Datum) 0;
|
||||||
|
nulls[attnum] = 'n';
|
||||||
|
slow = true; /* can't use attcacheoff anymore */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nulls[attnum] = ' ';
|
||||||
|
|
||||||
|
if (!slow && att[attnum]->attcacheoff >= 0)
|
||||||
|
{
|
||||||
|
off = att[attnum]->attcacheoff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off = att_align(off, att[attnum]->attalign);
|
||||||
|
|
||||||
|
if (!slow)
|
||||||
|
att[attnum]->attcacheoff = off;
|
||||||
|
}
|
||||||
|
|
||||||
|
values[attnum] = fetchatt(att[attnum], tp + off);
|
||||||
|
|
||||||
|
off = att_addlength(off, att[attnum]->attlen, tp + off);
|
||||||
|
|
||||||
|
if (att[attnum]->attlen <= 0)
|
||||||
|
slow = true; /* can't use attcacheoff anymore */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If tuple doesn't have all the atts indicated by tupleDesc, read
|
||||||
|
* the rest as null
|
||||||
|
*/
|
||||||
|
for (; attnum < tdesc_natts; attnum++)
|
||||||
|
{
|
||||||
|
values[attnum] = (Datum) 0;
|
||||||
|
nulls[attnum] = 'n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* heap_freetuple
|
* heap_freetuple
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.81 2004/05/26 04:41:03 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.82 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -65,6 +65,8 @@ typedef struct
|
|||||||
TupleDesc attrinfo; /* The attr info we are set up for */
|
TupleDesc attrinfo; /* The attr info we are set up for */
|
||||||
int nattrs;
|
int nattrs;
|
||||||
PrinttupAttrInfo *myinfo; /* Cached info about each attr */
|
PrinttupAttrInfo *myinfo; /* Cached info about each attr */
|
||||||
|
Datum *values; /* preallocated space for deformtuple */
|
||||||
|
char *nulls;
|
||||||
} DR_printtup;
|
} DR_printtup;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -103,6 +105,8 @@ printtup_create_DR(CommandDest dest, Portal portal)
|
|||||||
self->attrinfo = NULL;
|
self->attrinfo = NULL;
|
||||||
self->nattrs = 0;
|
self->nattrs = 0;
|
||||||
self->myinfo = NULL;
|
self->myinfo = NULL;
|
||||||
|
self->values = NULL;
|
||||||
|
self->nulls = NULL;
|
||||||
|
|
||||||
return (DestReceiver *) self;
|
return (DestReceiver *) self;
|
||||||
}
|
}
|
||||||
@ -243,15 +247,27 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
|
|||||||
int16 *formats = myState->portal->formats;
|
int16 *formats = myState->portal->formats;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* get rid of any old data */
|
||||||
if (myState->myinfo)
|
if (myState->myinfo)
|
||||||
pfree(myState->myinfo); /* get rid of any old data */
|
pfree(myState->myinfo);
|
||||||
myState->myinfo = NULL;
|
myState->myinfo = NULL;
|
||||||
|
if (myState->values)
|
||||||
|
pfree(myState->values);
|
||||||
|
myState->values = NULL;
|
||||||
|
if (myState->nulls)
|
||||||
|
pfree(myState->nulls);
|
||||||
|
myState->nulls = NULL;
|
||||||
|
|
||||||
myState->attrinfo = typeinfo;
|
myState->attrinfo = typeinfo;
|
||||||
myState->nattrs = numAttrs;
|
myState->nattrs = numAttrs;
|
||||||
if (numAttrs <= 0)
|
if (numAttrs <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
myState->myinfo = (PrinttupAttrInfo *)
|
myState->myinfo = (PrinttupAttrInfo *)
|
||||||
palloc0(numAttrs * sizeof(PrinttupAttrInfo));
|
palloc0(numAttrs * sizeof(PrinttupAttrInfo));
|
||||||
|
myState->values = (Datum *) palloc(numAttrs * sizeof(Datum));
|
||||||
|
myState->nulls = (char *) palloc(numAttrs * sizeof(char));
|
||||||
|
|
||||||
for (i = 0; i < numAttrs; i++)
|
for (i = 0; i < numAttrs; i++)
|
||||||
{
|
{
|
||||||
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
||||||
@ -297,6 +313,11 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
||||||
printtup_prepare_info(myState, typeinfo, natts);
|
printtup_prepare_info(myState, typeinfo, natts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* deconstruct the tuple (faster than a heap_getattr loop)
|
||||||
|
*/
|
||||||
|
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare a DataRow message
|
* Prepare a DataRow message
|
||||||
*/
|
*/
|
||||||
@ -310,12 +331,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
for (i = 0; i < natts; ++i)
|
for (i = 0; i < natts; ++i)
|
||||||
{
|
{
|
||||||
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
||||||
Datum origattr,
|
Datum origattr = myState->values[i],
|
||||||
attr;
|
attr;
|
||||||
bool isnull;
|
|
||||||
|
|
||||||
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
|
if (myState->nulls[i] == 'n')
|
||||||
if (isnull)
|
|
||||||
{
|
{
|
||||||
pq_sendint(&buf, -1, 4);
|
pq_sendint(&buf, -1, 4);
|
||||||
continue;
|
continue;
|
||||||
@ -383,6 +402,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
||||||
printtup_prepare_info(myState, typeinfo, natts);
|
printtup_prepare_info(myState, typeinfo, natts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* deconstruct the tuple (faster than a heap_getattr loop)
|
||||||
|
*/
|
||||||
|
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tell the frontend to expect new tuple data (in ASCII style)
|
* tell the frontend to expect new tuple data (in ASCII style)
|
||||||
*/
|
*/
|
||||||
@ -395,7 +419,7 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
k = 1 << 7;
|
k = 1 << 7;
|
||||||
for (i = 0; i < natts; ++i)
|
for (i = 0; i < natts; ++i)
|
||||||
{
|
{
|
||||||
if (!heap_attisnull(tuple, i + 1))
|
if (myState->nulls[i] != 'n')
|
||||||
j |= k; /* set bit if not null */
|
j |= k; /* set bit if not null */
|
||||||
k >>= 1;
|
k >>= 1;
|
||||||
if (k == 0) /* end of byte? */
|
if (k == 0) /* end of byte? */
|
||||||
@ -414,13 +438,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
for (i = 0; i < natts; ++i)
|
for (i = 0; i < natts; ++i)
|
||||||
{
|
{
|
||||||
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
||||||
Datum origattr,
|
Datum origattr = myState->values[i],
|
||||||
attr;
|
attr;
|
||||||
bool isnull;
|
|
||||||
char *outputstr;
|
char *outputstr;
|
||||||
|
|
||||||
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
|
if (myState->nulls[i] == 'n')
|
||||||
if (isnull)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Assert(thisState->format == 0);
|
Assert(thisState->format == 0);
|
||||||
@ -461,6 +483,13 @@ printtup_shutdown(DestReceiver *self)
|
|||||||
if (myState->myinfo)
|
if (myState->myinfo)
|
||||||
pfree(myState->myinfo);
|
pfree(myState->myinfo);
|
||||||
myState->myinfo = NULL;
|
myState->myinfo = NULL;
|
||||||
|
if (myState->values)
|
||||||
|
pfree(myState->values);
|
||||||
|
myState->values = NULL;
|
||||||
|
if (myState->nulls)
|
||||||
|
pfree(myState->nulls);
|
||||||
|
myState->nulls = NULL;
|
||||||
|
|
||||||
myState->attrinfo = NULL;
|
myState->attrinfo = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,6 +616,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
||||||
printtup_prepare_info(myState, typeinfo, natts);
|
printtup_prepare_info(myState, typeinfo, natts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* deconstruct the tuple (faster than a heap_getattr loop)
|
||||||
|
*/
|
||||||
|
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tell the frontend to expect new tuple data (in binary style)
|
* tell the frontend to expect new tuple data (in binary style)
|
||||||
*/
|
*/
|
||||||
@ -599,7 +633,7 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
k = 1 << 7;
|
k = 1 << 7;
|
||||||
for (i = 0; i < natts; ++i)
|
for (i = 0; i < natts; ++i)
|
||||||
{
|
{
|
||||||
if (!heap_attisnull(tuple, i + 1))
|
if (myState->nulls[i] != 'n')
|
||||||
j |= k; /* set bit if not null */
|
j |= k; /* set bit if not null */
|
||||||
k >>= 1;
|
k >>= 1;
|
||||||
if (k == 0) /* end of byte? */
|
if (k == 0) /* end of byte? */
|
||||||
@ -618,13 +652,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||||||
for (i = 0; i < natts; ++i)
|
for (i = 0; i < natts; ++i)
|
||||||
{
|
{
|
||||||
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
||||||
Datum origattr,
|
Datum origattr = myState->values[i],
|
||||||
attr;
|
attr;
|
||||||
bool isnull;
|
|
||||||
bytea *outputbytes;
|
bytea *outputbytes;
|
||||||
|
|
||||||
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
|
if (myState->nulls[i] == 'n')
|
||||||
if (isnull)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Assert(thisState->format == 1);
|
Assert(thisState->format == 1);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.41 2003/11/29 19:51:40 pgsql Exp $
|
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.42 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
@ -281,15 +281,26 @@ toast_delete(Relation rel, HeapTuple oldtup)
|
|||||||
Form_pg_attribute *att;
|
Form_pg_attribute *att;
|
||||||
int numAttrs;
|
int numAttrs;
|
||||||
int i;
|
int i;
|
||||||
Datum value;
|
Datum toast_values[MaxHeapAttributeNumber];
|
||||||
bool isnull;
|
char toast_nulls[MaxHeapAttributeNumber];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the tuple descriptor, the number of and attribute descriptors.
|
* Get the tuple descriptor and break down the tuple into fields.
|
||||||
|
*
|
||||||
|
* NOTE: it's debatable whether to use heap_deformtuple() here or
|
||||||
|
* just heap_getattr() only the varlena columns. The latter could
|
||||||
|
* win if there are few varlena columns and many non-varlena ones.
|
||||||
|
* However, heap_deformtuple costs only O(N) while the heap_getattr
|
||||||
|
* way would cost O(N^2) if there are many varlena columns, so it
|
||||||
|
* seems better to err on the side of linear cost. (We won't even
|
||||||
|
* be here unless there's at least one varlena column, by the way.)
|
||||||
*/
|
*/
|
||||||
tupleDesc = rel->rd_att;
|
tupleDesc = rel->rd_att;
|
||||||
numAttrs = tupleDesc->natts;
|
|
||||||
att = tupleDesc->attrs;
|
att = tupleDesc->attrs;
|
||||||
|
numAttrs = tupleDesc->natts;
|
||||||
|
|
||||||
|
Assert(numAttrs <= MaxHeapAttributeNumber);
|
||||||
|
heap_deformtuple(oldtup, tupleDesc, toast_values, toast_nulls);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for external stored attributes and delete them from the
|
* Check for external stored attributes and delete them from the
|
||||||
@ -299,8 +310,9 @@ toast_delete(Relation rel, HeapTuple oldtup)
|
|||||||
{
|
{
|
||||||
if (att[i]->attlen == -1)
|
if (att[i]->attlen == -1)
|
||||||
{
|
{
|
||||||
value = heap_getattr(oldtup, i + 1, tupleDesc, &isnull);
|
Datum value = toast_values[i];
|
||||||
if (!isnull && VARATT_IS_EXTERNAL(value))
|
|
||||||
|
if (toast_nulls[i] != 'n' && VARATT_IS_EXTERNAL(value))
|
||||||
toast_delete_datum(rel, value);
|
toast_delete_datum(rel, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,8 +333,6 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|||||||
Form_pg_attribute *att;
|
Form_pg_attribute *att;
|
||||||
int numAttrs;
|
int numAttrs;
|
||||||
int i;
|
int i;
|
||||||
bool old_isnull;
|
|
||||||
bool new_isnull;
|
|
||||||
|
|
||||||
bool need_change = false;
|
bool need_change = false;
|
||||||
bool need_free = false;
|
bool need_free = false;
|
||||||
@ -333,18 +343,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|||||||
|
|
||||||
char toast_action[MaxHeapAttributeNumber];
|
char toast_action[MaxHeapAttributeNumber];
|
||||||
char toast_nulls[MaxHeapAttributeNumber];
|
char toast_nulls[MaxHeapAttributeNumber];
|
||||||
|
char toast_oldnulls[MaxHeapAttributeNumber];
|
||||||
Datum toast_values[MaxHeapAttributeNumber];
|
Datum toast_values[MaxHeapAttributeNumber];
|
||||||
|
Datum toast_oldvalues[MaxHeapAttributeNumber];
|
||||||
int32 toast_sizes[MaxHeapAttributeNumber];
|
int32 toast_sizes[MaxHeapAttributeNumber];
|
||||||
bool toast_free[MaxHeapAttributeNumber];
|
bool toast_free[MaxHeapAttributeNumber];
|
||||||
bool toast_delold[MaxHeapAttributeNumber];
|
bool toast_delold[MaxHeapAttributeNumber];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the tuple descriptor, the number of and attribute descriptors
|
* Get the tuple descriptor and break down the tuple(s) into fields.
|
||||||
* and the location of the tuple values.
|
|
||||||
*/
|
*/
|
||||||
tupleDesc = rel->rd_att;
|
tupleDesc = rel->rd_att;
|
||||||
numAttrs = tupleDesc->natts;
|
|
||||||
att = tupleDesc->attrs;
|
att = tupleDesc->attrs;
|
||||||
|
numAttrs = tupleDesc->natts;
|
||||||
|
|
||||||
|
Assert(numAttrs <= MaxHeapAttributeNumber);
|
||||||
|
heap_deformtuple(newtup, tupleDesc, toast_values, toast_nulls);
|
||||||
|
if (oldtup != NULL)
|
||||||
|
heap_deformtuple(oldtup, tupleDesc, toast_oldvalues, toast_oldnulls);
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* Then collect information about the values given
|
* Then collect information about the values given
|
||||||
@ -353,12 +369,15 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|||||||
* ' ' default handling
|
* ' ' default handling
|
||||||
* 'p' already processed --- don't touch it
|
* 'p' already processed --- don't touch it
|
||||||
* 'x' incompressible, but OK to move off
|
* 'x' incompressible, but OK to move off
|
||||||
|
*
|
||||||
|
* NOTE: toast_sizes[i] is only made valid for varlena attributes with
|
||||||
|
* toast_action[i] different from 'p'.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
memset(toast_action, ' ', numAttrs * sizeof(char));
|
memset(toast_action, ' ', numAttrs * sizeof(char));
|
||||||
memset(toast_nulls, ' ', numAttrs * sizeof(char));
|
|
||||||
memset(toast_free, 0, numAttrs * sizeof(bool));
|
memset(toast_free, 0, numAttrs * sizeof(bool));
|
||||||
memset(toast_delold, 0, numAttrs * sizeof(bool));
|
memset(toast_delold, 0, numAttrs * sizeof(bool));
|
||||||
|
|
||||||
for (i = 0; i < numAttrs; i++)
|
for (i = 0; i < numAttrs; i++)
|
||||||
{
|
{
|
||||||
varattrib *old_value;
|
varattrib *old_value;
|
||||||
@ -369,27 +388,24 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|||||||
/*
|
/*
|
||||||
* For UPDATE get the old and new values of this attribute
|
* For UPDATE get the old and new values of this attribute
|
||||||
*/
|
*/
|
||||||
old_value = (varattrib *) DatumGetPointer(
|
old_value = (varattrib *) DatumGetPointer(toast_oldvalues[i]);
|
||||||
heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull));
|
|
||||||
toast_values[i] =
|
|
||||||
heap_getattr(newtup, i + 1, tupleDesc, &new_isnull);
|
|
||||||
new_value = (varattrib *) DatumGetPointer(toast_values[i]);
|
new_value = (varattrib *) DatumGetPointer(toast_values[i]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the old value is an external stored one, check if it has
|
* If the old value is an external stored one, check if it has
|
||||||
* changed so we have to delete it later.
|
* changed so we have to delete it later.
|
||||||
*/
|
*/
|
||||||
if (!old_isnull && att[i]->attlen == -1 &&
|
if (att[i]->attlen == -1 && toast_oldnulls[i] != 'n' &&
|
||||||
VARATT_IS_EXTERNAL(old_value))
|
VARATT_IS_EXTERNAL(old_value))
|
||||||
{
|
{
|
||||||
if (new_isnull || !VARATT_IS_EXTERNAL(new_value) ||
|
if (toast_nulls[i] == 'n' || !VARATT_IS_EXTERNAL(new_value) ||
|
||||||
old_value->va_content.va_external.va_valueid !=
|
old_value->va_content.va_external.va_valueid !=
|
||||||
new_value->va_content.va_external.va_valueid ||
|
new_value->va_content.va_external.va_valueid ||
|
||||||
old_value->va_content.va_external.va_toastrelid !=
|
old_value->va_content.va_external.va_toastrelid !=
|
||||||
new_value->va_content.va_external.va_toastrelid)
|
new_value->va_content.va_external.va_toastrelid)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The old external store value isn't needed any more
|
* The old external stored value isn't needed any more
|
||||||
* after the update
|
* after the update
|
||||||
*/
|
*/
|
||||||
toast_delold[i] = true;
|
toast_delold[i] = true;
|
||||||
@ -413,23 +429,21 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|||||||
/*
|
/*
|
||||||
* For INSERT simply get the new value
|
* For INSERT simply get the new value
|
||||||
*/
|
*/
|
||||||
toast_values[i] =
|
new_value = (varattrib *) DatumGetPointer(toast_values[i]);
|
||||||
heap_getattr(newtup, i + 1, tupleDesc, &new_isnull);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle NULL attributes
|
* Handle NULL attributes
|
||||||
*/
|
*/
|
||||||
if (new_isnull)
|
if (toast_nulls[i] == 'n')
|
||||||
{
|
{
|
||||||
toast_action[i] = 'p';
|
toast_action[i] = 'p';
|
||||||
toast_nulls[i] = 'n';
|
|
||||||
has_nulls = true;
|
has_nulls = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now look at varsize attributes
|
* Now look at varlena attributes
|
||||||
*/
|
*/
|
||||||
if (att[i]->attlen == -1)
|
if (att[i]->attlen == -1)
|
||||||
{
|
{
|
||||||
@ -461,10 +475,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Not a variable size attribute, plain storage always
|
* Not a varlena attribute, plain storage always
|
||||||
*/
|
*/
|
||||||
toast_action[i] = 'p';
|
toast_action[i] = 'p';
|
||||||
toast_sizes[i] = att[i]->attlen;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,8 +781,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|||||||
if (need_delold)
|
if (need_delold)
|
||||||
for (i = 0; i < numAttrs; i++)
|
for (i = 0; i < numAttrs; i++)
|
||||||
if (toast_delold[i])
|
if (toast_delold[i])
|
||||||
toast_delete_datum(rel,
|
toast_delete_datum(rel, toast_oldvalues[i]);
|
||||||
heap_getattr(oldtup, i + 1, tupleDesc, &old_isnull));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.109 2004/06/02 21:01:08 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.110 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2342,10 +2342,10 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|||||||
newslot = MakeTupleTableSlot();
|
newslot = MakeTupleTableSlot();
|
||||||
ExecSetSlotDescriptor(newslot, newTupDesc, false);
|
ExecSetSlotDescriptor(newslot, newTupDesc, false);
|
||||||
|
|
||||||
/* Preallocate values/nulls arrays (+1 in case natts==0) */
|
/* Preallocate values/nulls arrays */
|
||||||
i = Max(newTupDesc->natts, oldTupDesc->natts);
|
i = Max(newTupDesc->natts, oldTupDesc->natts);
|
||||||
values = (Datum *) palloc(i * sizeof(Datum) + 1);
|
values = (Datum *) palloc(i * sizeof(Datum));
|
||||||
nulls = (char *) palloc(i * sizeof(char) + 1);
|
nulls = (char *) palloc(i * sizeof(char));
|
||||||
memset(values, 0, i * sizeof(Datum));
|
memset(values, 0, i * sizeof(Datum));
|
||||||
memset(nulls, 'n', i * sizeof(char));
|
memset(nulls, 'n', i * sizeof(char));
|
||||||
|
|
||||||
@ -2363,24 +2363,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|||||||
* Extract data from old tuple. We can force to null any
|
* Extract data from old tuple. We can force to null any
|
||||||
* columns that are deleted according to the new tuple.
|
* columns that are deleted according to the new tuple.
|
||||||
*/
|
*/
|
||||||
int natts = oldTupDesc->natts;
|
int natts = newTupDesc->natts;
|
||||||
bool isNull;
|
|
||||||
|
heap_deformtuple(tuple, oldTupDesc, values, nulls);
|
||||||
|
|
||||||
for (i = 0; i < natts; i++)
|
for (i = 0; i < natts; i++)
|
||||||
{
|
{
|
||||||
if (newTupDesc->attrs[i]->attisdropped)
|
if (newTupDesc->attrs[i]->attisdropped)
|
||||||
nulls[i] = 'n';
|
nulls[i] = 'n';
|
||||||
else
|
|
||||||
{
|
|
||||||
values[i] = heap_getattr(tuple,
|
|
||||||
i + 1,
|
|
||||||
oldTupDesc,
|
|
||||||
&isNull);
|
|
||||||
if (isNull)
|
|
||||||
nulls[i] = 'n';
|
|
||||||
else
|
|
||||||
nulls[i] = ' ';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2393,6 +2383,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|||||||
foreach(l, tab->newvals)
|
foreach(l, tab->newvals)
|
||||||
{
|
{
|
||||||
NewColumnValue *ex = lfirst(l);
|
NewColumnValue *ex = lfirst(l);
|
||||||
|
bool isNull;
|
||||||
|
|
||||||
values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
|
values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
|
||||||
econtext,
|
econtext,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.57 2004/05/26 04:41:12 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.58 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
* DESCRIPTION
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* The "DefineFoo" routines take the parse tree and pick out the
|
||||||
@ -1329,12 +1329,8 @@ AlterDomainNotNull(List *names, bool notNull)
|
|||||||
for (i = 0; i < rtc->natts; i++)
|
for (i = 0; i < rtc->natts; i++)
|
||||||
{
|
{
|
||||||
int attnum = rtc->atts[i];
|
int attnum = rtc->atts[i];
|
||||||
Datum d;
|
|
||||||
bool isNull;
|
|
||||||
|
|
||||||
d = heap_getattr(tuple, attnum, tupdesc, &isNull);
|
if (heap_attisnull(tuple, attnum))
|
||||||
|
|
||||||
if (isNull)
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_NOT_NULL_VIOLATION),
|
(errcode(ERRCODE_NOT_NULL_VIOLATION),
|
||||||
errmsg("column \"%s\" of table \"%s\" contains null values",
|
errmsg("column \"%s\" of table \"%s\" contains null values",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.40 2004/05/26 04:41:14 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.41 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -246,12 +246,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
|
|||||||
TupleDesc cleanTupType;
|
TupleDesc cleanTupType;
|
||||||
TupleDesc tupType;
|
TupleDesc tupType;
|
||||||
int cleanLength;
|
int cleanLength;
|
||||||
bool isNull;
|
|
||||||
int i;
|
int i;
|
||||||
Datum *values;
|
Datum *values;
|
||||||
char *nulls;
|
char *nulls;
|
||||||
|
Datum *old_values;
|
||||||
|
char *old_nulls;
|
||||||
Datum values_array[64];
|
Datum values_array[64];
|
||||||
|
Datum old_values_array[64];
|
||||||
char nulls_array[64];
|
char nulls_array[64];
|
||||||
|
char old_nulls_array[64];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get info from the slot and the junk filter
|
* get info from the slot and the junk filter
|
||||||
@ -265,11 +268,15 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the arrays that will hold the attribute values and the null
|
* Create the arrays that will hold the attribute values and the null
|
||||||
* information for the new "clean" tuple.
|
* information for the old tuple and new "clean" tuple.
|
||||||
*
|
*
|
||||||
* Note: we use memory on the stack to optimize things when we are
|
* Note: we use memory on the stack to optimize things when we are
|
||||||
* dealing with a small number of attributes. for large tuples we just
|
* dealing with a small number of attributes. for large tuples we just
|
||||||
* use palloc.
|
* use palloc.
|
||||||
|
*
|
||||||
|
* Note: we could use just one set of arrays if we were willing to
|
||||||
|
* assume that the resno mapping is monotonic... I think it is, but
|
||||||
|
* won't take the risk of breaking things right now.
|
||||||
*/
|
*/
|
||||||
if (cleanLength > 64)
|
if (cleanLength > 64)
|
||||||
{
|
{
|
||||||
@ -281,36 +288,52 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
|
|||||||
values = values_array;
|
values = values_array;
|
||||||
nulls = nulls_array;
|
nulls = nulls_array;
|
||||||
}
|
}
|
||||||
|
if (tupType->natts > 64)
|
||||||
|
{
|
||||||
|
old_values = (Datum *) palloc(tupType->natts * sizeof(Datum));
|
||||||
|
old_nulls = (char *) palloc(tupType->natts * sizeof(char));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
old_values = old_values_array;
|
||||||
|
old_nulls = old_nulls_array;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Exctract one by one all the values of the "clean" tuple.
|
* Extract all the values of the old tuple.
|
||||||
|
*/
|
||||||
|
heap_deformtuple(tuple, tupType, old_values, old_nulls);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transpose into proper fields of the new tuple.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < cleanLength; i++)
|
for (i = 0; i < cleanLength; i++)
|
||||||
{
|
{
|
||||||
values[i] = heap_getattr(tuple, cleanMap[i], tupType, &isNull);
|
int j = cleanMap[i] - 1;
|
||||||
|
|
||||||
if (isNull)
|
values[i] = old_values[j];
|
||||||
nulls[i] = 'n';
|
nulls[i] = old_nulls[j];
|
||||||
else
|
|
||||||
nulls[i] = ' ';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now form the new tuple.
|
* Now form the new tuple.
|
||||||
*/
|
*/
|
||||||
cleanTuple = heap_formtuple(cleanTupType,
|
cleanTuple = heap_formtuple(cleanTupType, values, nulls);
|
||||||
values,
|
|
||||||
nulls);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We are done. Free any space allocated for 'values' and 'nulls' and
|
* We are done. Free any space allocated for 'values' and 'nulls' and
|
||||||
* return the new tuple.
|
* return the new tuple.
|
||||||
*/
|
*/
|
||||||
if (cleanLength > 64)
|
if (values != values_array)
|
||||||
{
|
{
|
||||||
pfree(values);
|
pfree(values);
|
||||||
pfree(nulls);
|
pfree(nulls);
|
||||||
}
|
}
|
||||||
|
if (old_values != old_values_array)
|
||||||
|
{
|
||||||
|
pfree(old_values);
|
||||||
|
pfree(old_nulls);
|
||||||
|
}
|
||||||
|
|
||||||
return cleanTuple;
|
return cleanTuple;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.115 2004/05/30 23:40:26 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.116 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -427,7 +427,6 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
|
|||||||
int numberOfAttributes;
|
int numberOfAttributes;
|
||||||
Datum *v;
|
Datum *v;
|
||||||
char *n;
|
char *n;
|
||||||
bool isnull;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
|
if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
|
||||||
@ -448,11 +447,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
|
|||||||
n = (char *) palloc(numberOfAttributes * sizeof(char));
|
n = (char *) palloc(numberOfAttributes * sizeof(char));
|
||||||
|
|
||||||
/* fetch old values and nulls */
|
/* fetch old values and nulls */
|
||||||
for (i = 0; i < numberOfAttributes; i++)
|
heap_deformtuple(tuple, rel->rd_att, v, n);
|
||||||
{
|
|
||||||
v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
|
|
||||||
n[i] = (isnull) ? 'n' : ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* replace values and nulls */
|
/* replace values and nulls */
|
||||||
for (i = 0; i < natts; i++)
|
for (i = 0; i < natts; i++)
|
||||||
@ -474,7 +469,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
|
|||||||
mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
|
mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
|
||||||
mtuple->t_self = tuple->t_self;
|
mtuple->t_self = tuple->t_self;
|
||||||
mtuple->t_tableOid = tuple->t_tableOid;
|
mtuple->t_tableOid = tuple->t_tableOid;
|
||||||
if (rel->rd_rel->relhasoids)
|
if (rel->rd_att->tdhasoid)
|
||||||
HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
|
HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.89 2004/04/21 18:24:26 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.90 2004/06/04 20:35:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -184,9 +184,9 @@ extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf,
|
|||||||
Buffer newbuf, HeapTuple newtup);
|
Buffer newbuf, HeapTuple newtup);
|
||||||
|
|
||||||
/* in common/heaptuple.c */
|
/* in common/heaptuple.c */
|
||||||
extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *value, char *nulls);
|
extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *values, char *nulls);
|
||||||
extern void DataFill(char *data, TupleDesc tupleDesc,
|
extern void DataFill(char *data, TupleDesc tupleDesc,
|
||||||
Datum *value, char *nulls, uint16 *infomask,
|
Datum *values, char *nulls, uint16 *infomask,
|
||||||
bits8 *bit);
|
bits8 *bit);
|
||||||
extern int heap_attisnull(HeapTuple tup, int attnum);
|
extern int heap_attisnull(HeapTuple tup, int attnum);
|
||||||
extern Datum nocachegetattr(HeapTuple tup, int attnum,
|
extern Datum nocachegetattr(HeapTuple tup, int attnum,
|
||||||
@ -194,9 +194,14 @@ extern Datum nocachegetattr(HeapTuple tup, int attnum,
|
|||||||
extern HeapTuple heap_copytuple(HeapTuple tuple);
|
extern HeapTuple heap_copytuple(HeapTuple tuple);
|
||||||
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
|
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
|
||||||
extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor,
|
extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor,
|
||||||
Datum *value, char *nulls);
|
Datum *values, char *nulls);
|
||||||
extern HeapTuple heap_modifytuple(HeapTuple tuple,
|
extern HeapTuple heap_modifytuple(HeapTuple tuple,
|
||||||
Relation relation, Datum *replValue, char *replNull, char *repl);
|
Relation relation,
|
||||||
|
Datum *replValues,
|
||||||
|
char *replNulls,
|
||||||
|
char *replActions);
|
||||||
|
extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc,
|
||||||
|
Datum *values, char *nulls);
|
||||||
extern void heap_freetuple(HeapTuple tuple);
|
extern void heap_freetuple(HeapTuple tuple);
|
||||||
extern HeapTuple heap_addheader(int natts, bool withoid, Size structlen, void *structure);
|
extern HeapTuple heap_addheader(int natts, bool withoid, Size structlen, void *structure);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user