diff options
Diffstat (limited to 'src')
675 files changed, 151127 insertions, 137370 deletions
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 60ec3e4d3ab..17257690303 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -1,18 +1,18 @@ /*------------------------------------------------------------------------- * * heaptuple.c-- - * This file contains heap tuple accessor and mutator routines, as well - * as a few various tuple utilities. + * This file contains heap tuple accessor and mutator routines, as well + * as a few various tuple utilities. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/heaptuple.c,v 1.21 1997/08/26 23:31:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/heaptuple.c,v 1.22 1997/09/07 04:37:30 momjian Exp $ * * NOTES - * The old interface functions have been converted to macros - * and moved to heapam.h + * The old interface functions have been converted to macros + * and moved to heapam.h * *------------------------------------------------------------------------- */ @@ -27,9 +27,9 @@ #include <utils/memutils.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif @@ -37,902 +37,991 @@ #if !defined(NO_ASSERT_CHECKING) && defined(sparc) && defined(sunos4) #define register -#endif /* !NO_ASSERT_CHECKING && sparc && sunos4 */ +#endif /* !NO_ASSERT_CHECKING && sparc && sunos4 */ /* ---------------------------------------------------------------- - * misc support routines + * misc support routines * ---------------------------------------------------------------- */ /* ---------------- - * ComputeDataSize + * ComputeDataSize * ---------------- */ Size ComputeDataSize(TupleDesc tupleDesc, - Datum value[], - char nulls[]) + Datum value[], + char nulls[]) { - uint32 data_length; - int i; - int numberOfAttributes = tupleDesc->natts; - AttributeTupleForm *att = tupleDesc->attrs; - - for (data_length = 0, i = 0; i < numberOfAttributes; i++) { - if (nulls[i] != ' ') continue; - - switch (att[i]->attlen) { - case -1: - /* - * This is the size of the disk representation and so - * must include the additional sizeof long. - */ - if (att[i]->attalign == 'd') { - data_length = DOUBLEALIGN(data_length) - + VARSIZE(DatumGetPointer(value[i])); - } else { - data_length = INTALIGN(data_length) - + VARSIZE(DatumGetPointer(value[i])); - } - break; - case sizeof(char): - data_length++; - break; - case sizeof(short): - data_length = SHORTALIGN(data_length + sizeof(short)); - break; - case sizeof(int32): - data_length = INTALIGN(data_length + sizeof(int32)); - break; - default: - if (att[i]->attlen < sizeof(int32)) - elog(WARN, "ComputeDataSize: attribute %d has len %d", - i, att[i]->attlen); - if (att[i]->attalign == 'd') - data_length = DOUBLEALIGN(data_length) + att[i]->attlen; - else - data_length = LONGALIGN(data_length) + att[i]->attlen; - break; + uint32 data_length; + int i; + int numberOfAttributes = tupleDesc->natts; + AttributeTupleForm *att = tupleDesc->attrs; + + for (data_length = 0, i = 0; i < numberOfAttributes; i++) + { + if (nulls[i] != ' ') + continue; + + switch (att[i]->attlen) + { + case -1: + + /* + * This is the size of the disk representation and so must + * include the additional sizeof long. + */ + if (att[i]->attalign == 'd') + { + data_length = DOUBLEALIGN(data_length) + + VARSIZE(DatumGetPointer(value[i])); + } + else + { + data_length = INTALIGN(data_length) + + VARSIZE(DatumGetPointer(value[i])); + } + break; + case sizeof(char): + data_length++; + break; + case sizeof(short): + data_length = SHORTALIGN(data_length + sizeof(short)); + break; + case sizeof(int32): + data_length = INTALIGN(data_length + sizeof(int32)); + break; + default: + if (att[i]->attlen < sizeof(int32)) + elog(WARN, "ComputeDataSize: attribute %d has len %d", + i, att[i]->attlen); + if (att[i]->attalign == 'd') + data_length = DOUBLEALIGN(data_length) + att[i]->attlen; + else + data_length = LONGALIGN(data_length) + att[i]->attlen; + break; + } } - } - - return data_length; + + return data_length; } /* ---------------- - * DataFill + * DataFill * ---------------- */ void DataFill(char *data, - TupleDesc tupleDesc, - Datum value[], - char nulls[], - char *infomask, - bits8 *bit) + TupleDesc tupleDesc, + Datum value[], + char nulls[], + char *infomask, + bits8 * bit) { - bits8 *bitP = 0; - int bitmask = 0; - uint32 data_length; - int i; - int numberOfAttributes = tupleDesc->natts; - AttributeTupleForm *att = tupleDesc->attrs; - - if (bit != NULL) { - bitP = &bit[-1]; - bitmask = CSIGNBIT; - } - - *infomask = 0; - - for (i = 0; i < numberOfAttributes; i++) { - if (bit != NULL) { - if (bitmask != CSIGNBIT) { - bitmask <<= 1; - } else { - bitP += 1; - *bitP = 0x0; - bitmask = 1; - } - - if (nulls[i] == 'n') { - *infomask |= HEAP_HASNULL; - continue; - } - - *bitP |= bitmask; + bits8 *bitP = 0; + int bitmask = 0; + uint32 data_length; + int i; + int numberOfAttributes = tupleDesc->natts; + AttributeTupleForm *att = tupleDesc->attrs; + + if (bit != NULL) + { + bitP = &bit[-1]; + bitmask = CSIGNBIT; } - - switch (att[i]->attlen) { - case -1: - *infomask |= HEAP_HASVARLENA; - if (att[i]->attalign=='d') { - data = (char *) DOUBLEALIGN(data); - } else { - data = (char *) INTALIGN(data); - } - data_length = VARSIZE(DatumGetPointer(value[i])); - memmove(data, DatumGetPointer(value[i]),data_length); - data += data_length; - break; - case sizeof(char): - *data = att[i]->attbyval ? - DatumGetChar(value[i]) : *((char *) value[i]); - data += sizeof(char); - break; - case sizeof(int16): - data = (char *) SHORTALIGN(data); - * (short *) data = (att[i]->attbyval ? - DatumGetInt16(value[i]) : - *((short *) value[i])); - data += sizeof(short); - break; - case sizeof(int32): - data = (char *) INTALIGN(data); - * (int32 *) data = (att[i]->attbyval ? - DatumGetInt32(value[i]) : - *((int32 *) value[i])); - data += sizeof(int32); - break; - default: - if (att[i]->attlen < sizeof(int32)) - elog(WARN, "DataFill: attribute %d has len %d", - i, att[i]->attlen); - if (att[i]->attalign == 'd') { - data = (char *) DOUBLEALIGN(data); - memmove(data, DatumGetPointer(value[i]), - att[i]->attlen); - data += att[i]->attlen; - } else { - data = (char *) LONGALIGN(data); - memmove(data, DatumGetPointer(value[i]), - att[i]->attlen); - data += att[i]->attlen; - } - break; + + *infomask = 0; + + for (i = 0; i < numberOfAttributes; i++) + { + if (bit != NULL) + { + if (bitmask != CSIGNBIT) + { + bitmask <<= 1; + } + else + { + bitP += 1; + *bitP = 0x0; + bitmask = 1; + } + + if (nulls[i] == 'n') + { + *infomask |= HEAP_HASNULL; + continue; + } + + *bitP |= bitmask; + } + + switch (att[i]->attlen) + { + case -1: + *infomask |= HEAP_HASVARLENA; + if (att[i]->attalign == 'd') + { + data = (char *) DOUBLEALIGN(data); + } + else + { + data = (char *) INTALIGN(data); + } + data_length = VARSIZE(DatumGetPointer(value[i])); + memmove(data, DatumGetPointer(value[i]), data_length); + data += data_length; + break; + case sizeof(char): + *data = att[i]->attbyval ? + DatumGetChar(value[i]) : *((char *) value[i]); + data += sizeof(char); + break; + case sizeof(int16): + data = (char *) SHORTALIGN(data); + *(short *) data = (att[i]->attbyval ? + DatumGetInt16(value[i]) : + *((short *) value[i])); + data += sizeof(short); + break; + case sizeof(int32): + data = (char *) INTALIGN(data); + *(int32 *) data = (att[i]->attbyval ? + DatumGetInt32(value[i]) : + *((int32 *) value[i])); + data += sizeof(int32); + break; + default: + if (att[i]->attlen < sizeof(int32)) + elog(WARN, "DataFill: attribute %d has len %d", + i, att[i]->attlen); + if (att[i]->attalign == 'd') + { + data = (char *) DOUBLEALIGN(data); + memmove(data, DatumGetPointer(value[i]), + att[i]->attlen); + data += att[i]->attlen; + } + else + { + data = (char *) LONGALIGN(data); + memmove(data, DatumGetPointer(value[i]), + att[i]->attlen); + data += att[i]->attlen; + } + break; + } } - } } /* ---------------------------------------------------------------- - * heap tuple interface + * heap tuple interface * ---------------------------------------------------------------- */ /* ---------------- - * heap_attisnull - returns 1 iff tuple attribute is not present + * heap_attisnull - returns 1 iff tuple attribute is not present * ---------------- */ int heap_attisnull(HeapTuple tup, int attnum) { - if (attnum > (int)tup->t_natts) - return (1); - - if (HeapTupleNoNulls(tup)) return(0); - - if (attnum > 0) { - return(att_isnull(attnum - 1, tup->t_bits)); - } else - switch (attnum) { - case SelfItemPointerAttributeNumber: - case ObjectIdAttributeNumber: - case MinTransactionIdAttributeNumber: - case MinCommandIdAttributeNumber: - case MaxTransactionIdAttributeNumber: - case MaxCommandIdAttributeNumber: - case ChainItemPointerAttributeNumber: - case AnchorItemPointerAttributeNumber: - case MinAbsoluteTimeAttributeNumber: - case MaxAbsoluteTimeAttributeNumber: - case VersionTypeAttributeNumber: - break; - - case 0: - elog(WARN, "heap_attisnull: zero attnum disallowed"); - - default: - elog(WARN, "heap_attisnull: undefined negative attnum"); + if (attnum > (int) tup->t_natts) + return (1); + + if (HeapTupleNoNulls(tup)) + return (0); + + if (attnum > 0) + { + return (att_isnull(attnum - 1, tup->t_bits)); } - - return (0); + else + switch (attnum) + { + case SelfItemPointerAttributeNumber: + case ObjectIdAttributeNumber: + case MinTransactionIdAttributeNumber: + case MinCommandIdAttributeNumber: + case MaxTransactionIdAttributeNumber: + case MaxCommandIdAttributeNumber: + case ChainItemPointerAttributeNumber: + case AnchorItemPointerAttributeNumber: + case MinAbsoluteTimeAttributeNumber: + case MaxAbsoluteTimeAttributeNumber: + case VersionTypeAttributeNumber: + break; + + case 0: + elog(WARN, "heap_attisnull: zero attnum disallowed"); + + default: + elog(WARN, "heap_attisnull: undefined negative attnum"); + } + + return (0); } /* ---------------------------------------------------------------- - * system attribute heap tuple support + * system attribute heap tuple support * ---------------------------------------------------------------- */ /* ---------------- - * heap_sysattrlen + * heap_sysattrlen * - * This routine returns the length of a system attribute. + * This routine returns the length of a system attribute. * ---------------- */ int heap_sysattrlen(AttrNumber attno) { - HeapTupleData *f = NULL; - - switch (attno) { - case SelfItemPointerAttributeNumber: return sizeof f->t_ctid; - case ObjectIdAttributeNumber: return sizeof f->t_oid; - case MinTransactionIdAttributeNumber: return sizeof f->t_xmin; - case MinCommandIdAttributeNumber: return sizeof f->t_cmin; - case MaxTransactionIdAttributeNumber: return sizeof f->t_xmax; - case MaxCommandIdAttributeNumber: return sizeof f->t_cmax; - case ChainItemPointerAttributeNumber: return sizeof f->t_chain; - case MinAbsoluteTimeAttributeNumber: return sizeof f->t_tmin; - case MaxAbsoluteTimeAttributeNumber: return sizeof f->t_tmax; - case VersionTypeAttributeNumber: return sizeof f->t_vtype; - - case AnchorItemPointerAttributeNumber: - elog(WARN, "heap_sysattrlen: field t_anchor does not exist!"); - return 0; - - default: - elog(WARN, "sysattrlen: System attribute number %d unknown.", attno); - return 0; - } + HeapTupleData *f = NULL; + + switch (attno) + { + case SelfItemPointerAttributeNumber: + return sizeof f->t_ctid; + case ObjectIdAttributeNumber: + return sizeof f->t_oid; + case MinTransactionIdAttributeNumber: + return sizeof f->t_xmin; + case MinCommandIdAttributeNumber: + return sizeof f->t_cmin; + case MaxTransactionIdAttributeNumber: + return sizeof f->t_xmax; + case MaxCommandIdAttributeNumber: + return sizeof f->t_cmax; + case ChainItemPointerAttributeNumber: + return sizeof f->t_chain; + case MinAbsoluteTimeAttributeNumber: + return sizeof f->t_tmin; + case MaxAbsoluteTimeAttributeNumber: + return sizeof f->t_tmax; + case VersionTypeAttributeNumber: + return sizeof f->t_vtype; + + case AnchorItemPointerAttributeNumber: + elog(WARN, "heap_sysattrlen: field t_anchor does not exist!"); + return 0; + + default: + elog(WARN, "sysattrlen: System attribute number %d unknown.", attno); + return 0; + } } /* ---------------- - * heap_sysattrbyval + * heap_sysattrbyval * - * This routine returns the "by-value" property of a system attribute. + * This routine returns the "by-value" property of a system attribute. * ---------------- */ bool heap_sysattrbyval(AttrNumber attno) { - bool byval; - - switch (attno) { - case SelfItemPointerAttributeNumber: - byval = false; - break; - case ObjectIdAttributeNumber: - byval = true; - break; - case MinTransactionIdAttributeNumber: - byval = true; - break; - case MinCommandIdAttributeNumber: - byval = true; - break; - case MaxTransactionIdAttributeNumber: - byval = true; - break; - case MaxCommandIdAttributeNumber: - byval = true; - break; - case ChainItemPointerAttributeNumber: - byval = false; - break; - case AnchorItemPointerAttributeNumber: - byval = false; - break; - case MinAbsoluteTimeAttributeNumber: - byval = true; - break; - case MaxAbsoluteTimeAttributeNumber: - byval = true; - break; - case VersionTypeAttributeNumber: - byval = true; - break; - default: - byval = true; - elog(WARN, "sysattrbyval: System attribute number %d unknown.", - attno); - break; - } - - return byval; + bool byval; + + switch (attno) + { + case SelfItemPointerAttributeNumber: + byval = false; + break; + case ObjectIdAttributeNumber: + byval = true; + break; + case MinTransactionIdAttributeNumber: + byval = true; + break; + case MinCommandIdAttributeNumber: + byval = true; + break; + case MaxTransactionIdAttributeNumber: + byval = true; + break; + case MaxCommandIdAttributeNumber: + byval = true; + break; + case ChainItemPointerAttributeNumber: + byval = false; + break; + case AnchorItemPointerAttributeNumber: + byval = false; + break; + case MinAbsoluteTimeAttributeNumber: + byval = true; + break; + case MaxAbsoluteTimeAttributeNumber: + byval = true; + break; + case VersionTypeAttributeNumber: + byval = true; + break; + default: + byval = true; + elog(WARN, "sysattrbyval: System attribute number %d unknown.", + attno); + break; + } + + return byval; } /* ---------------- - * heap_getsysattr + * heap_getsysattr * ---------------- */ -char * +char * heap_getsysattr(HeapTuple tup, Buffer b, int attnum) { - switch (attnum) { - case SelfItemPointerAttributeNumber: - return ((char *)&tup->t_ctid); - case ObjectIdAttributeNumber: - return ((char *) (long) tup->t_oid); - case MinTransactionIdAttributeNumber: - return ((char *) (long) tup->t_xmin); - case MinCommandIdAttributeNumber: - return ((char *) (long) tup->t_cmin); - case MaxTransactionIdAttributeNumber: - return ((char *) (long) tup->t_xmax); - case MaxCommandIdAttributeNumber: - return ((char *) (long) tup->t_cmax); - case ChainItemPointerAttributeNumber: - return ((char *) &tup->t_chain); - case AnchorItemPointerAttributeNumber: - elog(WARN, "heap_getsysattr: t_anchor does not exist!"); - break; - - /* - * For tmin and tmax, we need to do some extra work. These don't - * get filled in until the vacuum cleaner runs (or we manage to flush - * a page after setting the value correctly below). If the vacuum - * cleaner hasn't run yet, then the times stored in the tuple are - * wrong, and we need to look up the commit time of the transaction. - * We cache this value in the tuple to avoid doing the work more than - * once. - */ - - case MinAbsoluteTimeAttributeNumber: - if (!AbsoluteTimeIsBackwardCompatiblyValid(tup->t_tmin) && - TransactionIdDidCommit(tup->t_xmin)) - tup->t_tmin = TransactionIdGetCommitTime(tup->t_xmin); - return ((char *) (long) tup->t_tmin); - case MaxAbsoluteTimeAttributeNumber: - if (!AbsoluteTimeIsBackwardCompatiblyReal(tup->t_tmax)) { - if (TransactionIdDidCommit(tup->t_xmax)) - tup->t_tmax = TransactionIdGetCommitTime(tup->t_xmax); - else - tup->t_tmax = CURRENT_ABSTIME; + switch (attnum) + { + case SelfItemPointerAttributeNumber: + return ((char *) &tup->t_ctid); + case ObjectIdAttributeNumber: + return ((char *) (long) tup->t_oid); + case MinTransactionIdAttributeNumber: + return ((char *) (long) tup->t_xmin); + case MinCommandIdAttributeNumber: + return ((char *) (long) tup->t_cmin); + case MaxTransactionIdAttributeNumber: + return ((char *) (long) tup->t_xmax); + case MaxCommandIdAttributeNumber: + return ((char *) (long) tup->t_cmax); + case ChainItemPointerAttributeNumber: + return ((char *) &tup->t_chain); + case AnchorItemPointerAttributeNumber: + elog(WARN, "heap_getsysattr: t_anchor does not exist!"); + break; + + /* + * For tmin and tmax, we need to do some extra work. These don't + * get filled in until the vacuum cleaner runs (or we manage to + * flush a page after setting the value correctly below). If the + * vacuum cleaner hasn't run yet, then the times stored in the + * tuple are wrong, and we need to look up the commit time of the + * transaction. We cache this value in the tuple to avoid doing + * the work more than once. + */ + + case MinAbsoluteTimeAttributeNumber: + if (!AbsoluteTimeIsBackwardCompatiblyValid(tup->t_tmin) && + TransactionIdDidCommit(tup->t_xmin)) + tup->t_tmin = TransactionIdGetCommitTime(tup->t_xmin); + return ((char *) (long) tup->t_tmin); + case MaxAbsoluteTimeAttributeNumber: + if (!AbsoluteTimeIsBackwardCompatiblyReal(tup->t_tmax)) + { + if (TransactionIdDidCommit(tup->t_xmax)) + tup->t_tmax = TransactionIdGetCommitTime(tup->t_xmax); + else + tup->t_tmax = CURRENT_ABSTIME; + } + return ((char *) (long) tup->t_tmax); + case VersionTypeAttributeNumber: + return ((char *) (long) tup->t_vtype); + default: + elog(WARN, "heap_getsysattr: undefined attnum %d", attnum); } - return ((char *) (long) tup->t_tmax); - case VersionTypeAttributeNumber: - return ((char *) (long) tup->t_vtype); - default: - elog(WARN, "heap_getsysattr: undefined attnum %d", attnum); - } - return(NULL); + return (NULL); } /* ---------------- - * fastgetattr + * fastgetattr * - * This is a newer version of fastgetattr which attempts to be - * faster by caching attribute offsets in the attribute descriptor. + * This is a newer version of fastgetattr which attempts to be + * faster by caching attribute offsets in the attribute descriptor. * - * an alternate way to speed things up would be to cache offsets - * with the tuple, but that seems more difficult unless you take - * the storage hit of actually putting those offsets into the - * tuple you send to disk. Yuck. + * an alternate way to speed things up would be to cache offsets + * with the tuple, but that seems more difficult unless you take + * the storage hit of actually putting those offsets into the + * tuple you send to disk. Yuck. * - * This scheme will be slightly slower than that, but should - * preform well for queries which hit large #'s of tuples. After - * you cache the offsets once, examining all the other tuples using - * the same attribute descriptor will go much quicker. -cim 5/4/91 + * This scheme will be slightly slower than that, but should + * preform well for queries which hit large #'s of tuples. After + * you cache the offsets once, examining all the other tuples using + * the same attribute descriptor will go much quicker. -cim 5/4/91 * ---------------- */ -char * +char * fastgetattr(HeapTuple tup, - int attnum, - TupleDesc tupleDesc, - bool *isnull) + int attnum, + TupleDesc tupleDesc, + bool * isnull) { - char *tp; /* ptr to att in tuple */ - bits8 *bp = NULL; /* ptr to att in tuple */ - int slow; /* do we have to walk nulls? */ - AttributeTupleForm *att = tupleDesc->attrs; - - /* ---------------- - * sanity checks - * ---------------- - */ - - Assert(attnum > 0); - - /* ---------------- - * Three cases: - * - * 1: No nulls and no variable length attributes. - * 2: Has a null or a varlena AFTER att. - * 3: Has nulls or varlenas BEFORE att. - * ---------------- - */ - - if (isnull) - *isnull = false; - - if (HeapTupleNoNulls(tup)) { - attnum--; - if (att[attnum]->attcacheoff > 0) { - return (char *) - fetchatt( &(att[attnum]), - (char *)tup + tup->t_hoff + att[attnum]->attcacheoff); - } else if (attnum == 0) { - /* - * first attribute is always at position zero - */ - return((char *) fetchatt(&(att[0]), (char *) tup + tup->t_hoff)); - } - - tp = (char *) tup + tup->t_hoff; - - slow = 0; - } else { - /* - * there's a null somewhere in the tuple - */ + char *tp; /* ptr to att in tuple */ + bits8 *bp = NULL; /* ptr to att in tuple */ + int slow; /* do we have to walk nulls? */ + AttributeTupleForm *att = tupleDesc->attrs; - bp = tup->t_bits; - tp = (char *) tup + tup->t_hoff; - slow = 0; - attnum--; - /* ---------------- - * check to see if desired att is null + * sanity checks * ---------------- */ - - if (att_isnull(attnum, bp)) { - if (isnull) - *isnull = true; - return NULL; - } + + Assert(attnum > 0); /* ---------------- - * Now check to see if any preceeding bits are null... + * Three cases: + * + * 1: No nulls and no variable length attributes. + * 2: Has a null or a varlena AFTER att. + * 3: Has nulls or varlenas BEFORE att. * ---------------- */ - + + if (isnull) + *isnull = false; + + if (HeapTupleNoNulls(tup)) { - register int i = 0; /* current offset in bp */ - - for (i = 0; i < attnum && !slow; i++) { - if (att_isnull(i, bp)) slow = 1; - } + attnum--; + if (att[attnum]->attcacheoff > 0) + { + return (char *) + fetchatt(&(att[attnum]), + (char *) tup + tup->t_hoff + att[attnum]->attcacheoff); + } + else if (attnum == 0) + { + + /* + * first attribute is always at position zero + */ + return ((char *) fetchatt(&(att[0]), (char *) tup + tup->t_hoff)); + } + + tp = (char *) tup + tup->t_hoff; + + slow = 0; } - } - - /* - * now check for any non-fixed length attrs before our attribute - */ - if (!slow) { - if (att[attnum]->attcacheoff > 0) { - return (char *) - fetchatt(&(att[attnum]), - tp + att[attnum]->attcacheoff); - } else if (attnum == 0) { - return (char *) - fetchatt(&(att[0]), (char *) tup + tup->t_hoff); - } else if (!HeapTupleAllFixed(tup)) { - register int j = 0; - - for (j = 0; j < attnum && !slow; j++) - if (att[j]->attlen < 1) slow = 1; + else + { + + /* + * there's a null somewhere in the tuple + */ + + bp = tup->t_bits; + tp = (char *) tup + tup->t_hoff; + slow = 0; + attnum--; + + /* ---------------- + * check to see if desired att is null + * ---------------- + */ + + if (att_isnull(attnum, bp)) + { + if (isnull) + *isnull = true; + return NULL; + } + + /* ---------------- + * Now check to see if any preceeding bits are null... + * ---------------- + */ + + { + register int i = 0; /* current offset in bp */ + + for (i = 0; i < attnum && !slow; i++) + { + if (att_isnull(i, bp)) + slow = 1; + } + } } - } - - /* - * if slow is zero, and we got here, we know that we have a tuple with - * no nulls. We also have to initialize the remainder of - * the attribute cached offset values. - */ - if (!slow) { - register int j = 1; - register long off; - + /* - * need to set cache for some atts + * now check for any non-fixed length attrs before our attribute */ - - att[0]->attcacheoff = 0; - - while (att[j]->attcacheoff > 0) j++; - - off = att[j-1]->attcacheoff + att[j-1]->attlen; - - for (; j < attnum + 1; j++) { - switch(att[j]->attlen) { - case -1: - off = (att[j]->attalign=='d') ? - DOUBLEALIGN(off) : INTALIGN(off); - break; - case sizeof(char): - break; - case sizeof(short): - off = SHORTALIGN(off); - break; - case sizeof(int32): - off = INTALIGN(off); - break; - default: - if (att[j]->attlen < sizeof(int32)) { - elog(WARN, - "fastgetattr: attribute %d has len %d", - j, att[j]->attlen); + if (!slow) + { + if (att[attnum]->attcacheoff > 0) + { + return (char *) + fetchatt(&(att[attnum]), + tp + att[attnum]->attcacheoff); + } + else if (attnum == 0) + { + return (char *) + fetchatt(&(att[0]), (char *) tup + tup->t_hoff); + } + else if (!HeapTupleAllFixed(tup)) + { + register int j = 0; + + for (j = 0; j < attnum && !slow; j++) + if (att[j]->attlen < 1) + slow = 1; } - if (att[j]->attalign == 'd') - off = DOUBLEALIGN(off); - else - off = LONGALIGN(off); - break; - } - - att[j]->attcacheoff = off; - off += att[j]->attlen; } - - return - (char *)fetchatt(&(att[attnum]), tp + att[attnum]->attcacheoff); - } else { - register bool usecache = true; - register int off = 0; - register int i; - + /* - * Now we know that we have to walk the tuple CAREFULLY. - * - * Note - This loop is a little tricky. On iteration i we - * first set the offset for attribute i and figure out how much - * the offset should be incremented. Finally, we need to align the - * offset based on the size of attribute i+1 (for which the offset - * has been computed). -mer 12 Dec 1991 + * if slow is zero, and we got here, we know that we have a tuple with + * no nulls. We also have to initialize the remainder of the + * attribute cached offset values. */ - - for (i = 0; i < attnum; i++) { - if (!HeapTupleNoNulls(tup)) { - if (att_isnull(i, bp)) { - usecache = false; - continue; - } - } - switch (att[i]->attlen) { - case -1: - off = (att[i]->attalign=='d') ? - DOUBLEALIGN(off) : INTALIGN(off); - break; - case sizeof(char): - break; - case sizeof(short): - off = SHORTALIGN(off); - break; - case sizeof(int32): - off = INTALIGN(off); - break; - default: - if (att[i]->attlen < sizeof(int32)) - elog(WARN, - "fastgetattr2: attribute %d has len %d", - i, att[i]->attlen); - if (att[i]->attalign == 'd') - off = DOUBLEALIGN(off); - else - off = LONGALIGN(off); - break; - } - if (usecache && att[i]->attcacheoff > 0) { - off = att[i]->attcacheoff; - if (att[i]->attlen == -1) { - usecache = false; + if (!slow) + { + register int j = 1; + register long off; + + /* + * need to set cache for some atts + */ + + att[0]->attcacheoff = 0; + + while (att[j]->attcacheoff > 0) + j++; + + off = att[j - 1]->attcacheoff + att[j - 1]->attlen; + + for (; j < attnum + 1; j++) + { + switch (att[j]->attlen) + { + case -1: + off = (att[j]->attalign == 'd') ? + DOUBLEALIGN(off) : INTALIGN(off); + break; + case sizeof(char): + break; + case sizeof(short): + off = SHORTALIGN(off); + break; + case sizeof(int32): + off = INTALIGN(off); + break; + default: + if (att[j]->attlen < sizeof(int32)) + { + elog(WARN, + "fastgetattr: attribute %d has len %d", + j, att[j]->attlen); + } + if (att[j]->attalign == 'd') + off = DOUBLEALIGN(off); + else + off = LONGALIGN(off); + break; + } + + att[j]->attcacheoff = off; + off += att[j]->attlen; } - } else { - if (usecache) att[i]->attcacheoff = off; - } - - switch(att[i]->attlen) { - case sizeof(char): - off++; - break; - case sizeof(int16): - off += sizeof(int16); - break; - case sizeof(int32): - off += sizeof(int32); - break; - case -1: - usecache = false; - off += VARSIZE(tp + off); - break; - default: - off += att[i]->attlen; - break; - } + + return + (char *) fetchatt(&(att[attnum]), tp + att[attnum]->attcacheoff); } - switch (att[attnum]->attlen) { - case -1: - off = (att[attnum]->attalign=='d')? - DOUBLEALIGN(off) : INTALIGN(off); - break; - case sizeof(char): - break; - case sizeof(short): - off = SHORTALIGN(off); - break; - case sizeof(int32): - off = INTALIGN(off); - break; - default: - if (att[attnum]->attlen < sizeof(int32)) - elog(WARN, "fastgetattr3: attribute %d has len %d", - attnum, att[attnum]->attlen); - if (att[attnum]->attalign == 'd') - off = DOUBLEALIGN(off); - else - off = LONGALIGN(off); - break; + else + { + register bool usecache = true; + register int off = 0; + register int i; + + /* + * Now we know that we have to walk the tuple CAREFULLY. + * + * Note - This loop is a little tricky. On iteration i we first set + * the offset for attribute i and figure out how much the offset + * should be incremented. Finally, we need to align the offset + * based on the size of attribute i+1 (for which the offset has + * been computed). -mer 12 Dec 1991 + */ + + for (i = 0; i < attnum; i++) + { + if (!HeapTupleNoNulls(tup)) + { + if (att_isnull(i, bp)) + { + usecache = false; + continue; + } + } + switch (att[i]->attlen) + { + case -1: + off = (att[i]->attalign == 'd') ? + DOUBLEALIGN(off) : INTALIGN(off); + break; + case sizeof(char): + break; + case sizeof(short): + off = SHORTALIGN(off); + break; + case sizeof(int32): + off = INTALIGN(off); + break; + default: + if (att[i]->attlen < sizeof(int32)) + elog(WARN, + "fastgetattr2: attribute %d has len %d", + i, att[i]->attlen); + if (att[i]->attalign == 'd') + off = DOUBLEALIGN(off); + else + off = LONGALIGN(off); + break; + } + if (usecache && att[i]->attcacheoff > 0) + { + off = att[i]->attcacheoff; + if (att[i]->attlen == -1) + { + usecache = false; + } + } + else + { + if (usecache) + att[i]->attcacheoff = off; + } + + switch (att[i]->attlen) + { + case sizeof(char): + off++; + break; + case sizeof(int16): + off += sizeof(int16); + break; + case sizeof(int32): + off += sizeof(int32); + break; + case -1: + usecache = false; + off += VARSIZE(tp + off); + break; + default: + off += att[i]->attlen; + break; + } + } + switch (att[attnum]->attlen) + { + case -1: + off = (att[attnum]->attalign == 'd') ? + DOUBLEALIGN(off) : INTALIGN(off); + break; + case sizeof(char): + break; + case sizeof(short): + off = SHORTALIGN(off); + break; + case sizeof(int32): + off = INTALIGN(off); + break; + default: + if (att[attnum]->attlen < sizeof(int32)) + elog(WARN, "fastgetattr3: attribute %d has len %d", + attnum, att[attnum]->attlen); + if (att[attnum]->attalign == 'd') + off = DOUBLEALIGN(off); + else + off = LONGALIGN(off); + break; + } + return ((char *) fetchatt(&(att[attnum]), tp + off)); } - return((char *) fetchatt(&(att[attnum]), tp + off)); - } } /* ---------------- - * heap_copytuple + * heap_copytuple * - * returns a copy of an entire tuple + * returns a copy of an entire tuple * ---------------- */ HeapTuple heap_copytuple(HeapTuple tuple) { - HeapTuple newTuple; + HeapTuple newTuple; - if (! HeapTupleIsValid(tuple)) - return (NULL); - - /* XXX For now, just prevent an undetectable executor related error */ - if (tuple->t_len > MAXTUPLEN) { - elog(WARN, "palloctup: cannot handle length %d tuples", - tuple->t_len); - } - - newTuple = (HeapTuple) palloc(tuple->t_len); - memmove((char *) newTuple, (char *) tuple, (int) tuple->t_len); - return(newTuple); + if (!HeapTupleIsValid(tuple)) + return (NULL); + + /* XXX For now, just prevent an undetectable executor related error */ + if (tuple->t_len > MAXTUPLEN) + { + elog(WARN, "palloctup: cannot handle length %d tuples", + tuple->t_len); + } + + newTuple = (HeapTuple) palloc(tuple->t_len); + memmove((char *) newTuple, (char *) tuple, (int) tuple->t_len); + return (newTuple); } #ifdef NOT_USED /* ---------------- - * heap_deformtuple + * heap_deformtuple * - * the inverse of heap_formtuple (see below) + * the inverse of heap_formtuple (see below) * ---------------- */ void heap_deformtuple(HeapTuple tuple, - TupleDesc tdesc, - Datum values[], - char nulls[]) + 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] = (Datum)heap_getattr(tuple, - InvalidBuffer, - i+1, - tdesc, - &isnull); - if (isnull) - nulls[i] = 'n'; - else - nulls[i] = ' '; - } + int i; + int natts; + + Assert(HeapTupleIsValid(tuple)); + + natts = tuple->t_natts; + for (i = 0; i < natts; i++) + { + bool isnull; + + values[i] = (Datum) heap_getattr(tuple, + InvalidBuffer, + 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 null[] arrays + * constructs a tuple from the given value[] and null[] arrays * * old comments - * Handles alignment by aligning 2 byte attributes on short boundries - * and 3 or 4 byte attributes on long word boundries on a vax; and - * aligning non-byte attributes on short boundries on a sun. Does - * not properly align fixed length arrays of 1 or 2 byte types (yet). + * Handles alignment by aligning 2 byte attributes on short boundries + * and 3 or 4 byte attributes on long word boundries on a vax; and + * aligning non-byte attributes on short boundries on a sun. Does + * not properly align fixed length arrays of 1 or 2 byte types (yet). * - * Null attributes are indicated by a 'n' in the appropriate byte - * of the null[]. Non-null attributes are indicated by a ' ' (space). + * Null attributes are indicated by a 'n' in the appropriate byte + * of the null[]. Non-null attributes are indicated by a ' ' (space). * - * Fix me. (Figure that must keep context if debug--allow give oid.) - * Assumes in order. + * Fix me. (Figure that must keep context if debug--allow give oid.) + * Assumes in order. * ---------------- */ HeapTuple heap_formtuple(TupleDesc tupleDescriptor, - Datum value[], - char nulls[]) + Datum value[], + char nulls[]) { - char *tp; /* tuple pointer */ - HeapTuple tuple; /* return tuple */ - int bitmaplen; - long len; - int hoff; - bool hasnull = false; - int i; - int numberOfAttributes = tupleDescriptor->natts; - - len = sizeof *tuple - sizeof tuple->t_bits; - - for (i = 0; i < numberOfAttributes && !hasnull; i++) { - if (nulls[i] != ' ') hasnull = true; - } - - if (numberOfAttributes > MaxHeapAttributeNumber) - elog(WARN, "heap_formtuple: numberOfAttributes of %d > %d", - numberOfAttributes, MaxHeapAttributeNumber); - - if (hasnull) { - bitmaplen = BITMAPLEN(numberOfAttributes); - len += bitmaplen; - } - - hoff = len = DOUBLEALIGN(len); /* be conservative here */ - - len += ComputeDataSize(tupleDescriptor, value, nulls); - - tp = (char *) palloc(len); - tuple = (HeapTuple) tp; - - memset(tp, 0, (int)len); - - tuple->t_len = len; - tuple->t_natts = numberOfAttributes; - tuple->t_hoff = hoff; - tuple->t_tmin = INVALID_ABSTIME; - tuple->t_tmax = CURRENT_ABSTIME; - - DataFill((char *)tuple + tuple->t_hoff, - tupleDescriptor, - value, - nulls, - &tuple->t_infomask, - (hasnull ? tuple->t_bits : NULL)); - - return (tuple); + char *tp; /* tuple pointer */ + HeapTuple tuple; /* return tuple */ + int bitmaplen; + long len; + int hoff; + bool hasnull = false; + int i; + int numberOfAttributes = tupleDescriptor->natts; + + len = sizeof *tuple - sizeof tuple->t_bits; + + for (i = 0; i < numberOfAttributes && !hasnull; i++) + { + if (nulls[i] != ' ') + hasnull = true; + } + + if (numberOfAttributes > MaxHeapAttributeNumber) + elog(WARN, "heap_formtuple: numberOfAttributes of %d > %d", + numberOfAttributes, MaxHeapAttributeNumber); + + if (hasnull) + { + bitmaplen = BITMAPLEN(numberOfAttributes); + len += bitmaplen; + } + + hoff = len = DOUBLEALIGN(len); /* be conservative here */ + + len += ComputeDataSize(tupleDescriptor, value, nulls); + + tp = (char *) palloc(len); + tuple = (HeapTuple) tp; + + memset(tp, 0, (int) len); + + tuple->t_len = len; + tuple->t_natts = numberOfAttributes; + tuple->t_hoff = hoff; + tuple->t_tmin = INVALID_ABSTIME; + tuple->t_tmax = CURRENT_ABSTIME; + + DataFill((char *) tuple + tuple->t_hoff, + tupleDescriptor, + value, + nulls, + &tuple->t_infomask, + (hasnull ? tuple->t_bits : NULL)); + + return (tuple); } /* ---------------- - * heap_modifytuple + * heap_modifytuple * - * 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. * ---------------- */ HeapTuple heap_modifytuple(HeapTuple tuple, - Buffer buffer, - Relation relation, - Datum replValue[], - char replNull[], - char repl[]) + Buffer buffer, + Relation relation, + Datum replValue[], + char replNull[], + char repl[]) { - int attoff; - int numberOfAttributes; - Datum *value; - char *nulls; - bool isNull; - HeapTuple newTuple; - int madecopy; - uint8 infomask; - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(HeapTupleIsValid(tuple)); - Assert(BufferIsValid(buffer) || RelationIsValid(relation)); - Assert(HeapTupleIsValid(tuple)); - Assert(PointerIsValid(replValue)); - Assert(PointerIsValid(replNull)); - Assert(PointerIsValid(repl)); - - /* ---------------- - * if we're pointing to a disk page, then first - * make a copy of our tuple so that all the attributes - * are available. XXX this is inefficient -cim - * ---------------- - */ - madecopy = 0; - if (BufferIsValid(buffer) == true) { - relation = (Relation) BufferGetRelation(buffer); - tuple = heap_copytuple(tuple); - madecopy = 1; - } - - numberOfAttributes = RelationGetRelationTupleForm(relation)->relnatts; - - /* ---------------- - * allocate and fill value[] and nulls[] arrays from either - * the tuple or the repl information, as appropriate. - * ---------------- - */ - value = (Datum *) palloc(numberOfAttributes * sizeof *value); - nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); - - for (attoff = 0; - attoff < numberOfAttributes; - attoff += 1) { - - if (repl[attoff] == ' ') { - char *attr; - - attr = - heap_getattr(tuple, - InvalidBuffer, - AttrOffsetGetAttrNumber(attoff), - RelationGetTupleDescriptor(relation), - &isNull) ; - value[attoff] = PointerGetDatum(attr); - nulls[attoff] = (isNull) ? 'n' : ' '; - - } else if (repl[attoff] != 'r') { - elog(WARN, "heap_modifytuple: repl is \\%3d", repl[attoff]); - - } else { /* == 'r' */ - value[attoff] = replValue[attoff]; - nulls[attoff] = replNull[attoff]; + int attoff; + int numberOfAttributes; + Datum *value; + char *nulls; + bool isNull; + HeapTuple newTuple; + int madecopy; + uint8 infomask; + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(HeapTupleIsValid(tuple)); + Assert(BufferIsValid(buffer) || RelationIsValid(relation)); + Assert(HeapTupleIsValid(tuple)); + Assert(PointerIsValid(replValue)); + Assert(PointerIsValid(replNull)); + Assert(PointerIsValid(repl)); + + /* ---------------- + * if we're pointing to a disk page, then first + * make a copy of our tuple so that all the attributes + * are available. XXX this is inefficient -cim + * ---------------- + */ + madecopy = 0; + if (BufferIsValid(buffer) == true) + { + relation = (Relation) BufferGetRelation(buffer); + tuple = heap_copytuple(tuple); + madecopy = 1; + } + + numberOfAttributes = RelationGetRelationTupleForm(relation)->relnatts; + + /* ---------------- + * allocate and fill value[] and nulls[] arrays from either + * the tuple or the repl information, as appropriate. + * ---------------- + */ + value = (Datum *) palloc(numberOfAttributes * sizeof *value); + nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); + + for (attoff = 0; + attoff < numberOfAttributes; + attoff += 1) + { + + if (repl[attoff] == ' ') + { + char *attr; + + attr = + heap_getattr(tuple, + InvalidBuffer, + AttrOffsetGetAttrNumber(attoff), + RelationGetTupleDescriptor(relation), + &isNull); + value[attoff] = PointerGetDatum(attr); + nulls[attoff] = (isNull) ? 'n' : ' '; + + } + else if (repl[attoff] != 'r') + { + elog(WARN, "heap_modifytuple: repl is \\%3d", repl[attoff]); + + } + else + { /* == 'r' */ + value[attoff] = replValue[attoff]; + nulls[attoff] = replNull[attoff]; + } } - } - - /* ---------------- - * create a new tuple from the values[] and nulls[] arrays - * ---------------- - */ - newTuple = heap_formtuple(RelationGetTupleDescriptor(relation), - value, - nulls); - - /* ---------------- - * copy the header except for t_len, t_natts, t_hoff, t_bits, t_infomask - * ---------------- - */ - infomask = newTuple->t_infomask; - memmove((char *) &newTuple->t_ctid, /*XXX*/ - (char *) &tuple->t_ctid, - ((char *) &tuple->t_hoff - (char *) &tuple->t_ctid)); /*XXX*/ - newTuple->t_infomask = infomask; - newTuple->t_natts = numberOfAttributes; /* fix t_natts just in case */ - - /* ---------------- - * if we made a copy of the tuple, then free it. - * ---------------- - */ - if (madecopy) - pfree(tuple); - - return - newTuple; + + /* ---------------- + * create a new tuple from the values[] and nulls[] arrays + * ---------------- + */ + newTuple = heap_formtuple(RelationGetTupleDescriptor(relation), + value, + nulls); + + /* ---------------- + * copy the header except for t_len, t_natts, t_hoff, t_bits, t_infomask + * ---------------- + */ + infomask = newTuple->t_infomask; + memmove((char *) &newTuple->t_ctid, /* XXX */ + (char *) &tuple->t_ctid, + ((char *) &tuple->t_hoff - (char *) &tuple->t_ctid)); /* XXX */ + newTuple->t_infomask = infomask; + newTuple->t_natts = numberOfAttributes; /* fix t_natts just in + * case */ + + /* ---------------- + * if we made a copy of the tuple, then free it. + * ---------------- + */ + if (madecopy) + pfree(tuple); + + return + newTuple; } /* ---------------------------------------------------------------- - * other misc functions + * other misc functions * ---------------------------------------------------------------- */ HeapTuple heap_addheader(uint32 natts, /* max domain index */ - int structlen, /* its length */ - char *structure) /* pointer to the struct */ + int structlen, /* its length */ + char *structure) /* pointer to the struct */ { - register char *tp; /* tuple data pointer */ - HeapTuple tup; - long len; - int hoff; - - AssertArg(natts > 0); - - len = sizeof (HeapTupleData) - sizeof (tup->t_bits); - - hoff = len = DOUBLEALIGN(len); /* be conservative */ - len += structlen; - tp = (char *) palloc(len); - tup = (HeapTuple) tp; - memset((char*)tup, 0, len); - - tup->t_len = (short) len; /* XXX */ - tp += tup->t_hoff = hoff; - tup->t_natts = natts; - tup->t_infomask = 0; - - memmove(tp, structure, structlen); - - return (tup); + register char *tp; /* tuple data pointer */ + HeapTuple tup; + long len; + int hoff; + + AssertArg(natts > 0); + + len = sizeof(HeapTupleData) - sizeof(tup->t_bits); + + hoff = len = DOUBLEALIGN(len); /* be conservative */ + len += structlen; + tp = (char *) palloc(len); + tup = (HeapTuple) tp; + memset((char *) tup, 0, len); + + tup->t_len = (short) len; /* XXX */ + tp += tup->t_hoff = hoff; + tup->t_natts = natts; + tup->t_infomask = 0; + + memmove(tp, structure, structlen); + + return (tup); } diff --git a/src/backend/access/common/heapvalid.c b/src/backend/access/common/heapvalid.c index 186ee654b32..0caeb54e17c 100644 --- a/src/backend/access/common/heapvalid.c +++ b/src/backend/access/common/heapvalid.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * heapvalid.c-- - * heap tuple qualification validity checking code + * heap tuple qualification validity checking code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.16 1997/08/29 09:12:20 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.17 1997/09/07 04:37:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,128 +25,138 @@ #include <utils/builtins.h> /* ---------------- - * heap_keytest + * heap_keytest * - * Test a heap tuple with respect to a scan key. + * Test a heap tuple with respect to a scan key. * ---------------- */ bool heap_keytest(HeapTuple t, - TupleDesc tupdesc, - int nkeys, - ScanKey keys) + TupleDesc tupdesc, + int nkeys, + ScanKey keys) { - bool isnull; - Datum atp; - int test; - - for (; nkeys--; keys++) { - atp = (Datum)heap_getattr(t, InvalidBuffer, - keys->sk_attno, - tupdesc, - &isnull); - - if (isnull) - /* XXX eventually should check if SK_ISNULL */ - return false; - - if (keys->sk_flags & SK_ISNULL) { - return (false); + bool isnull; + Datum atp; + int test; + + for (; nkeys--; keys++) + { + atp = (Datum) heap_getattr(t, InvalidBuffer, + keys->sk_attno, + tupdesc, + &isnull); + + if (isnull) + /* XXX eventually should check if SK_ISNULL */ + return false; + + if (keys->sk_flags & SK_ISNULL) + { + return (false); + } + + if (keys->sk_func == (func_ptr) oideq) /* optimization */ + test = (keys->sk_argument == atp); + else if (keys->sk_flags & SK_COMMUTE) + test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure, + keys->sk_argument, atp); + else + test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure, + atp, keys->sk_argument); + + if (!test == !(keys->sk_flags & SK_NEGATE)) + return false; } - if (keys->sk_func == (func_ptr)oideq) /* optimization */ - test = (keys->sk_argument == atp); - else if (keys->sk_flags & SK_COMMUTE) - test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure, - keys->sk_argument, atp); - else - test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure, - atp, keys->sk_argument); - - if (!test == !(keys->sk_flags & SK_NEGATE)) - return false; - } - - return true; + return true; } /* ---------------- - * heap_tuple_satisfies + * heap_tuple_satisfies * - * Returns a valid HeapTuple if it satisfies the timequal and keytest. - * Returns NULL otherwise. Used to be heap_satisifies (sic) which - * returned a boolean. It now returns a tuple so that we can avoid doing two - * PageGetItem's per tuple. + * Returns a valid HeapTuple if it satisfies the timequal and keytest. + * Returns NULL otherwise. Used to be heap_satisifies (sic) which + * returned a boolean. It now returns a tuple so that we can avoid doing two + * PageGetItem's per tuple. * - * Complete check of validity including LP_CTUP and keytest. - * This should perhaps be combined with valid somehow in the - * future. (Also, additional rule tests/time range tests.) + * Complete check of validity including LP_CTUP and keytest. + * This should perhaps be combined with valid somehow in the + * future. (Also, additional rule tests/time range tests.) * - * on 8/21/92 mao says: i rearranged the tests here to do keytest before - * SatisfiesTimeQual. profiling indicated that even for vacuumed relations, - * time qual checking was more expensive than key testing. time qual is - * least likely to fail, too. we should really add the time qual test to - * the restriction and optimize it in the normal way. this has interactions - * with joey's expensive function work. + * on 8/21/92 mao says: i rearranged the tests here to do keytest before + * SatisfiesTimeQual. profiling indicated that even for vacuumed relations, + * time qual checking was more expensive than key testing. time qual is + * least likely to fail, too. we should really add the time qual test to + * the restriction and optimize it in the normal way. this has interactions + * with joey's expensive function work. * ---------------- */ HeapTuple heap_tuple_satisfies(ItemId itemId, - Relation relation, - Buffer buffer, - PageHeader disk_page, - TimeQual qual, - int nKeys, - ScanKey key) + Relation relation, + Buffer buffer, + PageHeader disk_page, + TimeQual qual, + int nKeys, + ScanKey key) { - HeapTuple tuple, result; - bool res; - TransactionId old_tmin, old_tmax; - - if (! ItemIdIsUsed(itemId)) - return NULL; - - tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId); - - if (key != NULL) - res = heap_keytest(tuple, RelationGetTupleDescriptor(relation), - nKeys, key); - else - res = TRUE; - - result = (HeapTuple)NULL; - if (res) { - if(relation->rd_rel->relkind == RELKIND_UNCATALOGED) { - result = tuple; - } else { - old_tmin = tuple->t_tmin; - old_tmax = tuple->t_tmax; - res = HeapTupleSatisfiesTimeQual(tuple,qual); - if(tuple->t_tmin != old_tmin || - tuple->t_tmax != old_tmax) { - SetBufferCommitInfoNeedsSave(buffer); - } - if(res) { - result = tuple; - } + HeapTuple tuple, + result; + bool res; + TransactionId old_tmin, + old_tmax; + + if (!ItemIdIsUsed(itemId)) + return NULL; + + tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId); + + if (key != NULL) + res = heap_keytest(tuple, RelationGetTupleDescriptor(relation), + nKeys, key); + else + res = TRUE; + + result = (HeapTuple) NULL; + if (res) + { + if (relation->rd_rel->relkind == RELKIND_UNCATALOGED) + { + result = tuple; + } + else + { + old_tmin = tuple->t_tmin; + old_tmax = tuple->t_tmax; + res = HeapTupleSatisfiesTimeQual(tuple, qual); + if (tuple->t_tmin != old_tmin || + tuple->t_tmax != old_tmax) + { + SetBufferCommitInfoNeedsSave(buffer); + } + if (res) + { + result = tuple; + } + } } - } - return result; + return result; } /* - * TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has - * already been updated once by the current transaction/command - * pair. + * TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has + * already been updated once by the current transaction/command + * pair. */ bool TupleUpdatedByCurXactAndCmd(HeapTuple t) { - if (TransactionIdEquals(t->t_xmax, - GetCurrentTransactionId()) && - CommandIdGEScanCommandId (t->t_cmax)) - return true; - - return false; + if (TransactionIdEquals(t->t_xmax, + GetCurrentTransactionId()) && + CommandIdGEScanCommandId(t->t_cmax)) + return true; + + return false; } diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c index a71fc46dc98..c133693801b 100644 --- a/src/backend/access/common/indextuple.c +++ b/src/backend/access/common/indextuple.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * indextuple.c-- - * This file contains index tuple accessor and mutator routines, - * as well as a few various tuple utilities. + * This file contains index tuple accessor and mutator routines, + * as well as a few various tuple utilities. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.15 1997/08/19 21:28:50 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.16 1997/09/07 04:37:37 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,402 +21,438 @@ #include <access/tupmacs.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static Size IndexInfoFindDataOffset(unsigned short t_info); -static char *fastgetiattr(IndexTuple tup, int attnum, - TupleDesc att, bool *isnull); +static Size IndexInfoFindDataOffset(unsigned short t_info); +static char * +fastgetiattr(IndexTuple tup, int attnum, + TupleDesc att, bool * isnull); /* ---------------------------------------------------------------- - * index_ tuple interface routines + * index_ tuple interface routines * ---------------------------------------------------------------- */ /* ---------------- - * index_formtuple + * index_formtuple * ---------------- */ IndexTuple index_formtuple(TupleDesc tupleDescriptor, - Datum value[], - char null[]) + Datum value[], + char null[]) { - register char *tp; /* tuple pointer */ - IndexTuple tuple; /* return tuple */ - Size size, hoff; - int i; - unsigned short infomask = 0; - bool hasnull = false; - char tupmask = 0; - int numberOfAttributes = tupleDescriptor->natts; - - if (numberOfAttributes > MaxIndexAttributeNumber) - elog(WARN, "index_formtuple: numberOfAttributes of %d > %d", - numberOfAttributes, MaxIndexAttributeNumber); - - - for (i = 0; i < numberOfAttributes && !hasnull; i++) { - if (null[i] != ' ') hasnull = true; - } - - if (hasnull) infomask |= INDEX_NULL_MASK; - - hoff = IndexInfoFindDataOffset(infomask); - size = hoff - + ComputeDataSize(tupleDescriptor, - value, null); - size = DOUBLEALIGN(size); /* be conservative */ - - tp = (char *) palloc(size); - tuple = (IndexTuple) tp; - memset(tp,0,(int)size); - - DataFill((char *)tp + hoff, - tupleDescriptor, - value, - null, - &tupmask, - (hasnull ? (bits8*)tp + sizeof(*tuple) : NULL)); - - /* - * We do this because DataFill wants to initialize a "tupmask" which - * is used for HeapTuples, but we want an indextuple infomask. The only - * "relevent" info is the "has variable attributes" field, which is in - * mask position 0x02. We have already set the null mask above. - */ - - if (tupmask & 0x02) infomask |= INDEX_VAR_MASK; - - /* - * Here we make sure that we can actually hold the size. We also want - * to make sure that size is not aligned oddly. This actually is a - * rather odd way to make sure the size is not too large overall. - */ - - if (size & 0xE000) - elog(WARN, "index_formtuple: data takes %d bytes: too big", size); - - - infomask |= size; - - /* ---------------- - * initialize metadata - * ---------------- - */ - tuple->t_info = infomask; - return (tuple); + register char *tp; /* tuple pointer */ + IndexTuple tuple; /* return tuple */ + Size size, + hoff; + int i; + unsigned short infomask = 0; + bool hasnull = false; + char tupmask = 0; + int numberOfAttributes = tupleDescriptor->natts; + + if (numberOfAttributes > MaxIndexAttributeNumber) + elog(WARN, "index_formtuple: numberOfAttributes of %d > %d", + numberOfAttributes, MaxIndexAttributeNumber); + + + for (i = 0; i < numberOfAttributes && !hasnull; i++) + { + if (null[i] != ' ') + hasnull = true; + } + + if (hasnull) + infomask |= INDEX_NULL_MASK; + + hoff = IndexInfoFindDataOffset(infomask); + size = hoff + + ComputeDataSize(tupleDescriptor, + value, null); + size = DOUBLEALIGN(size); /* be conservative */ + + tp = (char *) palloc(size); + tuple = (IndexTuple) tp; + memset(tp, 0, (int) size); + + DataFill((char *) tp + hoff, + tupleDescriptor, + value, + null, + &tupmask, + (hasnull ? (bits8 *) tp + sizeof(*tuple) : NULL)); + + /* + * We do this because DataFill wants to initialize a "tupmask" which + * is used for HeapTuples, but we want an indextuple infomask. The + * only "relevent" info is the "has variable attributes" field, which + * is in mask position 0x02. We have already set the null mask above. + */ + + if (tupmask & 0x02) + infomask |= INDEX_VAR_MASK; + + /* + * Here we make sure that we can actually hold the size. We also want + * to make sure that size is not aligned oddly. This actually is a + * rather odd way to make sure the size is not too large overall. + */ + + if (size & 0xE000) + elog(WARN, "index_formtuple: data takes %d bytes: too big", size); + + + infomask |= size; + + /* ---------------- + * initialize metadata + * ---------------- + */ + tuple->t_info = infomask; + return (tuple); } /* ---------------- - * fastgetiattr + * fastgetiattr * - * This is a newer version of fastgetiattr which attempts to be - * faster by caching attribute offsets in the attribute descriptor. + * This is a newer version of fastgetiattr which attempts to be + * faster by caching attribute offsets in the attribute descriptor. * - * an alternate way to speed things up would be to cache offsets - * with the tuple, but that seems more difficult unless you take - * the storage hit of actually putting those offsets into the - * tuple you send to disk. Yuck. + * an alternate way to speed things up would be to cache offsets + * with the tuple, but that seems more difficult unless you take + * the storage hit of actually putting those offsets into the + * tuple you send to disk. Yuck. * - * This scheme will be slightly slower than that, but should - * preform well for queries which hit large #'s of tuples. After - * you cache the offsets once, examining all the other tuples using - * the same attribute descriptor will go much quicker. -cim 5/4/91 + * This scheme will be slightly slower than that, but should + * preform well for queries which hit large #'s of tuples. After + * you cache the offsets once, examining all the other tuples using + * the same attribute descriptor will go much quicker. -cim 5/4/91 * ---------------- */ -static char * +static char * fastgetiattr(IndexTuple tup, - int attnum, - TupleDesc tupleDesc, - bool *isnull) + int attnum, + TupleDesc tupleDesc, + bool * isnull) { - register char *tp; /* ptr to att in tuple */ - register char *bp = NULL; /* ptr to att in tuple */ - int slow; /* do we have to walk nulls? */ - register int data_off; /* tuple data offset */ - AttributeTupleForm *att = tupleDesc->attrs; - - /* ---------------- - * sanity checks - * ---------------- - */ - - Assert(PointerIsValid(isnull)); - Assert(attnum > 0); - - /* ---------------- - * Three cases: - * - * 1: No nulls and no variable length attributes. - * 2: Has a null or a varlena AFTER att. - * 3: Has nulls or varlenas BEFORE att. - * ---------------- - */ - - *isnull = false; - data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup : - IndexInfoFindDataOffset(tup->t_info); - - if (IndexTupleNoNulls(tup)) { - - /* first attribute is always at position zero */ - - if (attnum == 1) { - return(fetchatt(&(att[0]), (char *) tup + data_off)); - } - attnum--; - - if (att[attnum]->attcacheoff > 0) { - return(fetchatt(&(att[attnum]), - (char *) tup + data_off + - att[attnum]->attcacheoff)); - } - - tp = (char *) tup + data_off; - - slow = 0; - }else { /* there's a null somewhere in the tuple */ - - bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are here! */ - slow = 0; + register char *tp; /* ptr to att in tuple */ + register char *bp = NULL; /* ptr to att in tuple */ + int slow; /* do we have to walk nulls? */ + register int data_off; /* tuple data offset */ + AttributeTupleForm *att = tupleDesc->attrs; + /* ---------------- - * check to see if desired att is null + * sanity checks * ---------------- */ - - attnum--; - { - if (att_isnull(attnum, bp)) { - *isnull = true; - return NULL; - } - } + + Assert(PointerIsValid(isnull)); + Assert(attnum > 0); + /* ---------------- - * Now check to see if any preceeding bits are null... + * Three cases: + * + * 1: No nulls and no variable length attributes. + * 2: Has a null or a varlena AFTER att. + * 3: Has nulls or varlenas BEFORE att. * ---------------- */ + + *isnull = false; + data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup : + IndexInfoFindDataOffset(tup->t_info); + + if (IndexTupleNoNulls(tup)) { - register int i = 0; /* current offset in bp */ - register int mask; /* bit in byte we're looking at */ - register char n; /* current byte in bp */ - register int byte, finalbit; - - byte = attnum >> 3; - finalbit = attnum & 0x07; - - for (; i <= byte; i++) { - n = bp[i]; - if (i < byte) { - /* check for nulls in any "earlier" bytes */ - if ((~n) != 0) { - slow++; - break; - } - } else { - /* check for nulls "before" final bit of last byte*/ - mask = (finalbit << 1) - 1; - if ((~n) & mask) - slow++; + + /* first attribute is always at position zero */ + + if (attnum == 1) + { + return (fetchatt(&(att[0]), (char *) tup + data_off)); + } + attnum--; + + if (att[attnum]->attcacheoff > 0) + { + return (fetchatt(&(att[attnum]), + (char *) tup + data_off + + att[attnum]->attcacheoff)); } - } + + tp = (char *) tup + data_off; + + slow = 0; } - tp = (char *) tup + data_off; - } - - /* now check for any non-fixed length attrs before our attribute */ - - if (!slow) { - if (att[attnum]->attcacheoff > 0) { - return(fetchatt(&(att[attnum]), - tp + att[attnum]->attcacheoff)); - }else if (!IndexTupleAllFixed(tup)) { - register int j = 0; - - for (j = 0; j < attnum && !slow; j++) - if (att[j]->attlen < 1) slow = 1; + else + { /* there's a null somewhere in the tuple */ + + bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are + * here! */ + slow = 0; + /* ---------------- + * check to see if desired att is null + * ---------------- + */ + + attnum--; + { + if (att_isnull(attnum, bp)) + { + *isnull = true; + return NULL; + } + } + /* ---------------- + * Now check to see if any preceeding bits are null... + * ---------------- + */ + { + register int i = 0; /* current offset in bp */ + register int mask; /* bit in byte we're looking at */ + register char n; /* current byte in bp */ + register int byte, + finalbit; + + byte = attnum >> 3; + finalbit = attnum & 0x07; + + for (; i <= byte; i++) + { + n = bp[i]; + if (i < byte) + { + /* check for nulls in any "earlier" bytes */ + if ((~n) != 0) + { + slow++; + break; + } + } + else + { + /* check for nulls "before" final bit of last byte */ + mask = (finalbit << 1) - 1; + if ((~n) & mask) + slow++; + } + } + } + tp = (char *) tup + data_off; } - } - - /* - * if slow is zero, and we got here, we know that we have a tuple with - * no nulls. We also know that we have to initialize the remainder of - * the attribute cached offset values. - */ - - if (!slow) { - register int j = 1; - register long off; - - /* - * need to set cache for some atts - */ - - att[0]->attcacheoff = 0; - - while (att[j]->attcacheoff > 0) j++; - - off = att[j-1]->attcacheoff + - att[j-1]->attlen; - - for (; j < attnum + 1; j++) { - /* - * Fix me when going to a machine with more than a four-byte - * word! - */ - - switch(att[j]->attlen) + + /* now check for any non-fixed length attrs before our attribute */ + + if (!slow) + { + if (att[attnum]->attcacheoff > 0) { - case -1: - off = (att[j]->attalign=='d')? - DOUBLEALIGN(off):INTALIGN(off); - break; - case sizeof(char): - break; - case sizeof(short): - off = SHORTALIGN(off); - break; - case sizeof(int32): - off = INTALIGN(off); - break; - default: - if (att[j]->attlen > sizeof(int32)) - off = (att[j]->attalign=='d')? - DOUBLEALIGN(off) : LONGALIGN(off); - else - elog(WARN, "fastgetiattr: attribute %d has len %d", - j, att[j]->attlen); - break; - + return (fetchatt(&(att[attnum]), + tp + att[attnum]->attcacheoff)); + } + else if (!IndexTupleAllFixed(tup)) + { + register int j = 0; + + for (j = 0; j < attnum && !slow; j++) + if (att[j]->attlen < 1) + slow = 1; } - - att[j]->attcacheoff = off; - off += att[j]->attlen; } - - return(fetchatt( &(att[attnum]), - tp + att[attnum]->attcacheoff)); - }else { - register bool usecache = true; - register int off = 0; - register int i; - + /* - * Now we know that we have to walk the tuple CAREFULLY. + * if slow is zero, and we got here, we know that we have a tuple with + * no nulls. We also know that we have to initialize the remainder of + * the attribute cached offset values. */ - - for (i = 0; i < attnum; i++) { - if (!IndexTupleNoNulls(tup)) { - if (att_isnull(i, bp)) { - usecache = false; - continue; + + if (!slow) + { + register int j = 1; + register long off; + + /* + * need to set cache for some atts + */ + + att[0]->attcacheoff = 0; + + while (att[j]->attcacheoff > 0) + j++; + + off = att[j - 1]->attcacheoff + + att[j - 1]->attlen; + + for (; j < attnum + 1; j++) + { + + /* + * Fix me when going to a machine with more than a four-byte + * word! + */ + + switch (att[j]->attlen) + { + case -1: + off = (att[j]->attalign == 'd') ? + DOUBLEALIGN(off) : INTALIGN(off); + break; + case sizeof(char): + break; + case sizeof(short): + off = SHORTALIGN(off); + break; + case sizeof(int32): + off = INTALIGN(off); + break; + default: + if (att[j]->attlen > sizeof(int32)) + off = (att[j]->attalign == 'd') ? + DOUBLEALIGN(off) : LONGALIGN(off); + else + elog(WARN, "fastgetiattr: attribute %d has len %d", + j, att[j]->attlen); + break; + + } + + att[j]->attcacheoff = off; + off += att[j]->attlen; } - } - - if (usecache && att[i]->attcacheoff > 0) { - off = att[i]->attcacheoff; - if (att[i]->attlen == -1) - usecache = false; - else - continue; - } - - if (usecache) att[i]->attcacheoff = off; - switch(att[i]->attlen) + + return (fetchatt(&(att[attnum]), + tp + att[attnum]->attcacheoff)); + } + else + { + register bool usecache = true; + register int off = 0; + register int i; + + /* + * Now we know that we have to walk the tuple CAREFULLY. + */ + + for (i = 0; i < attnum; i++) { + if (!IndexTupleNoNulls(tup)) + { + if (att_isnull(i, bp)) + { + usecache = false; + continue; + } + } + + if (usecache && att[i]->attcacheoff > 0) + { + off = att[i]->attcacheoff; + if (att[i]->attlen == -1) + usecache = false; + else + continue; + } + + if (usecache) + att[i]->attcacheoff = off; + switch (att[i]->attlen) + { + case sizeof(char): + off++; + break; + case sizeof(short): + off = SHORTALIGN(off) +sizeof(short); + break; + case sizeof(int32): + off = INTALIGN(off) + sizeof(int32); + break; + case -1: + usecache = false; + off = (att[i]->attalign == 'd') ? + DOUBLEALIGN(off) : INTALIGN(off); + off += VARSIZE(tp + off); + break; + default: + if (att[i]->attlen > sizeof(int32)) + off = (att[i]->attalign == 'd') ? + DOUBLEALIGN(off) + att[i]->attlen : + LONGALIGN(off) + att[i]->attlen; + else + elog(WARN, "fastgetiattr2: attribute %d has len %d", + i, att[i]->attlen); + + break; + } + } + + /* + * I don't know why this code was missed here! I've got it from + * heaptuple.c:fastgetattr(). - vadim 06/12/97 + */ + switch (att[attnum]->attlen) + { + case -1: + off = (att[attnum]->attalign == 'd') ? + DOUBLEALIGN(off) : INTALIGN(off); + break; case sizeof(char): - off++; - break; + break; case sizeof(short): - off = SHORTALIGN(off) + sizeof(short); - break; + off = SHORTALIGN(off); + break; case sizeof(int32): - off = INTALIGN(off) + sizeof(int32); - break; - case -1: - usecache = false; - off = (att[i]->attalign=='d')? - DOUBLEALIGN(off):INTALIGN(off); - off += VARSIZE(tp + off); - break; + off = INTALIGN(off); + break; default: - if (att[i]->attlen > sizeof(int32)) - off = (att[i]->attalign=='d') ? - DOUBLEALIGN(off) + att[i]->attlen : - LONGALIGN(off) + att[i]->attlen; - else - elog(WARN, "fastgetiattr2: attribute %d has len %d", - i, att[i]->attlen); - - break; + if (att[attnum]->attlen < sizeof(int32)) + elog(WARN, "fastgetattr3: attribute %d has len %d", + attnum, att[attnum]->attlen); + if (att[attnum]->attalign == 'd') + off = DOUBLEALIGN(off); + else + off = LONGALIGN(off); + break; } + + return (fetchatt(&att[attnum], tp + off)); } - /* - * I don't know why this code was missed here! - * I've got it from heaptuple.c:fastgetattr(). - * - vadim 06/12/97 - */ - switch (att[attnum]->attlen) { - case -1: - off = (att[attnum]->attalign=='d')? - DOUBLEALIGN(off) : INTALIGN(off); - break; - case sizeof(char): - break; - case sizeof(short): - off = SHORTALIGN(off); - break; - case sizeof(int32): - off = INTALIGN(off); - break; - default: - if (att[attnum]->attlen < sizeof(int32)) - elog(WARN, "fastgetattr3: attribute %d has len %d", - attnum, att[attnum]->attlen); - if (att[attnum]->attalign == 'd') - off = DOUBLEALIGN(off); - else - off = LONGALIGN(off); - break; - } - - return(fetchatt(&att[attnum], tp + off)); - } } /* ---------------- - * index_getattr + * index_getattr * ---------------- */ Datum index_getattr(IndexTuple tuple, - AttrNumber attNum, - TupleDesc tupDesc, - bool *isNullOutP) + AttrNumber attNum, + TupleDesc tupDesc, + bool * isNullOutP) { - Assert (attNum > 0); + Assert(attNum > 0); - return (Datum) - fastgetiattr(tuple, attNum, tupDesc, isNullOutP); + return (Datum) + fastgetiattr(tuple, attNum, tupDesc, isNullOutP); } RetrieveIndexResult FormRetrieveIndexResult(ItemPointer indexItemPointer, - ItemPointer heapItemPointer) + ItemPointer heapItemPointer) { - RetrieveIndexResult result; - - Assert(ItemPointerIsValid(indexItemPointer)); - Assert(ItemPointerIsValid(heapItemPointer)); - - result = (RetrieveIndexResult) palloc(sizeof *result); - - result->index_iptr = *indexItemPointer; - result->heap_iptr = *heapItemPointer; - - return (result); + RetrieveIndexResult result; + + Assert(ItemPointerIsValid(indexItemPointer)); + Assert(ItemPointerIsValid(heapItemPointer)); + + result = (RetrieveIndexResult) palloc(sizeof *result); + + result->index_iptr = *indexItemPointer; + result->heap_iptr = *heapItemPointer; + + return (result); } /* @@ -425,19 +461,21 @@ FormRetrieveIndexResult(ItemPointer indexItemPointer, * * Change me if adding an attribute to IndexTuples!!!!!!!!!!! */ -static Size +static Size IndexInfoFindDataOffset(unsigned short t_info) { - if (!(t_info & INDEX_NULL_MASK)) - return((Size) sizeof(IndexTupleData)); - else { - Size size = sizeof(IndexTupleData); - - if (t_info & INDEX_NULL_MASK) { - size += sizeof(IndexAttributeBitMapData); + if (!(t_info & INDEX_NULL_MASK)) + return ((Size) sizeof(IndexTupleData)); + else + { + Size size = sizeof(IndexTupleData); + + if (t_info & INDEX_NULL_MASK) + { + size += sizeof(IndexAttributeBitMapData); + } + return DOUBLEALIGN(size); /* be conservative */ } - return DOUBLEALIGN(size); /* be conservative */ - } } /* @@ -445,17 +483,17 @@ IndexInfoFindDataOffset(unsigned short t_info) * we assume we have space that is already palloc'ed. */ void -CopyIndexTuple(IndexTuple source, IndexTuple *target) +CopyIndexTuple(IndexTuple source, IndexTuple * target) { - Size size; - IndexTuple ret; - - size = IndexTupleSize(source); - if (*target == NULL) { - *target = (IndexTuple) palloc(size); - } - - ret = *target; - memmove((char*)ret, (char*)source, size); -} + Size size; + IndexTuple ret; + + size = IndexTupleSize(source); + if (*target == NULL) + { + *target = (IndexTuple) palloc(size); + } + ret = *target; + memmove((char *) ret, (char *) source, size); +} diff --git a/src/backend/access/common/indexvalid.c b/src/backend/access/common/indexvalid.c index aff9af42f8d..9f8501beb2e 100644 --- a/src/backend/access/common/indexvalid.c +++ b/src/backend/access/common/indexvalid.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * indexvalid.c-- - * index tuple qualification validity checking code + * index tuple qualification validity checking code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.14 1997/03/18 18:38:19 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.15 1997/09/07 04:37:38 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,64 +21,70 @@ #include <executor/execdebug.h> /* ---------------------------------------------------------------- - * index scan key qualification code + * index scan key qualification code * ---------------------------------------------------------------- */ -int NIndexTupleProcessed; +int NIndexTupleProcessed; /* ---------------- - * index_keytest + * index_keytest * * old comments - * May eventually combine with other tests (like timeranges)? - * Should have Buffer buffer; as an argument and pass it to amgetattr. + * May eventually combine with other tests (like timeranges)? + * Should have Buffer buffer; as an argument and pass it to amgetattr. * ---------------- */ bool index_keytest(IndexTuple tuple, - TupleDesc tupdesc, - int scanKeySize, - ScanKey key) + TupleDesc tupdesc, + int scanKeySize, + ScanKey key) { - bool isNull; - Datum datum; - int test; - - IncrIndexProcessed(); - - while (scanKeySize > 0) { - datum = index_getattr(tuple, - key[0].sk_attno, - tupdesc, - &isNull); - - if (isNull) { - /* XXX eventually should check if SK_ISNULL */ - return (false); - } - - if (key[0].sk_flags & SK_ISNULL) { - return (false); - } + bool isNull; + Datum datum; + int test; - if (key[0].sk_flags & SK_COMMUTE) { - test = (*(key[0].sk_func)) - (DatumGetPointer(key[0].sk_argument), - datum) ? 1 : 0; - } else { - test = (*(key[0].sk_func)) - (datum, - DatumGetPointer(key[0].sk_argument)) ? 1 : 0; - } - - if (!test == !(key[0].sk_flags & SK_NEGATE)) { - return (false); + IncrIndexProcessed(); + + while (scanKeySize > 0) + { + datum = index_getattr(tuple, + key[0].sk_attno, + tupdesc, + &isNull); + + if (isNull) + { + /* XXX eventually should check if SK_ISNULL */ + return (false); + } + + if (key[0].sk_flags & SK_ISNULL) + { + return (false); + } + + if (key[0].sk_flags & SK_COMMUTE) + { + test = (*(key[0].sk_func)) + (DatumGetPointer(key[0].sk_argument), + datum) ? 1 : 0; + } + else + { + test = (*(key[0].sk_func)) + (datum, + DatumGetPointer(key[0].sk_argument)) ? 1 : 0; + } + + if (!test == !(key[0].sk_flags & SK_NEGATE)) + { + return (false); + } + + scanKeySize -= 1; + key++; } - - scanKeySize -= 1; - key++; - } - - return (true); -} + return (true); +} diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 98fbddc639d..599ac59a455 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * printtup.c-- - * Routines to print out tuples to the destination (binary or non-binary - * portals, frontend/interactive backend, etc.). + * Routines to print out tuples to the destination (binary or non-binary + * portals, frontend/interactive backend, etc.). * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.15 1997/08/26 23:31:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.16 1997/09/07 04:37:39 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -16,279 +16,304 @@ #include <string.h> #include <postgres.h> -#include <fmgr.h> -#include <access/heapam.h> -#include <access/printtup.h> +#include <fmgr.h> +#include <access/heapam.h> +#include <access/printtup.h> #include <catalog/pg_type.h> #include <libpq/libpq.h> #include <utils/syscache.h> /* ---------------------------------------------------------------- - * printtup / debugtup support + * printtup / debugtup support * ---------------------------------------------------------------- */ /* ---------------- - * typtoout - used by printtup and debugtup + * typtoout - used by printtup and debugtup * ---------------- */ Oid typtoout(Oid type) { - HeapTuple typeTuple; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type), - 0, 0, 0); - - if (HeapTupleIsValid(typeTuple)) - return((Oid) - ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput); - - elog(WARN, "typtoout: Cache lookup of type %d failed", type); - return(InvalidOid); + HeapTuple typeTuple; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type), + 0, 0, 0); + + if (HeapTupleIsValid(typeTuple)) + return ((Oid) + ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput); + + elog(WARN, "typtoout: Cache lookup of type %d failed", type); + return (InvalidOid); } Oid gettypelem(Oid type) { - HeapTuple typeTuple; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type), - 0,0,0); - - if (HeapTupleIsValid(typeTuple)) - return((Oid) - ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem); - - elog(WARN, "typtoout: Cache lookup of type %d failed", type); - return(InvalidOid); + HeapTuple typeTuple; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type), + 0, 0, 0); + + if (HeapTupleIsValid(typeTuple)) + return ((Oid) + ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem); + + elog(WARN, "typtoout: Cache lookup of type %d failed", type); + return (InvalidOid); } /* ---------------- - * printtup + * printtup * ---------------- */ void printtup(HeapTuple tuple, TupleDesc typeinfo) { - int i, j, k; - char *outputstr, *attr; - bool isnull; - Oid typoutput; - - /* ---------------- - * tell the frontend to expect new tuple data - * ---------------- - */ - pq_putnchar("D", 1); - - /* ---------------- - * send a bitmap of which attributes are null - * ---------------- - */ - j = 0; - k = 1 << 7; - for (i = 0; i < tuple->t_natts; ) { - i++; /* heap_getattr is a macro, so no increment */ - attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull); - if (!isnull) - j |= k; - k >>= 1; - if (!(i & 7)) { - pq_putint(j, 1); - j = 0; - k = 1 << 7; + int i, + j, + k; + char *outputstr, + *attr; + bool isnull; + Oid typoutput; + + /* ---------------- + * tell the frontend to expect new tuple data + * ---------------- + */ + pq_putnchar("D", 1); + + /* ---------------- + * send a bitmap of which attributes are null + * ---------------- + */ + j = 0; + k = 1 << 7; + for (i = 0; i < tuple->t_natts;) + { + i++; /* heap_getattr is a macro, so no + * increment */ + attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull); + if (!isnull) + j |= k; + k >>= 1; + if (!(i & 7)) + { + pq_putint(j, 1); + j = 0; + k = 1 << 7; + } } - } - if (i & 7) - pq_putint(j, 1); - - /* ---------------- - * send the attributes of this tuple - * ---------------- - */ - for (i = 0; i < tuple->t_natts; ++i) { - attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull); - typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); - - if (!isnull && OidIsValid(typoutput)) { - outputstr = fmgr(typoutput, attr, - gettypelem(typeinfo->attrs[i]->atttypid)); - pq_putint(strlen(outputstr)+4, 4); - pq_putnchar(outputstr, strlen(outputstr)); - pfree(outputstr); + if (i & 7) + pq_putint(j, 1); + + /* ---------------- + * send the attributes of this tuple + * ---------------- + */ + for (i = 0; i < tuple->t_natts; ++i) + { + attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull); + typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); + + if (!isnull && OidIsValid(typoutput)) + { + outputstr = fmgr(typoutput, attr, + gettypelem(typeinfo->attrs[i]->atttypid)); + pq_putint(strlen(outputstr) + 4, 4); + pq_putnchar(outputstr, strlen(outputstr)); + pfree(outputstr); + } } - } } /* ---------------- - * printatt + * printatt * ---------------- */ static void printatt(unsigned attributeId, - AttributeTupleForm attributeP, - char *value) + AttributeTupleForm attributeP, + char *value) { - printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n", - attributeId, - attributeP->attname.data, - value != NULL ? " = \"" : "", - value != NULL ? value : "", - value != NULL ? "\"" : "", - (unsigned int) (attributeP->atttypid), - attributeP->attlen, - attributeP->attbyval ? 't' : 'f'); + printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n", + attributeId, + attributeP->attname.data, + value != NULL ? " = \"" : "", + value != NULL ? value : "", + value != NULL ? "\"" : "", + (unsigned int) (attributeP->atttypid), + attributeP->attlen, + attributeP->attbyval ? 't' : 'f'); } /* ---------------- - * showatts + * showatts * ---------------- */ void showatts(char *name, TupleDesc tupleDesc) { - int i; - int natts = tupleDesc->natts; - AttributeTupleForm *attinfo = tupleDesc->attrs; + int i; + int natts = tupleDesc->natts; + AttributeTupleForm *attinfo = tupleDesc->attrs; - puts(name); - for (i = 0; i < natts; ++i) - printatt((unsigned) i+1, attinfo[i], (char *) NULL); - printf("\t----\n"); + puts(name); + for (i = 0; i < natts; ++i) + printatt((unsigned) i + 1, attinfo[i], (char *) NULL); + printf("\t----\n"); } /* ---------------- - * debugtup + * debugtup * ---------------- */ void debugtup(HeapTuple tuple, TupleDesc typeinfo) { - register int i; - char *attr, *value; - bool isnull; - Oid typoutput; - - for (i = 0; i < tuple->t_natts; ++i) { - attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull); - typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); - - if (!isnull && OidIsValid(typoutput)) { - value = fmgr(typoutput, attr, - gettypelem(typeinfo->attrs[i]->atttypid)); - printatt((unsigned) i+1, typeinfo->attrs[i], value); - pfree(value); + register int i; + char *attr, + *value; + bool isnull; + Oid typoutput; + + for (i = 0; i < tuple->t_natts; ++i) + { + attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull); + typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); + + if (!isnull && OidIsValid(typoutput)) + { + value = fmgr(typoutput, attr, + gettypelem(typeinfo->attrs[i]->atttypid)); + printatt((unsigned) i + 1, typeinfo->attrs[i], value); + pfree(value); + } } - } - printf("\t----\n"); + printf("\t----\n"); } /* ---------------- - * printtup_internal - * Protocol expects either T, D, C, E, or N. - * We use a different data prefix, e.g. 'B' instead of 'D' to - * indicate a tuple in internal (binary) form. + * printtup_internal + * Protocol expects either T, D, C, E, or N. + * We use a different data prefix, e.g. 'B' instead of 'D' to + * indicate a tuple in internal (binary) form. * - * This is same as printtup, except we don't use the typout func. + * This is same as printtup, except we don't use the typout func. * ---------------- */ void printtup_internal(HeapTuple tuple, TupleDesc typeinfo) { - int i, j, k; - char *attr; - bool isnull; - - /* ---------------- - * tell the frontend to expect new tuple data - * ---------------- - */ - pq_putnchar("B", 1); - - /* ---------------- - * send a bitmap of which attributes are null - * ---------------- - */ - j = 0; - k = 1 << 7; - for (i = 0; i < tuple->t_natts; ) { - i++; /* heap_getattr is a macro, so no increment */ - attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull); - if (!isnull) - j |= k; - k >>= 1; - if (!(i & 7)) { - pq_putint(j, 1); - j = 0; - k = 1 << 7; + int i, + j, + k; + char *attr; + bool isnull; + + /* ---------------- + * tell the frontend to expect new tuple data + * ---------------- + */ + pq_putnchar("B", 1); + + /* ---------------- + * send a bitmap of which attributes are null + * ---------------- + */ + j = 0; + k = 1 << 7; + for (i = 0; i < tuple->t_natts;) + { + i++; /* heap_getattr is a macro, so no + * increment */ + attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull); + if (!isnull) + j |= k; + k >>= 1; + if (!(i & 7)) + { + pq_putint(j, 1); + j = 0; + k = 1 << 7; + } } - } - if (i & 7) - pq_putint(j, 1); - - /* ---------------- - * send the attributes of this tuple - * ---------------- - */ + if (i & 7) + pq_putint(j, 1); + + /* ---------------- + * send the attributes of this tuple + * ---------------- + */ #ifdef IPORTAL_DEBUG - fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts); + fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts); #endif - for (i = 0; i < tuple->t_natts; ++i) { - int32 len = typeinfo->attrs[i]->attlen; - - attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull); - if (!isnull) { - /* # of bytes, and opaque data */ - if (len == -1) { - /* variable length, assume a varlena structure */ - len = VARSIZE(attr) - VARHDRSZ; - - pq_putint(len, sizeof(int32)); - pq_putnchar(VARDATA(attr), len); -#ifdef IPORTAL_DEBUG + for (i = 0; i < tuple->t_natts; ++i) + { + int32 len = typeinfo->attrs[i]->attlen; + + attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull); + if (!isnull) { - char *d = VARDATA(attr); - - fprintf(stderr, "length %d data %x%x%x%x\n", - len, *d, *(d+1), *(d+2), *(d+3)); - } + /* # of bytes, and opaque data */ + if (len == -1) + { + /* variable length, assume a varlena structure */ + len = VARSIZE(attr) - VARHDRSZ; + + pq_putint(len, sizeof(int32)); + pq_putnchar(VARDATA(attr), len); +#ifdef IPORTAL_DEBUG + { + char *d = VARDATA(attr); + + fprintf(stderr, "length %d data %x%x%x%x\n", + len, *d, *(d + 1), *(d + 2), *(d + 3)); + } #endif - } else { - /* fixed size */ - if (typeinfo->attrs[i]->attbyval) { - int8 i8; - int16 i16; - int32 i32; - - pq_putint(len, sizeof(int32)); - switch (len) { - case sizeof(int8): - i8 = DatumGetChar(attr); - pq_putnchar((char *) &i8, len); - break; - case sizeof(int16): - i16 = DatumGetInt16(attr); - pq_putnchar((char *) &i16, len); - break; - case sizeof(int32): - i32 = DatumGetInt32(attr); - pq_putnchar((char *) &i32, len); - break; - } + } + else + { + /* fixed size */ + if (typeinfo->attrs[i]->attbyval) + { + int8 i8; + int16 i16; + int32 i32; + + pq_putint(len, sizeof(int32)); + switch (len) + { + case sizeof(int8): + i8 = DatumGetChar(attr); + pq_putnchar((char *) &i8, len); + break; + case sizeof(int16): + i16 = DatumGetInt16(attr); + pq_putnchar((char *) &i16, len); + break; + case sizeof(int32): + i32 = DatumGetInt32(attr); + pq_putnchar((char *) &i32, len); + break; + } #ifdef IPORTAL_DEBUG - fprintf(stderr, "byval length %d data %d\n", len, attr); + fprintf(stderr, "byval length %d data %d\n", len, attr); #endif - } else { - pq_putint(len, sizeof(int32)); - pq_putnchar(attr, len); + } + else + { + pq_putint(len, sizeof(int32)); + pq_putnchar(attr, len); #ifdef IPORTAL_DEBUG - fprintf(stderr, "byref length %d data %x\n", len, attr); + fprintf(stderr, "byref length %d data %x\n", len, attr); #endif + } + } } - } } - } } diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c index fb242497ebc..9fbe264ae5c 100644 --- a/src/backend/access/common/scankey.c +++ b/src/backend/access/common/scankey.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * scan.c-- - * scan direction and key code + * scan direction and key code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.9 1996/11/05 07:42:45 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.10 1997/09/07 04:37:39 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -19,49 +19,49 @@ /* * ScanKeyEntryIsLegal -- - * True iff the scan key entry is legal. + * True iff the scan key entry is legal. */ #define ScanKeyEntryIsLegal(entry) \ - ((bool) (AssertMacro(PointerIsValid(entry)) && \ - AttributeNumberIsValid(entry->sk_attno))) + ((bool) (AssertMacro(PointerIsValid(entry)) && \ + AttributeNumberIsValid(entry->sk_attno))) /* * ScanKeyEntrySetIllegal -- - * Marks a scan key entry as illegal. + * Marks a scan key entry as illegal. */ void ScanKeyEntrySetIllegal(ScanKey entry) { - Assert(PointerIsValid(entry)); - - entry->sk_flags = 0; /* just in case... */ - entry->sk_attno = InvalidAttrNumber; - entry->sk_procedure = 0; /* should be InvalidRegProcedure */ + Assert(PointerIsValid(entry)); + + entry->sk_flags = 0; /* just in case... */ + entry->sk_attno = InvalidAttrNumber; + entry->sk_procedure = 0; /* should be InvalidRegProcedure */ } /* * ScanKeyEntryInitialize -- - * Initializes an scan key entry. + * Initializes an scan key entry. * * Note: - * Assumes the scan key entry is valid. - * Assumes the intialized scan key entry will be legal. + * Assumes the scan key entry is valid. + * Assumes the intialized scan key entry will be legal. */ void ScanKeyEntryInitialize(ScanKey entry, - bits16 flags, - AttrNumber attributeNumber, - RegProcedure procedure, - Datum argument) + bits16 flags, + AttrNumber attributeNumber, + RegProcedure procedure, + Datum argument) { - Assert(PointerIsValid(entry)); - - entry->sk_flags = flags; - entry->sk_attno = attributeNumber; - entry->sk_procedure = procedure; - entry->sk_argument = argument; - fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs); - - Assert(ScanKeyEntryIsLegal(entry)); + Assert(PointerIsValid(entry)); + + entry->sk_flags = flags; + entry->sk_attno = attributeNumber; + entry->sk_procedure = procedure; + entry->sk_argument = argument; + fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs); + + Assert(ScanKeyEntryIsLegal(entry)); } diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index a38a5229f28..e616702a8ea 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * tupdesc.c-- - * POSTGRES tuple descriptor support code + * POSTGRES tuple descriptor support code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.19 1997/08/22 02:55:39 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.20 1997/09/07 04:37:41 momjian Exp $ * * NOTES - * some of the executor utility code such as "ExecTypeFromTL" should be - * moved here. + * some of the executor utility code such as "ExecTypeFromTL" should be + * moved here. * *------------------------------------------------------------------------- */ @@ -28,518 +28,534 @@ #include <utils/syscache.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* ---------------------------------------------------------------- - * CreateTemplateTupleDesc + * CreateTemplateTupleDesc * - * This function allocates and zeros a tuple descriptor structure. + * This function allocates and zeros a tuple descriptor structure. * ---------------------------------------------------------------- */ TupleDesc CreateTemplateTupleDesc(int natts) { - uint32 size; - TupleDesc desc; - - /* ---------------- - * sanity checks - * ---------------- - */ - AssertArg(natts >= 1); - - /* ---------------- - * allocate enough memory for the tuple descriptor and - * zero it as TupleDescInitEntry assumes that the descriptor - * is filled with NULL pointers. - * ---------------- - */ - size = natts * sizeof (AttributeTupleForm); - desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); - desc->attrs = (AttributeTupleForm*) palloc(size); - desc->constr = NULL; - memset(desc->attrs, 0, size); - - desc->natts = natts; - - return (desc); + uint32 size; + TupleDesc desc; + + /* ---------------- + * sanity checks + * ---------------- + */ + AssertArg(natts >= 1); + + /* ---------------- + * allocate enough memory for the tuple descriptor and + * zero it as TupleDescInitEntry assumes that the descriptor + * is filled with NULL pointers. + * ---------------- + */ + size = natts * sizeof(AttributeTupleForm); + desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); + desc->attrs = (AttributeTupleForm *) palloc(size); + desc->constr = NULL; + memset(desc->attrs, 0, size); + + desc->natts = natts; + + return (desc); } /* ---------------------------------------------------------------- - * CreateTupleDesc + * CreateTupleDesc * - * This function allocates a new TupleDesc from AttributeTupleForm array + * This function allocates a new TupleDesc from AttributeTupleForm array * ---------------------------------------------------------------- */ TupleDesc -CreateTupleDesc(int natts, AttributeTupleForm* attrs) +CreateTupleDesc(int natts, AttributeTupleForm * attrs) { - TupleDesc desc; - - /* ---------------- - * sanity checks - * ---------------- - */ - AssertArg(natts >= 1); - - desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); - desc->attrs = attrs; - desc->natts = natts; - desc->constr = NULL; - - return (desc); + TupleDesc desc; + + /* ---------------- + * sanity checks + * ---------------- + */ + AssertArg(natts >= 1); + + desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); + desc->attrs = attrs; + desc->natts = natts; + desc->constr = NULL; + + return (desc); } /* ---------------------------------------------------------------- - * CreateTupleDescCopy + * CreateTupleDescCopy * - * This function creates a new TupleDesc by copying from an existing - * TupleDesc - * - * !!! Constraints are not copied !!! + * This function creates a new TupleDesc by copying from an existing + * TupleDesc + * + * !!! Constraints are not copied !!! * ---------------------------------------------------------------- */ TupleDesc CreateTupleDescCopy(TupleDesc tupdesc) { - TupleDesc desc; - int i, size; - - desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); - desc->natts = tupdesc->natts; - size = desc->natts * sizeof (AttributeTupleForm); - desc->attrs = (AttributeTupleForm*) palloc(size); - for (i=0;i<desc->natts;i++) { - desc->attrs[i] = - (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); - memmove(desc->attrs[i], - tupdesc->attrs[i], - ATTRIBUTE_TUPLE_SIZE); - desc->attrs[i]->attnotnull = false; - desc->attrs[i]->atthasdef = false; - } - desc->constr = NULL; - - return desc; + TupleDesc desc; + int i, + size; + + desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); + desc->natts = tupdesc->natts; + size = desc->natts * sizeof(AttributeTupleForm); + desc->attrs = (AttributeTupleForm *) palloc(size); + for (i = 0; i < desc->natts; i++) + { + desc->attrs[i] = + (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + memmove(desc->attrs[i], + tupdesc->attrs[i], + ATTRIBUTE_TUPLE_SIZE); + desc->attrs[i]->attnotnull = false; + desc->attrs[i]->atthasdef = false; + } + desc->constr = NULL; + + return desc; } /* ---------------------------------------------------------------- - * CreateTupleDescCopyConstr + * CreateTupleDescCopyConstr + * + * This function creates a new TupleDesc by copying from an existing + * TupleDesc (with Constraints) * - * This function creates a new TupleDesc by copying from an existing - * TupleDesc (with Constraints) - * * ---------------------------------------------------------------- */ TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc) { - TupleDesc desc; - TupleConstr *constr = tupdesc->constr; - int i, size; - - desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); - desc->natts = tupdesc->natts; - size = desc->natts * sizeof (AttributeTupleForm); - desc->attrs = (AttributeTupleForm*) palloc(size); - for (i=0;i<desc->natts;i++) { - desc->attrs[i] = - (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); - memmove(desc->attrs[i], - tupdesc->attrs[i], - ATTRIBUTE_TUPLE_SIZE); - } - if (constr) - { - TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr)); - - cpy->has_not_null = constr->has_not_null; - - if ( ( cpy->num_defval = constr->num_defval ) > 0 ) - { - cpy->defval = (AttrDefault *) palloc (cpy->num_defval * sizeof (AttrDefault)); - memcpy (cpy->defval, constr->defval, cpy->num_defval * sizeof (AttrDefault)); - for (i = cpy->num_defval - 1; i >= 0; i--) - { - if ( constr->defval[i].adbin ) - cpy->defval[i].adbin = pstrdup (constr->defval[i].adbin); - if ( constr->defval[i].adsrc ) - cpy->defval[i].adsrc = pstrdup (constr->defval[i].adsrc); - } - } - - if ( ( cpy->num_check = constr->num_check ) > 0 ) - { - cpy->check = (ConstrCheck *) palloc (cpy->num_check * sizeof (ConstrCheck)); - memcpy (cpy->check, constr->check, cpy->num_check * sizeof (ConstrCheck)); - for (i = cpy->num_check - 1; i >= 0; i--) - { - if ( constr->check[i].ccname ) - cpy->check[i].ccname = pstrdup (constr->check[i].ccname); - if ( constr->check[i].ccbin ) - cpy->check[i].ccbin = pstrdup (constr->check[i].ccbin); - if ( constr->check[i].ccsrc ) - cpy->check[i].ccsrc = pstrdup (constr->check[i].ccsrc); - } - } - - desc->constr = cpy; - } - else - desc->constr = NULL; - - return desc; + TupleDesc desc; + TupleConstr *constr = tupdesc->constr; + int i, + size; + + desc = (TupleDesc) palloc(sizeof(struct tupleDesc)); + desc->natts = tupdesc->natts; + size = desc->natts * sizeof(AttributeTupleForm); + desc->attrs = (AttributeTupleForm *) palloc(size); + for (i = 0; i < desc->natts; i++) + { + desc->attrs[i] = + (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + memmove(desc->attrs[i], + tupdesc->attrs[i], + ATTRIBUTE_TUPLE_SIZE); + } + if (constr) + { + TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr)); + + cpy->has_not_null = constr->has_not_null; + + if ((cpy->num_defval = constr->num_defval) > 0) + { + cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault)); + memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault)); + for (i = cpy->num_defval - 1; i >= 0; i--) + { + if (constr->defval[i].adbin) + cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin); + if (constr->defval[i].adsrc) + cpy->defval[i].adsrc = pstrdup(constr->defval[i].adsrc); + } + } + + if ((cpy->num_check = constr->num_check) > 0) + { + cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck)); + memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck)); + for (i = cpy->num_check - 1; i >= 0; i--) + { + if (constr->check[i].ccname) + cpy->check[i].ccname = pstrdup(constr->check[i].ccname); + if (constr->check[i].ccbin) + cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin); + if (constr->check[i].ccsrc) + cpy->check[i].ccsrc = pstrdup(constr->check[i].ccsrc); + } + } + + desc->constr = cpy; + } + else + desc->constr = NULL; + + return desc; } void -FreeTupleDesc (TupleDesc tupdesc) +FreeTupleDesc(TupleDesc tupdesc) { - int i; - - for (i = 0; i < tupdesc->natts; i++) - pfree (tupdesc->attrs[i]); - pfree (tupdesc->attrs); - if ( tupdesc->constr ) - { - if ( tupdesc->constr->num_defval > 0 ) - { - AttrDefault *attrdef = tupdesc->constr->defval; - - for (i = tupdesc->constr->num_defval - 1; i >= 0; i--) - { - if ( attrdef[i].adbin ) - pfree (attrdef[i].adbin); - if ( attrdef[i].adsrc ) - pfree (attrdef[i].adsrc); - } - pfree (attrdef); - } - if ( tupdesc->constr->num_check > 0 ) - { - ConstrCheck *check = tupdesc->constr->check; - - for (i = tupdesc->constr->num_check - 1; i >= 0; i--) - { - if ( check[i].ccname ) - pfree (check[i].ccname); - if ( check[i].ccbin ) - pfree (check[i].ccbin); - if ( check[i].ccsrc ) - pfree (check[i].ccsrc); - } - pfree (check); - } - pfree (tupdesc->constr); - } - - pfree (tupdesc); + int i; + + for (i = 0; i < tupdesc->natts; i++) + pfree(tupdesc->attrs[i]); + pfree(tupdesc->attrs); + if (tupdesc->constr) + { + if (tupdesc->constr->num_defval > 0) + { + AttrDefault *attrdef = tupdesc->constr->defval; + + for (i = tupdesc->constr->num_defval - 1; i >= 0; i--) + { + if (attrdef[i].adbin) + pfree(attrdef[i].adbin); + if (attrdef[i].adsrc) + pfree(attrdef[i].adsrc); + } + pfree(attrdef); + } + if (tupdesc->constr->num_check > 0) + { + ConstrCheck *check = tupdesc->constr->check; + + for (i = tupdesc->constr->num_check - 1; i >= 0; i--) + { + if (check[i].ccname) + pfree(check[i].ccname); + if (check[i].ccbin) + pfree(check[i].ccbin); + if (check[i].ccsrc) + pfree(check[i].ccsrc); + } + pfree(check); + } + pfree(tupdesc->constr); + } + + pfree(tupdesc); } /* ---------------------------------------------------------------- - * TupleDescInitEntry + * TupleDescInitEntry * - * This function initializes a single attribute structure in - * a preallocated tuple descriptor. + * This function initializes a single attribute structure in + * a preallocated tuple descriptor. * ---------------------------------------------------------------- */ bool TupleDescInitEntry(TupleDesc desc, - AttrNumber attributeNumber, - char *attributeName, - char *typeName, - int attdim, - bool attisset) + AttrNumber attributeNumber, + char *attributeName, + char *typeName, + int attdim, + bool attisset) { - HeapTuple tuple; - TypeTupleForm typeForm; - AttributeTupleForm att; - - /* ---------------- - * sanity checks - * ---------------- - */ - AssertArg(PointerIsValid(desc)); - AssertArg(attributeNumber >= 1); - /* attributeName's are sometimes NULL, - from resdom's. I don't know why that is, though -- Jolly */ -/* AssertArg(NameIsValid(attributeName));*/ -/* AssertArg(NameIsValid(typeName));*/ - - AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1])); - - - /* ---------------- - * allocate storage for this attribute - * ---------------- - */ - - att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); - desc->attrs[attributeNumber - 1] = att; - - /* ---------------- - * initialize some of the attribute fields - * ---------------- - */ - att->attrelid = 0; /* dummy value */ - - if (attributeName != NULL) - namestrcpy(&(att->attname), attributeName); - else - memset(att->attname.data,0,NAMEDATALEN); - - - att->attdisbursion = 0; /* dummy value */ - att->attcacheoff = -1; - - att->attnum = attributeNumber; - att->attnelems = attdim; - att->attisset = attisset; - - att->attnotnull = false; - att->atthasdef = false; - - /* ---------------- - * search the system cache for the type tuple of the attribute - * we are creating so that we can get the typeid and some other - * stuff. - * - * Note: in the special case of - * - * create EMP (name = char16, manager = EMP) - * - * RelationNameCreateHeapRelation() calls BuildDesc() which - * calls this routine and since EMP does not exist yet, the - * system cache lookup below fails. That's fine, but rather - * then doing a elog(WARN) we just leave that information - * uninitialized, return false, then fix things up later. - * -cim 6/14/90 - * ---------------- - */ - tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName), - 0,0,0); - if (! HeapTupleIsValid(tuple)) { + HeapTuple tuple; + TypeTupleForm typeForm; + AttributeTupleForm att; + /* ---------------- - * here type info does not exist yet so we just fill - * the attribute with dummy information and return false. + * sanity checks * ---------------- */ - att->atttypid = InvalidOid; - att->attlen = (int16) 0; - att->attbyval = (bool) 0; - att->attalign = 'i'; - return false; - } - - /* ---------------- - * type info exists so we initialize our attribute - * information from the type tuple we found.. - * ---------------- - */ - typeForm = (TypeTupleForm) GETSTRUCT(tuple); - - att->atttypid = tuple->t_oid; - att->attalign = typeForm->typalign; - - /* ------------------------ - If this attribute is a set, what is really stored in the - attribute is the OID of a tuple in the pg_proc catalog. - The pg_proc tuple contains the query string which defines - this set - i.e., the query to run to get the set. - So the atttypid (just assigned above) refers to the type returned - by this query, but the actual length of this attribute is the - length (size) of an OID. - - Why not just make the atttypid point to the OID type, instead - of the type the query returns? Because the executor uses the atttypid - to tell the front end what type will be returned (in BeginCommand), - and in the end the type returned will be the result of the query, not - an OID. - - Why not wait until the return type of the set is known (i.e., the - recursive call to the executor to execute the set has returned) - before telling the front end what the return type will be? Because - the executor is a delicate thing, and making sure that the correct - order of front-end commands is maintained is messy, especially - considering that target lists may change as inherited attributes - are considered, etc. Ugh. - ----------------------------------------- - */ - if (attisset) { - Type t = type("oid"); - att->attlen = tlen(t); - att->attbyval = tbyval(t); - } else { - att->attlen = typeForm->typlen; - att->attbyval = typeForm->typbyval; - } - - - return true; + AssertArg(PointerIsValid(desc)); + AssertArg(attributeNumber >= 1); + + /* + * attributeName's are sometimes NULL, from resdom's. I don't know + * why that is, though -- Jolly + */ +/* AssertArg(NameIsValid(attributeName));*/ +/* AssertArg(NameIsValid(typeName));*/ + + AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1])); + + + /* ---------------- + * allocate storage for this attribute + * ---------------- + */ + + att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + desc->attrs[attributeNumber - 1] = att; + + /* ---------------- + * initialize some of the attribute fields + * ---------------- + */ + att->attrelid = 0; /* dummy value */ + + if (attributeName != NULL) + namestrcpy(&(att->attname), attributeName); + else + memset(att->attname.data, 0, NAMEDATALEN); + + + att->attdisbursion = 0; /* dummy value */ + att->attcacheoff = -1; + + att->attnum = attributeNumber; + att->attnelems = attdim; + att->attisset = attisset; + + att->attnotnull = false; + att->atthasdef = false; + + /* ---------------- + * search the system cache for the type tuple of the attribute + * we are creating so that we can get the typeid and some other + * stuff. + * + * Note: in the special case of + * + * create EMP (name = char16, manager = EMP) + * + * RelationNameCreateHeapRelation() calls BuildDesc() which + * calls this routine and since EMP does not exist yet, the + * system cache lookup below fails. That's fine, but rather + * then doing a elog(WARN) we just leave that information + * uninitialized, return false, then fix things up later. + * -cim 6/14/90 + * ---------------- + */ + tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + /* ---------------- + * here type info does not exist yet so we just fill + * the attribute with dummy information and return false. + * ---------------- + */ + att->atttypid = InvalidOid; + att->attlen = (int16) 0; + att->attbyval = (bool) 0; + att->attalign = 'i'; + return false; + } + + /* ---------------- + * type info exists so we initialize our attribute + * information from the type tuple we found.. + * ---------------- + */ + typeForm = (TypeTupleForm) GETSTRUCT(tuple); + + att->atttypid = tuple->t_oid; + att->attalign = typeForm->typalign; + + /* ------------------------ + If this attribute is a set, what is really stored in the + attribute is the OID of a tuple in the pg_proc catalog. + The pg_proc tuple contains the query string which defines + this set - i.e., the query to run to get the set. + So the atttypid (just assigned above) refers to the type returned + by this query, but the actual length of this attribute is the + length (size) of an OID. + + Why not just make the atttypid point to the OID type, instead + of the type the query returns? Because the executor uses the atttypid + to tell the front end what type will be returned (in BeginCommand), + and in the end the type returned will be the result of the query, not + an OID. + + Why not wait until the return type of the set is known (i.e., the + recursive call to the executor to execute the set has returned) + before telling the front end what the return type will be? Because + the executor is a delicate thing, and making sure that the correct + order of front-end commands is maintained is messy, especially + considering that target lists may change as inherited attributes + are considered, etc. Ugh. + ----------------------------------------- + */ + if (attisset) + { + Type t = type("oid"); + + att->attlen = tlen(t); + att->attbyval = tbyval(t); + } + else + { + att->attlen = typeForm->typlen; + att->attbyval = typeForm->typbyval; + } + + + return true; } /* ---------------------------------------------------------------- - * TupleDescMakeSelfReference + * TupleDescMakeSelfReference * - * This function initializes a "self-referential" attribute like - * manager in "create EMP (name=text, manager = EMP)". - * It calls TypeShellMake() which inserts a "shell" type - * tuple into pg_type. A self-reference is one kind of set, so - * its size and byval are the same as for a set. See the comments - * above in TupleDescInitEntry. + * This function initializes a "self-referential" attribute like + * manager in "create EMP (name=text, manager = EMP)". + * It calls TypeShellMake() which inserts a "shell" type + * tuple into pg_type. A self-reference is one kind of set, so + * its size and byval are the same as for a set. See the comments + * above in TupleDescInitEntry. * ---------------------------------------------------------------- */ static void TupleDescMakeSelfReference(TupleDesc desc, - AttrNumber attnum, - char *relname) + AttrNumber attnum, + char *relname) { - AttributeTupleForm att; - Type t = type("oid"); - - att = desc->attrs[attnum-1]; - att->atttypid = TypeShellMake(relname); - att->attlen = tlen(t); - att->attbyval = tbyval(t); - att->attnelems = 0; + AttributeTupleForm att; + Type t = type("oid"); + + att = desc->attrs[attnum - 1]; + att->atttypid = TypeShellMake(relname); + att->attlen = tlen(t); + att->attbyval = tbyval(t); + att->attnelems = 0; } /* ---------------------------------------------------------------- - * BuildDescForRelation + * BuildDescForRelation * - * This is a general purpose function identical to BuildDesc - * but is used by the DefineRelation() code to catch the - * special case where you + * This is a general purpose function identical to BuildDesc + * but is used by the DefineRelation() code to catch the + * special case where you * - * create FOO ( ..., x = FOO ) + * create FOO ( ..., x = FOO ) * - * here, the initial type lookup for "x = FOO" will fail - * because FOO isn't in the catalogs yet. But since we - * are creating FOO, instead of doing an elog() we add - * a shell type tuple to pg_type and fix things later - * in amcreate(). + * here, the initial type lookup for "x = FOO" will fail + * because FOO isn't in the catalogs yet. But since we + * are creating FOO, instead of doing an elog() we add + * a shell type tuple to pg_type and fix things later + * in amcreate(). * ---------------------------------------------------------------- */ TupleDesc -BuildDescForRelation(List *schema, char *relname) +BuildDescForRelation(List * schema, char *relname) { - int natts; - AttrNumber attnum; - List *p; - TupleDesc desc; - AttrDefault *attrdef = NULL; - TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr)); - char *attname; - char *typename; - int attdim; - int ndef = 0; - bool attisset; - - /* ---------------- - * allocate a new tuple descriptor - * ---------------- - */ - natts = length(schema); - desc = CreateTemplateTupleDesc(natts); - constr->has_not_null = false; - - attnum = 0; - - typename = palloc(NAMEDATALEN); - - foreach(p, schema) { - ColumnDef *entry; - List *arry; + int natts; + AttrNumber attnum; + List *p; + TupleDesc desc; + AttrDefault *attrdef = NULL; + TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr)); + char *attname; + char *typename; + int attdim; + int ndef = 0; + bool attisset; /* ---------------- - * for each entry in the list, get the name and type - * information from the list and have TupleDescInitEntry - * fill in the attribute information we need. + * allocate a new tuple descriptor * ---------------- - */ - attnum++; - - entry = lfirst(p); - attname = entry->colname; - arry = entry->typename->arrayBounds; - attisset = entry->typename->setof; - - strNcpy(typename, entry->typename->name,NAMEDATALEN-1); - if (arry != NIL) - attdim = length(arry); - else - attdim = 0; - - if (! TupleDescInitEntry(desc, attnum, attname, - typename, attdim, attisset)) { - /* ---------------- - * if TupleDescInitEntry() fails, it means there is - * no type in the system catalogs. So now we check if - * the type name equals the relation name. If so we - * have a self reference, otherwise it's an error. - * ---------------- - */ - if (!strcmp(typename, relname)) { - TupleDescMakeSelfReference(desc, attnum, relname); - } else - elog(WARN, "DefineRelation: no such type %s", - typename); - } - - /* - * this is for char() and varchar(). When an entry is of type - * char() or varchar(), typlen is set to the appropriate length, - * which we'll use here instead. (The catalog lookup only returns - * the length of bpchar and varchar which is not what we want!) - * - ay 6/95 */ - if (entry->typename->typlen > 0) { - desc->attrs[attnum - 1]->attlen = entry->typename->typlen; - } + natts = length(schema); + desc = CreateTemplateTupleDesc(natts); + constr->has_not_null = false; - /* This is for constraints */ - if (entry->is_not_null) - constr->has_not_null = true; - desc->attrs[attnum-1]->attnotnull = entry->is_not_null; - - if ( entry->defval != NULL ) + attnum = 0; + + typename = palloc(NAMEDATALEN); + + foreach(p, schema) { - if ( attrdef == NULL ) - attrdef = (AttrDefault*) palloc (natts * sizeof (AttrDefault)); - attrdef[ndef].adnum = attnum; - attrdef[ndef].adbin = NULL; - attrdef[ndef].adsrc = entry->defval; - ndef++; - desc->attrs[attnum-1]->atthasdef = true; + ColumnDef *entry; + List *arry; + + /* ---------------- + * for each entry in the list, get the name and type + * information from the list and have TupleDescInitEntry + * fill in the attribute information we need. + * ---------------- + */ + attnum++; + + entry = lfirst(p); + attname = entry->colname; + arry = entry->typename->arrayBounds; + attisset = entry->typename->setof; + + strNcpy(typename, entry->typename->name, NAMEDATALEN - 1); + if (arry != NIL) + attdim = length(arry); + else + attdim = 0; + + if (!TupleDescInitEntry(desc, attnum, attname, + typename, attdim, attisset)) + { + /* ---------------- + * if TupleDescInitEntry() fails, it means there is + * no type in the system catalogs. So now we check if + * the type name equals the relation name. If so we + * have a self reference, otherwise it's an error. + * ---------------- + */ + if (!strcmp(typename, relname)) + { + TupleDescMakeSelfReference(desc, attnum, relname); + } + else + elog(WARN, "DefineRelation: no such type %s", + typename); + } + + /* + * this is for char() and varchar(). When an entry is of type + * char() or varchar(), typlen is set to the appropriate length, + * which we'll use here instead. (The catalog lookup only returns + * the length of bpchar and varchar which is not what we want!) - + * ay 6/95 + */ + if (entry->typename->typlen > 0) + { + desc->attrs[attnum - 1]->attlen = entry->typename->typlen; + } + + /* This is for constraints */ + if (entry->is_not_null) + constr->has_not_null = true; + desc->attrs[attnum - 1]->attnotnull = entry->is_not_null; + + if (entry->defval != NULL) + { + if (attrdef == NULL) + attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault)); + attrdef[ndef].adnum = attnum; + attrdef[ndef].adbin = NULL; + attrdef[ndef].adsrc = entry->defval; + ndef++; + desc->attrs[attnum - 1]->atthasdef = true; + } + } + if (constr->has_not_null || ndef > 0) + { + desc->constr = constr; - } - if ( constr->has_not_null || ndef > 0 ) - { - desc->constr = constr; - - if ( ndef > 0 ) /* DEFAULTs */ - { - if ( ndef < natts ) - constr->defval = (AttrDefault*) - repalloc (attrdef, ndef * sizeof (AttrDefault)); - else - constr->defval = attrdef; - constr->num_defval = ndef; - } - else - constr->num_defval = 0; - constr->num_check = 0; - } - else - { - pfree (constr); - desc->constr = NULL; - } - return desc; + if (ndef > 0) /* DEFAULTs */ + { + if (ndef < natts) + constr->defval = (AttrDefault *) + repalloc(attrdef, ndef * sizeof(AttrDefault)); + else + constr->defval = attrdef; + constr->num_defval = ndef; + } + else + constr->num_defval = 0; + constr->num_check = 0; + } + else + { + pfree(constr); + desc->constr = NULL; + } + return desc; } - diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 1d36f340ed6..598f9ed8f02 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * gist.c-- - * interface routines for the postgres GiST index access method. + * interface routines for the postgres GiST index access method. * * * @@ -26,308 +26,345 @@ #include <utils/syscache.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* non-export function prototypes */ -static InsertIndexResult gistdoinsert(Relation r, IndexTuple itup, - GISTSTATE *GISTstate); -static InsertIndexResult gistentryinsert(Relation r, GISTSTACK *stk, - IndexTuple tup, - GISTSTATE *giststate); -static void gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup, - IndexTuple rtup, GISTSTATE *giststate); -static void gistAdjustKeys(Relation r, GISTSTACK *stk, BlockNumber blk, - char *datum, int att_size, GISTSTATE *giststate); -static void gistintinsert(Relation r, GISTSTACK *stk, IndexTuple ltup, - IndexTuple rtup, GISTSTATE *giststate); -static InsertIndexResult gistSplit(Relation r, Buffer buffer, - GISTSTACK *stack, IndexTuple itup, - GISTSTATE *giststate); -static void gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt, +static InsertIndexResult +gistdoinsert(Relation r, IndexTuple itup, + GISTSTATE * GISTstate); +static InsertIndexResult +gistentryinsert(Relation r, GISTSTACK * stk, + IndexTuple tup, + GISTSTATE * giststate); +static void +gistentryinserttwo(Relation r, GISTSTACK * stk, IndexTuple ltup, + IndexTuple rtup, GISTSTATE * giststate); +static void +gistAdjustKeys(Relation r, GISTSTACK * stk, BlockNumber blk, + char *datum, int att_size, GISTSTATE * giststate); +static void +gistintinsert(Relation r, GISTSTACK * stk, IndexTuple ltup, + IndexTuple rtup, GISTSTATE * giststate); +static InsertIndexResult +gistSplit(Relation r, Buffer buffer, + GISTSTACK * stack, IndexTuple itup, + GISTSTATE * giststate); +static void +gistnewroot(GISTSTATE * giststate, Relation r, IndexTuple lt, IndexTuple rt); -static void GISTInitBuffer(Buffer b, uint32 f); -static BlockNumber gistChooseSubtree(Relation r, IndexTuple itup, int level, - GISTSTATE *giststate, - GISTSTACK **retstack, Buffer *leafbuf); -static OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, - GISTSTATE *giststate); -static int gistnospace(Page p, IndexTuple it); -void gistdelete(Relation r, ItemPointer tid); +static void GISTInitBuffer(Buffer b, uint32 f); +static BlockNumber +gistChooseSubtree(Relation r, IndexTuple itup, int level, + GISTSTATE * giststate, + GISTSTACK ** retstack, Buffer * leafbuf); +static OffsetNumber +gistchoose(Relation r, Page p, IndexTuple it, + GISTSTATE * giststate); +static int gistnospace(Page p, IndexTuple it); +void gistdelete(Relation r, ItemPointer tid); static IndexTuple gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t); -static void gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, - Relation r, Page pg, OffsetNumber o, int b, bool l) ; -static char *int_range_out(INTRANGE *r); +static void +gistcentryinit(GISTSTATE * giststate, GISTENTRY * e, char *pr, + Relation r, Page pg, OffsetNumber o, int b, bool l); +static char *int_range_out(INTRANGE * r); /* ** routine to build an index. Basically calls insert over and over */ void gistbuild(Relation heap, - Relation index, - int natts, - AttrNumber *attnum, - IndexStrategy istrat, - uint16 pint, - Datum *params, - FuncIndexInfo *finfo, - PredInfo *predInfo) + Relation index, + int natts, + AttrNumber * attnum, + IndexStrategy istrat, + uint16 pint, + Datum * params, + FuncIndexInfo * finfo, + PredInfo * predInfo) { - HeapScanDesc scan; - Buffer buffer; - AttrNumber i; - HeapTuple htup; - IndexTuple itup; - TupleDesc hd, id; - InsertIndexResult res; - Datum *d; - bool *nulls; - int nb, nh, ni; + HeapScanDesc scan; + Buffer buffer; + AttrNumber i; + HeapTuple htup; + IndexTuple itup; + TupleDesc hd, + id; + InsertIndexResult res; + Datum *d; + bool *nulls; + int nb, + nh, + ni; + #ifndef OMIT_PARTIAL_INDEX - ExprContext *econtext; - TupleTable tupleTable; - TupleTableSlot *slot; + ExprContext *econtext; + TupleTable tupleTable; + TupleTableSlot *slot; + #endif - Oid hrelid, irelid; - Node *pred, *oldPred; - GISTSTATE giststate; - GISTENTRY tmpcentry; - bool *compvec; - - /* GiSTs only know how to do stupid locking now */ - RelationSetLockForWrite(index); - - setheapoverride(TRUE); /* so we can see the new pg_index tuple */ - initGISTstate(&giststate, index); - setheapoverride(FALSE); - - pred = predInfo->pred; - oldPred = predInfo->oldPred; - - /* - * We expect to be called exactly once for any index relation. - * If that's not the case, big trouble's what we have. - */ - - if (oldPred == NULL && (nb = RelationGetNumberOfBlocks(index)) != 0) - elog(WARN, "%.16s already contains data", &(index->rd_rel->relname.data[0])); - - /* initialize the root page (if this is a new index) */ - if (oldPred == NULL) { - buffer = ReadBuffer(index, P_NEW); - GISTInitBuffer(buffer, F_LEAF); - WriteBuffer(buffer); - } - - /* init the tuple descriptors and get set for a heap scan */ - hd = RelationGetTupleDescriptor(heap); - id = RelationGetTupleDescriptor(index); - d = (Datum *)palloc(natts * sizeof (*d)); - nulls = (bool *)palloc(natts * sizeof (*nulls)); - - /* - * If this is a predicate (partial) index, we will need to evaluate the - * predicate using ExecQual, which requires the current tuple to be in a - * slot of a TupleTable. In addition, ExecQual must have an ExprContext - * referring to that slot. Here, we initialize dummy TupleTable and - * ExprContext objects for this purpose. --Nels, Feb '92 - */ + Oid hrelid, + irelid; + Node *pred, + *oldPred; + GISTSTATE giststate; + GISTENTRY tmpcentry; + bool *compvec; + + /* GiSTs only know how to do stupid locking now */ + RelationSetLockForWrite(index); + + setheapoverride(TRUE); /* so we can see the new pg_index tuple */ + initGISTstate(&giststate, index); + setheapoverride(FALSE); + + pred = predInfo->pred; + oldPred = predInfo->oldPred; + + /* + * We expect to be called exactly once for any index relation. If + * that's not the case, big trouble's what we have. + */ + + if (oldPred == NULL && (nb = RelationGetNumberOfBlocks(index)) != 0) + elog(WARN, "%.16s already contains data", &(index->rd_rel->relname.data[0])); + + /* initialize the root page (if this is a new index) */ + if (oldPred == NULL) + { + buffer = ReadBuffer(index, P_NEW); + GISTInitBuffer(buffer, F_LEAF); + WriteBuffer(buffer); + } + + /* init the tuple descriptors and get set for a heap scan */ + hd = RelationGetTupleDescriptor(heap); + id = RelationGetTupleDescriptor(index); + d = (Datum *) palloc(natts * sizeof(*d)); + nulls = (bool *) palloc(natts * sizeof(*nulls)); + + /* + * If this is a predicate (partial) index, we will need to evaluate + * the predicate using ExecQual, which requires the current tuple to + * be in a slot of a TupleTable. In addition, ExecQual must have an + * ExprContext referring to that slot. Here, we initialize dummy + * TupleTable and ExprContext objects for this purpose. --Nels, Feb + * '92 + */ #ifndef OMIT_PARTIAL_INDEX - if (pred != NULL || oldPred != NULL) { - tupleTable = ExecCreateTupleTable(1); - slot = ExecAllocTableSlot(tupleTable); - econtext = makeNode(ExprContext); - FillDummyExprContext(econtext, slot, hd, buffer); - } - else /* shut the compiler up */ + if (pred != NULL || oldPred != NULL) + { + tupleTable = ExecCreateTupleTable(1); + slot = ExecAllocTableSlot(tupleTable); + econtext = makeNode(ExprContext); + FillDummyExprContext(econtext, slot, hd, buffer); + } + else +/* shut the compiler up */ { tupleTable = NULL; slot = NULL; econtext = NULL; } -#endif /* OMIT_PARTIAL_INDEX */ - scan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL); - htup = heap_getnext(scan, 0, &buffer); - - /* int the tuples as we insert them */ - nh = ni = 0; - - for (; HeapTupleIsValid(htup); htup = heap_getnext(scan, 0, &buffer)) { - - nh++; - - /* - * If oldPred != NULL, this is an EXTEND INDEX command, so skip - * this tuple if it was already in the existing partial index - */ - if (oldPred != NULL) { +#endif /* OMIT_PARTIAL_INDEX */ + scan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL); + htup = heap_getnext(scan, 0, &buffer); + + /* int the tuples as we insert them */ + nh = ni = 0; + + for (; HeapTupleIsValid(htup); htup = heap_getnext(scan, 0, &buffer)) + { + + nh++; + + /* + * If oldPred != NULL, this is an EXTEND INDEX command, so skip + * this tuple if it was already in the existing partial index + */ + if (oldPred != NULL) + { #ifndef OMIT_PARTIAL_INDEX - /*SetSlotContents(slot, htup); */ - slot->val = htup; - if (ExecQual((List*)oldPred, econtext) == true) { + /* SetSlotContents(slot, htup); */ + slot->val = htup; + if (ExecQual((List *) oldPred, econtext) == true) + { + ni++; + continue; + } +#endif /* OMIT_PARTIAL_INDEX */ + } + + /* + * Skip this tuple if it doesn't satisfy the partial-index + * predicate + */ + if (pred != NULL) + { +#ifndef OMIT_PARTIAL_INDEX + /* SetSlotContents(slot, htup); */ + slot->val = htup; + if (ExecQual((List *) pred, econtext) == false) + continue; +#endif /* OMIT_PARTIAL_INDEX */ + } + ni++; - continue; - } -#endif /* OMIT_PARTIAL_INDEX */ + + /* + * For the current heap tuple, extract all the attributes we use + * in this index, and note which are null. + */ + + for (i = 1; i <= natts; i++) + { + int attoff; + bool attnull; + + /* + * Offsets are from the start of the tuple, and are + * zero-based; indices are one-based. The next call returns i + * - 1. That's data hiding for you. + */ + + attoff = AttrNumberGetAttrOffset(i); + + /* + * d[attoff] = HeapTupleGetAttributeValue(htup, buffer, + */ + d[attoff] = GetIndexValue(htup, + hd, + attoff, + attnum, + finfo, + &attnull, + buffer); + nulls[attoff] = (attnull ? 'n' : ' '); + } + + /* immediately compress keys to normalize */ + compvec = (bool *) palloc(sizeof(bool) * natts); + for (i = 0; i < natts; i++) + { + gistcentryinit(&giststate, &tmpcentry, (char *) d[i], + (Relation) NULL, (Page) NULL, (OffsetNumber) 0, + -1 /* size is currently bogus */ , TRUE); + if (d[i] != (Datum) tmpcentry.pred && !(giststate.keytypbyval)) + compvec[i] = TRUE; + else + compvec[i] = FALSE; + d[i] = (Datum) tmpcentry.pred; + } + + /* form an index tuple and point it at the heap tuple */ + itup = index_formtuple(id, &d[0], nulls); + itup->t_tid = htup->t_ctid; + + /* + * Since we already have the index relation locked, we call + * gistdoinsert directly. Normal access method calls dispatch + * through gistinsert, which locks the relation for write. This + * is the right thing to do if you're inserting single tups, but + * not when you're initializing the whole index at once. + */ + + res = gistdoinsert(index, itup, &giststate); + for (i = 0; i < natts; i++) + if (compvec[i] == TRUE) + pfree((char *) d[i]); + pfree(itup); + pfree(res); + pfree(compvec); } - - /* Skip this tuple if it doesn't satisfy the partial-index predicate */ - if (pred != NULL) { + + /* okay, all heap tuples are indexed */ + heap_endscan(scan); + RelationUnsetLockForWrite(index); + + if (pred != NULL || oldPred != NULL) + { #ifndef OMIT_PARTIAL_INDEX - /*SetSlotContents(slot, htup); */ - slot->val = htup; - if (ExecQual((List*)pred, econtext) == false) - continue; -#endif /* OMIT_PARTIAL_INDEX */ + ExecDestroyTupleTable(tupleTable, true); + pfree(econtext); +#endif /* OMIT_PARTIAL_INDEX */ } - - ni++; - + /* - * For the current heap tuple, extract all the attributes - * we use in this index, and note which are null. + * Since we just inted the tuples in the heap, we update its stats in + * pg_relation to guarantee that the planner takes advantage of the + * index we just created. UpdateStats() does a + * CommandinterIncrement(), which flushes changed entries from the + * system relcache. The act of constructing an index changes these + * heap and index tuples in the system catalogs, so they need to be + * flushed. We close them to guarantee that they will be. */ - - for (i = 1; i <= natts; i++) { - int attoff; - bool attnull; - - /* - * Offsets are from the start of the tuple, and are - * zero-based; indices are one-based. The next call - * returns i - 1. That's data hiding for you. - */ - - attoff = AttrNumberGetAttrOffset(i); - /* - d[attoff] = HeapTupleGetAttributeValue(htup, buffer, - */ - d[attoff] = GetIndexValue(htup, - hd, - attoff, - attnum, - finfo, - &attnull, - buffer); - nulls[attoff] = (attnull ? 'n' : ' '); - } - - /* immediately compress keys to normalize */ - compvec = (bool *)palloc(sizeof(bool) * natts); - for (i = 0; i < natts; i++) { - gistcentryinit(&giststate, &tmpcentry, (char *)d[i], - (Relation) NULL, (Page) NULL, (OffsetNumber) 0, - -1 /* size is currently bogus */, TRUE); - if (d[i] != (Datum)tmpcentry.pred && !(giststate.keytypbyval)) - compvec[i] = TRUE; - else compvec[i] = FALSE; - d[i] = (Datum)tmpcentry.pred; + + hrelid = heap->rd_id; + irelid = index->rd_id; + heap_close(heap); + index_close(index); + + UpdateStats(hrelid, nh, true); + UpdateStats(irelid, ni, false); + + if (oldPred != NULL) + { + if (ni == nh) + pred = NULL; + UpdateIndexPredicate(irelid, oldPred, pred); } - /* form an index tuple and point it at the heap tuple */ - itup = index_formtuple(id, &d[0], nulls); - itup->t_tid = htup->t_ctid; - - /* - * Since we already have the index relation locked, we - * call gistdoinsert directly. Normal access method calls - * dispatch through gistinsert, which locks the relation - * for write. This is the right thing to do if you're - * inserting single tups, but not when you're initializing - * the whole index at once. - */ - - res = gistdoinsert(index, itup, &giststate); - for (i = 0; i < natts; i++) - if (compvec[i] == TRUE) pfree((char *)d[i]); - pfree(itup); - pfree(res); - pfree(compvec); - } - - /* okay, all heap tuples are indexed */ - heap_endscan(scan); - RelationUnsetLockForWrite(index); - - if (pred != NULL || oldPred != NULL) { -#ifndef OMIT_PARTIAL_INDEX - ExecDestroyTupleTable(tupleTable, true); - pfree(econtext); -#endif /* OMIT_PARTIAL_INDEX */ - } - - /* - * Since we just inted the tuples in the heap, we update its - * stats in pg_relation to guarantee that the planner takes - * advantage of the index we just created. UpdateStats() does a - * CommandinterIncrement(), which flushes changed entries from - * the system relcache. The act of constructing an index changes - * these heap and index tuples in the system catalogs, so they - * need to be flushed. We close them to guarantee that they - * will be. - */ - - hrelid = heap->rd_id; - irelid = index->rd_id; - heap_close(heap); - index_close(index); - - UpdateStats(hrelid, nh, true); - UpdateStats(irelid, ni, false); - - if (oldPred != NULL) { - if (ni == nh) pred = NULL; - UpdateIndexPredicate(irelid, oldPred, pred); - } - - /* be tidy */ - pfree(nulls); - pfree(d); + /* be tidy */ + pfree(nulls); + pfree(d); } /* - * gistinsert -- wrapper for GiST tuple insertion. + * gistinsert -- wrapper for GiST tuple insertion. * - * This is the public interface routine for tuple insertion in GiSTs. - * It doesn't do any work; just locks the relation and passes the buck. + * This is the public interface routine for tuple insertion in GiSTs. + * It doesn't do any work; just locks the relation and passes the buck. */ InsertIndexResult -gistinsert(Relation r, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) +gistinsert(Relation r, Datum * datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) { - InsertIndexResult res; - IndexTuple itup; - GISTSTATE giststate; - GISTENTRY tmpentry; - int i; - bool *compvec; - - initGISTstate(&giststate, r); - - /* immediately compress keys to normalize */ - compvec = (bool *)palloc(sizeof(bool) * r->rd_att->natts); - for (i = 0; i < r->rd_att->natts; i++) { - gistcentryinit(&giststate, &tmpentry, (char *)datum[i], - (Relation) NULL, (Page) NULL, (OffsetNumber) 0, - -1 /* size is currently bogus */, TRUE); - if (datum[i] != (Datum)tmpentry.pred && !(giststate.keytypbyval)) - compvec[i] = TRUE; - else compvec[i] = FALSE; - datum[i] = (Datum)tmpentry.pred; - } - itup = index_formtuple(RelationGetTupleDescriptor(r), datum, nulls); - itup->t_tid = *ht_ctid; - - RelationSetLockForWrite(r); - res = gistdoinsert(r, itup, &giststate); - for (i = 0; i < r->rd_att->natts; i++) - if (compvec[i] == TRUE) pfree((char *)datum[i]); - pfree(itup); - pfree(compvec); - - /* XXX two-phase locking -- don't unlock the relation until EOT */ - return (res); + InsertIndexResult res; + IndexTuple itup; + GISTSTATE giststate; + GISTENTRY tmpentry; + int i; + bool *compvec; + + initGISTstate(&giststate, r); + + /* immediately compress keys to normalize */ + compvec = (bool *) palloc(sizeof(bool) * r->rd_att->natts); + for (i = 0; i < r->rd_att->natts; i++) + { + gistcentryinit(&giststate, &tmpentry, (char *) datum[i], + (Relation) NULL, (Page) NULL, (OffsetNumber) 0, + -1 /* size is currently bogus */ , TRUE); + if (datum[i] != (Datum) tmpentry.pred && !(giststate.keytypbyval)) + compvec[i] = TRUE; + else + compvec[i] = FALSE; + datum[i] = (Datum) tmpentry.pred; + } + itup = index_formtuple(RelationGetTupleDescriptor(r), datum, nulls); + itup->t_tid = *ht_ctid; + + RelationSetLockForWrite(r); + res = gistdoinsert(r, itup, &giststate); + for (i = 0; i < r->rd_att->natts; i++) + if (compvec[i] == TRUE) + pfree((char *) datum[i]); + pfree(itup); + pfree(compvec); + + /* XXX two-phase locking -- don't unlock the relation until EOT */ + return (res); } /* @@ -336,475 +373,509 @@ gistinsert(Relation r, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation ** that knowledge (some compression routines may want to fish around ** on the page, for example, or do something special for leaf nodes.) */ -static OffsetNumber -gistPageAddItem(GISTSTATE *giststate, - Relation r, - Page page, - Item item, - Size size, - OffsetNumber offsetNumber, - ItemIdFlags flags, - GISTENTRY *dentry, - IndexTuple *newtup) +static OffsetNumber +gistPageAddItem(GISTSTATE * giststate, + Relation r, + Page page, + Item item, + Size size, + OffsetNumber offsetNumber, + ItemIdFlags flags, + GISTENTRY * dentry, + IndexTuple * newtup) { - GISTENTRY tmpcentry; - IndexTuple itup = (IndexTuple)item; - - /* recompress the item given that we now know the exact page and - offset for insertion */ - gistdentryinit(giststate, dentry, - (((char *) itup) + sizeof(IndexTupleData)), - (Relation)0, (Page)0, (OffsetNumber)InvalidOffsetNumber, - IndexTupleSize(itup) - sizeof(IndexTupleData), FALSE); - gistcentryinit(giststate, &tmpcentry, dentry->pred, r, page, - offsetNumber, dentry->bytes, FALSE); - *newtup = gist_tuple_replacekey(r, *dentry, itup); - /* be tidy */ - if (tmpcentry.pred != dentry->pred - && tmpcentry.pred != (((char *) itup) + sizeof(IndexTupleData))) - pfree(tmpcentry.pred); - - return(PageAddItem(page, (Item) *newtup, IndexTupleSize(*newtup), - offsetNumber, flags)); + GISTENTRY tmpcentry; + IndexTuple itup = (IndexTuple) item; + + /* + * recompress the item given that we now know the exact page and + * offset for insertion + */ + gistdentryinit(giststate, dentry, + (((char *) itup) + sizeof(IndexTupleData)), + (Relation) 0, (Page) 0, (OffsetNumber) InvalidOffsetNumber, + IndexTupleSize(itup) - sizeof(IndexTupleData), FALSE); + gistcentryinit(giststate, &tmpcentry, dentry->pred, r, page, + offsetNumber, dentry->bytes, FALSE); + *newtup = gist_tuple_replacekey(r, *dentry, itup); + /* be tidy */ + if (tmpcentry.pred != dentry->pred + && tmpcentry.pred != (((char *) itup) + sizeof(IndexTupleData))) + pfree(tmpcentry.pred); + + return (PageAddItem(page, (Item) * newtup, IndexTupleSize(*newtup), + offsetNumber, flags)); } -static InsertIndexResult -gistdoinsert(Relation r, - IndexTuple itup, /* itup contains compressed entry */ - GISTSTATE *giststate) +static InsertIndexResult +gistdoinsert(Relation r, + IndexTuple itup, /* itup contains compressed entry */ + GISTSTATE * giststate) { - GISTENTRY tmpdentry; - InsertIndexResult res; - OffsetNumber l; - GISTSTACK *stack; - Buffer buffer; - BlockNumber blk; - Page page; - OffsetNumber off; - IndexTuple newtup; - - /* 3rd arg is ignored for now */ - blk = gistChooseSubtree(r, itup, 0, giststate, &stack, &buffer); - page = (Page) BufferGetPage(buffer); - - if (gistnospace(page, itup)) { - /* need to do a split */ - res = gistSplit(r, buffer, stack, itup, giststate); + GISTENTRY tmpdentry; + InsertIndexResult res; + OffsetNumber l; + GISTSTACK *stack; + Buffer buffer; + BlockNumber blk; + Page page; + OffsetNumber off; + IndexTuple newtup; + + /* 3rd arg is ignored for now */ + blk = gistChooseSubtree(r, itup, 0, giststate, &stack, &buffer); + page = (Page) BufferGetPage(buffer); + + if (gistnospace(page, itup)) + { + /* need to do a split */ + res = gistSplit(r, buffer, stack, itup, giststate); + gistfreestack(stack); + WriteBuffer(buffer); /* don't forget to release buffer! */ + return (res); + } + + if (PageIsEmpty(page)) + off = FirstOffsetNumber; + else + off = OffsetNumberNext(PageGetMaxOffsetNumber(page)); + + /* add the item and write the buffer */ + l = gistPageAddItem(giststate, r, page, (Item) itup, IndexTupleSize(itup), + off, LP_USED, &tmpdentry, &newtup); + WriteBuffer(buffer); + + /* now expand the page boundary in the parent to include the new child */ + gistAdjustKeys(r, stack, blk, tmpdentry.pred, tmpdentry.bytes, giststate); gistfreestack(stack); - WriteBuffer(buffer); /* don't forget to release buffer! */ + + /* be tidy */ + if (itup != newtup) + pfree(newtup); + if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) + pfree(tmpdentry.pred); + + /* build and return an InsertIndexResult for this insertion */ + res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); + ItemPointerSet(&(res->pointerData), blk, l); + return (res); - } - - if (PageIsEmpty(page)) - off = FirstOffsetNumber; - else - off = OffsetNumberNext(PageGetMaxOffsetNumber(page)); - - /* add the item and write the buffer */ - l = gistPageAddItem(giststate, r, page, (Item) itup, IndexTupleSize(itup), - off, LP_USED, &tmpdentry, &newtup); - WriteBuffer(buffer); - - /* now expand the page boundary in the parent to include the new child */ - gistAdjustKeys(r, stack, blk, tmpdentry.pred, tmpdentry.bytes, giststate); - gistfreestack(stack); - - /* be tidy */ - if (itup != newtup) - pfree(newtup); - if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) - pfree(tmpdentry.pred); - - /* build and return an InsertIndexResult for this insertion */ - res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); - ItemPointerSet(&(res->pointerData), blk, l); - - return (res); } -static BlockNumber -gistChooseSubtree(Relation r, IndexTuple itup, /* itup has compressed entry */ - int level, - GISTSTATE *giststate, - GISTSTACK **retstack /*out*/, - Buffer *leafbuf /*out*/) +static BlockNumber +gistChooseSubtree(Relation r, IndexTuple itup, /* itup has compressed + * entry */ + int level, + GISTSTATE * giststate, + GISTSTACK ** retstack /* out */ , + Buffer * leafbuf /* out */ ) { - Buffer buffer; - BlockNumber blk; - GISTSTACK *stack; - Page page; - GISTPageOpaque opaque; - IndexTuple which; - - blk = GISTP_ROOT; - buffer = InvalidBuffer; - stack = (GISTSTACK *) NULL; - - do { - /* let go of current buffer before getting next */ - if (buffer != InvalidBuffer) - ReleaseBuffer(buffer); - - /* get next buffer */ - buffer = ReadBuffer(r, blk); - page = (Page) BufferGetPage(buffer); - - opaque = (GISTPageOpaque) PageGetSpecialPointer(page); - if (!(opaque->flags & F_LEAF)) { - GISTSTACK *n; - ItemId iid; - - n = (GISTSTACK *) palloc(sizeof(GISTSTACK)); - n->gs_parent = stack; - n->gs_blk = blk; - n->gs_child = gistchoose(r, page, itup, giststate); - stack = n; - - iid = PageGetItemId(page, n->gs_child); - which = (IndexTuple) PageGetItem(page, iid); - blk = ItemPointerGetBlockNumber(&(which->t_tid)); - } - } while (!(opaque->flags & F_LEAF)); - - *retstack = stack; - *leafbuf = buffer; - - return(blk); + Buffer buffer; + BlockNumber blk; + GISTSTACK *stack; + Page page; + GISTPageOpaque opaque; + IndexTuple which; + + blk = GISTP_ROOT; + buffer = InvalidBuffer; + stack = (GISTSTACK *) NULL; + + do + { + /* let go of current buffer before getting next */ + if (buffer != InvalidBuffer) + ReleaseBuffer(buffer); + + /* get next buffer */ + buffer = ReadBuffer(r, blk); + page = (Page) BufferGetPage(buffer); + + opaque = (GISTPageOpaque) PageGetSpecialPointer(page); + if (!(opaque->flags & F_LEAF)) + { + GISTSTACK *n; + ItemId iid; + + n = (GISTSTACK *) palloc(sizeof(GISTSTACK)); + n->gs_parent = stack; + n->gs_blk = blk; + n->gs_child = gistchoose(r, page, itup, giststate); + stack = n; + + iid = PageGetItemId(page, n->gs_child); + which = (IndexTuple) PageGetItem(page, iid); + blk = ItemPointerGetBlockNumber(&(which->t_tid)); + } + } while (!(opaque->flags & F_LEAF)); + + *retstack = stack; + *leafbuf = buffer; + + return (blk); } static void gistAdjustKeys(Relation r, - GISTSTACK *stk, - BlockNumber blk, - char *datum, /* datum is uncompressed */ - int att_size, - GISTSTATE *giststate) + GISTSTACK * stk, + BlockNumber blk, + char *datum, /* datum is uncompressed */ + int att_size, + GISTSTATE * giststate) { - char *oldud; - Page p; - Buffer b; - bool result; - bytea *evec; - GISTENTRY centry, *ev0p, *ev1p; - int size, datumsize; - IndexTuple tid; - - if (stk == (GISTSTACK *) NULL) - return; - - b = ReadBuffer(r, stk->gs_blk); - p = BufferGetPage(b); - - oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->gs_child)); - tid = (IndexTuple) oldud; - size = IndexTupleSize((IndexTuple)oldud) - sizeof(IndexTupleData); - oldud += sizeof(IndexTupleData); - - evec = (bytea *) palloc(2*sizeof(GISTENTRY) + VARHDRSZ); - VARSIZE(evec) = 2*sizeof(GISTENTRY) + VARHDRSZ; - - /* insert decompressed oldud into entry vector */ - gistdentryinit(giststate, &((GISTENTRY *)VARDATA(evec))[0], - oldud, r, p, stk->gs_child, - size, FALSE); - ev0p = &((GISTENTRY *)VARDATA(evec))[0]; - - /* insert datum entry into entry vector */ - gistentryinit(((GISTENTRY *)VARDATA(evec))[1], datum, - (Relation)NULL,(Page)NULL,(OffsetNumber)0, att_size, FALSE); - ev1p = &((GISTENTRY *)VARDATA(evec))[1]; - - /* form union of decompressed entries */ - datum = (char *) (giststate->unionFn)(evec, &datumsize); - - /* did union leave decompressed version of oldud unchanged? */ - (giststate->equalFn)(ev0p->pred, datum, &result); - if (!result) { - TupleDesc td = RelationGetTupleDescriptor(r); - - /* compress datum for storage on page */ - gistcentryinit(giststate, ¢ry, datum, ev0p->rel, ev0p->page, - ev0p->offset, datumsize, FALSE); - if (td->attrs[0]->attlen >= 0) { - memmove(oldud, centry.pred, att_size); - gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size, - giststate); + char *oldud; + Page p; + Buffer b; + bool result; + bytea *evec; + GISTENTRY centry, + *ev0p, + *ev1p; + int size, + datumsize; + IndexTuple tid; + + if (stk == (GISTSTACK *) NULL) + return; + + b = ReadBuffer(r, stk->gs_blk); + p = BufferGetPage(b); + + oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->gs_child)); + tid = (IndexTuple) oldud; + size = IndexTupleSize((IndexTuple) oldud) - sizeof(IndexTupleData); + oldud += sizeof(IndexTupleData); + + evec = (bytea *) palloc(2 * sizeof(GISTENTRY) + VARHDRSZ); + VARSIZE(evec) = 2 * sizeof(GISTENTRY) + VARHDRSZ; + + /* insert decompressed oldud into entry vector */ + gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[0], + oldud, r, p, stk->gs_child, + size, FALSE); + ev0p = &((GISTENTRY *) VARDATA(evec))[0]; + + /* insert datum entry into entry vector */ + gistentryinit(((GISTENTRY *) VARDATA(evec))[1], datum, + (Relation) NULL, (Page) NULL, (OffsetNumber) 0, att_size, FALSE); + ev1p = &((GISTENTRY *) VARDATA(evec))[1]; + + /* form union of decompressed entries */ + datum = (char *) (giststate->unionFn) (evec, &datumsize); + + /* did union leave decompressed version of oldud unchanged? */ + (giststate->equalFn) (ev0p->pred, datum, &result); + if (!result) + { + TupleDesc td = RelationGetTupleDescriptor(r); + + /* compress datum for storage on page */ + gistcentryinit(giststate, ¢ry, datum, ev0p->rel, ev0p->page, + ev0p->offset, datumsize, FALSE); + if (td->attrs[0]->attlen >= 0) + { + memmove(oldud, centry.pred, att_size); + gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size, + giststate); + } + else if (VARSIZE(centry.pred) == VARSIZE(oldud)) + { + memmove(oldud, centry.pred, VARSIZE(centry.pred)); + gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size, + giststate); + } + else + { + + /* + * * new datum is not the same size as the old. * We have to + * delete the old entry and insert the new * one. Note that + * this may cause a split here! + */ + IndexTuple newtup; + ItemPointerData oldtid; + char *isnull; + TupleDesc tupDesc; + InsertIndexResult res; + + /* delete old tuple */ + ItemPointerSet(&oldtid, stk->gs_blk, stk->gs_child); + gistdelete(r, (ItemPointer) & oldtid); + + /* generate and insert new tuple */ + tupDesc = r->rd_att; + isnull = (char *) palloc(r->rd_rel->relnatts); + memset(isnull, ' ', r->rd_rel->relnatts); + newtup = (IndexTuple) index_formtuple(tupDesc, + (Datum *) & centry.pred, isnull); + pfree(isnull); + /* set pointer in new tuple to point to current child */ + ItemPointerSet(&oldtid, blk, 1); + newtup->t_tid = oldtid; + + /* inserting the new entry also adjust keys above */ + res = gistentryinsert(r, stk, newtup, giststate); + + /* in stack, set info to point to new tuple */ + stk->gs_blk = ItemPointerGetBlockNumber(&(res->pointerData)); + stk->gs_child = ItemPointerGetOffsetNumber(&(res->pointerData)); + + pfree(res); + } + WriteBuffer(b); + + if (centry.pred != datum) + pfree(datum); } - else if (VARSIZE(centry.pred) == VARSIZE(oldud)) { - memmove(oldud, centry.pred, VARSIZE(centry.pred)); - gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size, - giststate); + else + { + ReleaseBuffer(b); } - else { - /* - ** new datum is not the same size as the old. - ** We have to delete the old entry and insert the new - ** one. Note that this may cause a split here! - */ - IndexTuple newtup; - ItemPointerData oldtid; - char *isnull; - TupleDesc tupDesc; - InsertIndexResult res; - - /* delete old tuple */ - ItemPointerSet(&oldtid, stk->gs_blk, stk->gs_child); - gistdelete(r, (ItemPointer)&oldtid); - - /* generate and insert new tuple */ - tupDesc = r->rd_att; - isnull = (char *) palloc(r->rd_rel->relnatts); - memset(isnull, ' ', r->rd_rel->relnatts); - newtup = (IndexTuple) index_formtuple(tupDesc, - (Datum *) ¢ry.pred, isnull); - pfree(isnull); - /* set pointer in new tuple to point to current child */ - ItemPointerSet(&oldtid, blk, 1); - newtup->t_tid = oldtid; - - /* inserting the new entry also adjust keys above */ - res = gistentryinsert(r, stk, newtup, giststate); - - /* in stack, set info to point to new tuple */ - stk->gs_blk = ItemPointerGetBlockNumber(&(res->pointerData)); - stk->gs_child = ItemPointerGetOffsetNumber(&(res->pointerData)); - - pfree(res); - } - WriteBuffer(b); - - if (centry.pred != datum) - pfree(datum); - } - else { - ReleaseBuffer(b); - } - pfree(evec); + pfree(evec); } /* - * gistSplit -- split a page in the tree. + * gistSplit -- split a page in the tree. * */ -static InsertIndexResult +static InsertIndexResult gistSplit(Relation r, - Buffer buffer, - GISTSTACK *stack, - IndexTuple itup, /* contains compressed entry */ - GISTSTATE *giststate) + Buffer buffer, + GISTSTACK * stack, + IndexTuple itup, /* contains compressed entry */ + GISTSTATE * giststate) { - Page p; - Buffer leftbuf, rightbuf; - Page left, right; - ItemId itemid; - IndexTuple item; - IndexTuple ltup, rtup, newtup; - OffsetNumber maxoff; - OffsetNumber i; - OffsetNumber leftoff, rightoff; - BlockNumber lbknum, rbknum; - BlockNumber bufblock; - GISTPageOpaque opaque; - int blank; - InsertIndexResult res; - char *isnull; - GIST_SPLITVEC v; - TupleDesc tupDesc; - bytea *entryvec; - bool *decompvec; - IndexTuple item_1; - GISTENTRY tmpdentry, tmpentry; - - isnull = (char *) palloc(r->rd_rel->relnatts); - for (blank = 0; blank < r->rd_rel->relnatts; blank++) - isnull[blank] = ' '; - p = (Page) BufferGetPage(buffer); - opaque = (GISTPageOpaque) PageGetSpecialPointer(p); - - - /* - * The root of the tree is the first block in the relation. If - * we're about to split the root, we need to do some hocus-pocus - * to enforce this guarantee. - */ - - if (BufferGetBlockNumber(buffer) == GISTP_ROOT) { - leftbuf = ReadBuffer(r, P_NEW); - GISTInitBuffer(leftbuf, opaque->flags); - lbknum = BufferGetBlockNumber(leftbuf); - left = (Page) BufferGetPage(leftbuf); - } else { - leftbuf = buffer; - IncrBufferRefCount(buffer); - lbknum = BufferGetBlockNumber(buffer); - left = (Page) PageGetTempPage(p, sizeof(GISTPageOpaqueData)); - } - - rightbuf = ReadBuffer(r, P_NEW); - GISTInitBuffer(rightbuf, opaque->flags); - rbknum = BufferGetBlockNumber(rightbuf); - right = (Page) BufferGetPage(rightbuf); - - /* generate the item array */ - maxoff = PageGetMaxOffsetNumber(p); - entryvec = (bytea *)palloc(VARHDRSZ + (maxoff + 2) * sizeof(GISTENTRY)); - decompvec = (bool *)palloc(VARHDRSZ + (maxoff + 2) * sizeof(bool)); - for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { - item_1 = (IndexTuple) PageGetItem(p, PageGetItemId(p, i)); - gistdentryinit(giststate, &((GISTENTRY *)VARDATA(entryvec))[i], - (((char *) item_1) + sizeof(IndexTupleData)), - r, p, i, - IndexTupleSize(item_1) - sizeof(IndexTupleData), FALSE); - if ((char *)(((GISTENTRY *)VARDATA(entryvec))[i].pred) - == (((char *) item_1) + sizeof(IndexTupleData))) - decompvec[i] = FALSE; - else decompvec[i] = TRUE; - } - - /* add the new datum as the last entry */ - gistdentryinit(giststate, &(((GISTENTRY *)VARDATA(entryvec))[maxoff+1]), - (((char *) itup) + sizeof(IndexTupleData)), - (Relation)NULL, (Page)NULL, - (OffsetNumber)0, tmpentry.bytes, FALSE); - if ((char *)(((GISTENTRY *)VARDATA(entryvec))[maxoff+1]).pred != - (((char *) itup) + sizeof(IndexTupleData))) - decompvec[maxoff+1] = TRUE; - else decompvec[maxoff+1] = FALSE; - - VARSIZE(entryvec) = (maxoff + 2) * sizeof(GISTENTRY) + VARHDRSZ; - - /* now let the user-defined picksplit function set up the split vector */ - (giststate->picksplitFn)(entryvec, &v); - - /* compress ldatum and rdatum */ - gistcentryinit(giststate, &tmpentry, v.spl_ldatum, (Relation)NULL, - (Page)NULL, (OffsetNumber)0, - ((GISTENTRY *)VARDATA(entryvec))[i].bytes, FALSE); - if (v.spl_ldatum != tmpentry.pred) - pfree(v.spl_ldatum); - v.spl_ldatum = tmpentry.pred; - - gistcentryinit(giststate, &tmpentry, v.spl_rdatum, (Relation)NULL, - (Page)NULL, (OffsetNumber)0, - ((GISTENTRY *)VARDATA(entryvec))[i].bytes, FALSE); - if (v.spl_rdatum != tmpentry.pred) - pfree(v.spl_rdatum); - v.spl_rdatum = tmpentry.pred; - - /* clean up the entry vector: its preds need to be deleted, too */ - for (i = FirstOffsetNumber; i <= maxoff+1; i = OffsetNumberNext(i)) - if (decompvec[i]) - pfree(((GISTENTRY *)VARDATA(entryvec))[i].pred); - pfree(entryvec); - pfree(decompvec); - - leftoff = rightoff = FirstOffsetNumber; - maxoff = PageGetMaxOffsetNumber(p); - for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { - itemid = PageGetItemId(p, i); - item = (IndexTuple) PageGetItem(p, itemid); - - if (i == *(v.spl_left)) { - gistPageAddItem(giststate, r, left, (Item) item, - IndexTupleSize(item), - leftoff, LP_USED, &tmpdentry, &newtup); - leftoff = OffsetNumberNext(leftoff); - v.spl_left++; /* advance in left split vector */ - /* be tidy */ - if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData))) - pfree(tmpdentry.pred); - if ((IndexTuple)item != newtup) - pfree(newtup); - } - else { - gistPageAddItem(giststate, r, right, (Item) item, - IndexTupleSize(item), - rightoff, LP_USED, &tmpdentry, &newtup); - rightoff = OffsetNumberNext(rightoff); - v.spl_right++; /* advance in right split vector */ - /* be tidy */ - if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData))) - pfree(tmpdentry.pred); - if (item != newtup) - pfree(newtup); + Page p; + Buffer leftbuf, + rightbuf; + Page left, + right; + ItemId itemid; + IndexTuple item; + IndexTuple ltup, + rtup, + newtup; + OffsetNumber maxoff; + OffsetNumber i; + OffsetNumber leftoff, + rightoff; + BlockNumber lbknum, + rbknum; + BlockNumber bufblock; + GISTPageOpaque opaque; + int blank; + InsertIndexResult res; + char *isnull; + GIST_SPLITVEC v; + TupleDesc tupDesc; + bytea *entryvec; + bool *decompvec; + IndexTuple item_1; + GISTENTRY tmpdentry, + tmpentry; + + isnull = (char *) palloc(r->rd_rel->relnatts); + for (blank = 0; blank < r->rd_rel->relnatts; blank++) + isnull[blank] = ' '; + p = (Page) BufferGetPage(buffer); + opaque = (GISTPageOpaque) PageGetSpecialPointer(p); + + + /* + * The root of the tree is the first block in the relation. If we're + * about to split the root, we need to do some hocus-pocus to enforce + * this guarantee. + */ + + if (BufferGetBlockNumber(buffer) == GISTP_ROOT) + { + leftbuf = ReadBuffer(r, P_NEW); + GISTInitBuffer(leftbuf, opaque->flags); + lbknum = BufferGetBlockNumber(leftbuf); + left = (Page) BufferGetPage(leftbuf); } - } - - /* build an InsertIndexResult for this insertion */ - res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); - - /* now insert the new index tuple */ - if (*(v.spl_left) != FirstOffsetNumber) { - gistPageAddItem(giststate, r, left, (Item) itup, - IndexTupleSize(itup), - leftoff, LP_USED, &tmpdentry, &newtup); - leftoff = OffsetNumberNext(leftoff); - ItemPointerSet(&(res->pointerData), lbknum, leftoff); - /* be tidy */ - if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) - pfree(tmpdentry.pred); - if (itup != newtup) - pfree(newtup); - } else { - gistPageAddItem(giststate, r, right, (Item) itup, - IndexTupleSize(itup), - rightoff, LP_USED, &tmpdentry, &newtup); - rightoff = OffsetNumberNext(rightoff); - ItemPointerSet(&(res->pointerData), rbknum, rightoff); - /* be tidy */ - if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) - pfree(tmpdentry.pred); - if (itup != newtup) - pfree(newtup); - } - - if ((bufblock = BufferGetBlockNumber(buffer)) != GISTP_ROOT) { - PageRestoreTempPage(left, p); - } - WriteBuffer(leftbuf); - WriteBuffer(rightbuf); - - /* - * Okay, the page is split. We have three things left to do: - * - * 1) Adjust any active scans on this index to cope with changes - * we introduced in its structure by splitting this page. - * - * 2) "Tighten" the bounding box of the pointer to the left - * page in the parent node in the tree, if any. Since we - * moved a bunch of stuff off the left page, we expect it - * to get smaller. This happens in the internal insertion - * routine. - * - * 3) Insert a pointer to the right page in the parent. This - * may cause the parent to split. If it does, we need to - * repeat steps one and two for each split node in the tree. - */ - - /* adjust active scans */ - gistadjscans(r, GISTOP_SPLIT, bufblock, FirstOffsetNumber); - - tupDesc = r->rd_att; - - ltup = (IndexTuple) index_formtuple(tupDesc, - (Datum *) &(v.spl_ldatum), isnull); - rtup = (IndexTuple) index_formtuple(tupDesc, - (Datum *) &(v.spl_rdatum), isnull); - pfree(isnull); - - /* set pointers to new child pages in the internal index tuples */ - ItemPointerSet(&(ltup->t_tid), lbknum, 1); - ItemPointerSet(&(rtup->t_tid), rbknum, 1); - - gistintinsert(r, stack, ltup, rtup, giststate); - - pfree(ltup); - pfree(rtup); - - return (res); + else + { + leftbuf = buffer; + IncrBufferRefCount(buffer); + lbknum = BufferGetBlockNumber(buffer); + left = (Page) PageGetTempPage(p, sizeof(GISTPageOpaqueData)); + } + + rightbuf = ReadBuffer(r, P_NEW); + GISTInitBuffer(rightbuf, opaque->flags); + rbknum = BufferGetBlockNumber(rightbuf); + right = (Page) BufferGetPage(rightbuf); + + /* generate the item array */ + maxoff = PageGetMaxOffsetNumber(p); + entryvec = (bytea *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(GISTENTRY)); + decompvec = (bool *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(bool)); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + item_1 = (IndexTuple) PageGetItem(p, PageGetItemId(p, i)); + gistdentryinit(giststate, &((GISTENTRY *) VARDATA(entryvec))[i], + (((char *) item_1) + sizeof(IndexTupleData)), + r, p, i, + IndexTupleSize(item_1) - sizeof(IndexTupleData), FALSE); + if ((char *) (((GISTENTRY *) VARDATA(entryvec))[i].pred) + == (((char *) item_1) + sizeof(IndexTupleData))) + decompvec[i] = FALSE; + else + decompvec[i] = TRUE; + } + + /* add the new datum as the last entry */ + gistdentryinit(giststate, &(((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]), + (((char *) itup) + sizeof(IndexTupleData)), + (Relation) NULL, (Page) NULL, + (OffsetNumber) 0, tmpentry.bytes, FALSE); + if ((char *) (((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]).pred != + (((char *) itup) + sizeof(IndexTupleData))) + decompvec[maxoff + 1] = TRUE; + else + decompvec[maxoff + 1] = FALSE; + + VARSIZE(entryvec) = (maxoff + 2) * sizeof(GISTENTRY) + VARHDRSZ; + + /* now let the user-defined picksplit function set up the split vector */ + (giststate->picksplitFn) (entryvec, &v); + + /* compress ldatum and rdatum */ + gistcentryinit(giststate, &tmpentry, v.spl_ldatum, (Relation) NULL, + (Page) NULL, (OffsetNumber) 0, + ((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE); + if (v.spl_ldatum != tmpentry.pred) + pfree(v.spl_ldatum); + v.spl_ldatum = tmpentry.pred; + + gistcentryinit(giststate, &tmpentry, v.spl_rdatum, (Relation) NULL, + (Page) NULL, (OffsetNumber) 0, + ((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE); + if (v.spl_rdatum != tmpentry.pred) + pfree(v.spl_rdatum); + v.spl_rdatum = tmpentry.pred; + + /* clean up the entry vector: its preds need to be deleted, too */ + for (i = FirstOffsetNumber; i <= maxoff + 1; i = OffsetNumberNext(i)) + if (decompvec[i]) + pfree(((GISTENTRY *) VARDATA(entryvec))[i].pred); + pfree(entryvec); + pfree(decompvec); + + leftoff = rightoff = FirstOffsetNumber; + maxoff = PageGetMaxOffsetNumber(p); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + itemid = PageGetItemId(p, i); + item = (IndexTuple) PageGetItem(p, itemid); + + if (i == *(v.spl_left)) + { + gistPageAddItem(giststate, r, left, (Item) item, + IndexTupleSize(item), + leftoff, LP_USED, &tmpdentry, &newtup); + leftoff = OffsetNumberNext(leftoff); + v.spl_left++; /* advance in left split vector */ + /* be tidy */ + if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData))) + pfree(tmpdentry.pred); + if ((IndexTuple) item != newtup) + pfree(newtup); + } + else + { + gistPageAddItem(giststate, r, right, (Item) item, + IndexTupleSize(item), + rightoff, LP_USED, &tmpdentry, &newtup); + rightoff = OffsetNumberNext(rightoff); + v.spl_right++; /* advance in right split vector */ + /* be tidy */ + if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData))) + pfree(tmpdentry.pred); + if (item != newtup) + pfree(newtup); + } + } + + /* build an InsertIndexResult for this insertion */ + res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); + + /* now insert the new index tuple */ + if (*(v.spl_left) != FirstOffsetNumber) + { + gistPageAddItem(giststate, r, left, (Item) itup, + IndexTupleSize(itup), + leftoff, LP_USED, &tmpdentry, &newtup); + leftoff = OffsetNumberNext(leftoff); + ItemPointerSet(&(res->pointerData), lbknum, leftoff); + /* be tidy */ + if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) + pfree(tmpdentry.pred); + if (itup != newtup) + pfree(newtup); + } + else + { + gistPageAddItem(giststate, r, right, (Item) itup, + IndexTupleSize(itup), + rightoff, LP_USED, &tmpdentry, &newtup); + rightoff = OffsetNumberNext(rightoff); + ItemPointerSet(&(res->pointerData), rbknum, rightoff); + /* be tidy */ + if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData))) + pfree(tmpdentry.pred); + if (itup != newtup) + pfree(newtup); + } + + if ((bufblock = BufferGetBlockNumber(buffer)) != GISTP_ROOT) + { + PageRestoreTempPage(left, p); + } + WriteBuffer(leftbuf); + WriteBuffer(rightbuf); + + /* + * Okay, the page is split. We have three things left to do: + * + * 1) Adjust any active scans on this index to cope with changes we + * introduced in its structure by splitting this page. + * + * 2) "Tighten" the bounding box of the pointer to the left page in the + * parent node in the tree, if any. Since we moved a bunch of stuff + * off the left page, we expect it to get smaller. This happens in + * the internal insertion routine. + * + * 3) Insert a pointer to the right page in the parent. This may cause + * the parent to split. If it does, we need to repeat steps one and + * two for each split node in the tree. + */ + + /* adjust active scans */ + gistadjscans(r, GISTOP_SPLIT, bufblock, FirstOffsetNumber); + + tupDesc = r->rd_att; + + ltup = (IndexTuple) index_formtuple(tupDesc, + (Datum *) & (v.spl_ldatum), isnull); + rtup = (IndexTuple) index_formtuple(tupDesc, + (Datum *) & (v.spl_rdatum), isnull); + pfree(isnull); + + /* set pointers to new child pages in the internal index tuples */ + ItemPointerSet(&(ltup->t_tid), lbknum, 1); + ItemPointerSet(&(rtup->t_tid), rbknum, 1); + + gistintinsert(r, stack, ltup, rtup, giststate); + + pfree(ltup); + pfree(rtup); + + return (res); } /* @@ -813,22 +884,23 @@ gistSplit(Relation r, */ static void gistintinsert(Relation r, - GISTSTACK *stk, - IndexTuple ltup, /* new version of entry for old page */ - IndexTuple rtup, /* entry for new page */ - GISTSTATE *giststate) + GISTSTACK * stk, + IndexTuple ltup, /* new version of entry for old page */ + IndexTuple rtup, /* entry for new page */ + GISTSTATE * giststate) { - ItemPointerData ltid; + ItemPointerData ltid; - if (stk == (GISTSTACK *) NULL) { - gistnewroot(giststate, r, ltup, rtup); - return; - } - - /* remove old left pointer, insert the 2 new entries */ - ItemPointerSet(<id, stk->gs_blk, stk->gs_child); - gistdelete(r, (ItemPointer)<id); - gistentryinserttwo(r, stk, ltup, rtup, giststate); + if (stk == (GISTSTACK *) NULL) + { + gistnewroot(giststate, r, ltup, rtup); + return; + } + + /* remove old left pointer, insert the 2 new entries */ + ItemPointerSet(<id, stk->gs_blk, stk->gs_child); + gistdelete(r, (ItemPointer) & ltid); + gistentryinserttwo(r, stk, ltup, rtup, giststate); } @@ -836,280 +908,299 @@ gistintinsert(Relation r, ** Insert two entries onto one page, handling a split for either one! */ static void -gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup, - IndexTuple rtup, GISTSTATE *giststate) +gistentryinserttwo(Relation r, GISTSTACK * stk, IndexTuple ltup, + IndexTuple rtup, GISTSTATE * giststate) { - Buffer b; - Page p; - InsertIndexResult res; - GISTENTRY tmpentry; - IndexTuple newtup; - - b = ReadBuffer(r, stk->gs_blk); - p = BufferGetPage(b); - - if (gistnospace(p, ltup)) { - res = gistSplit(r, b, stk->gs_parent, ltup, giststate); - WriteBuffer(b); /* don't forget to release buffer! - 01/31/94 */ - pfree(res); - gistdoinsert(r, rtup, giststate); - } else { - gistPageAddItem(giststate, r, p, (Item)ltup, - IndexTupleSize(ltup), InvalidOffsetNumber, - LP_USED, &tmpentry, &newtup); - WriteBuffer(b); - gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred, - tmpentry.bytes, giststate); - /* be tidy */ - if (tmpentry.pred != (((char *) ltup) + sizeof(IndexTupleData))) - pfree(tmpentry.pred); - if (ltup != newtup) - pfree(newtup); - gistentryinsert(r, stk, rtup, giststate); - } -} + Buffer b; + Page p; + InsertIndexResult res; + GISTENTRY tmpentry; + IndexTuple newtup; + + b = ReadBuffer(r, stk->gs_blk); + p = BufferGetPage(b); + + if (gistnospace(p, ltup)) + { + res = gistSplit(r, b, stk->gs_parent, ltup, giststate); + WriteBuffer(b); /* don't forget to release buffer! - + * 01/31/94 */ + pfree(res); + gistdoinsert(r, rtup, giststate); + } + else + { + gistPageAddItem(giststate, r, p, (Item) ltup, + IndexTupleSize(ltup), InvalidOffsetNumber, + LP_USED, &tmpentry, &newtup); + WriteBuffer(b); + gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred, + tmpentry.bytes, giststate); + /* be tidy */ + if (tmpentry.pred != (((char *) ltup) + sizeof(IndexTupleData))) + pfree(tmpentry.pred); + if (ltup != newtup) + pfree(newtup); + gistentryinsert(r, stk, rtup, giststate); + } +} /* ** Insert an entry onto a page */ -static InsertIndexResult -gistentryinsert(Relation r, GISTSTACK *stk, IndexTuple tup, - GISTSTATE *giststate) +static InsertIndexResult +gistentryinsert(Relation r, GISTSTACK * stk, IndexTuple tup, + GISTSTATE * giststate) { - Buffer b; - Page p; - InsertIndexResult res; - OffsetNumber off; - GISTENTRY tmpentry; - IndexTuple newtup; - - b = ReadBuffer(r, stk->gs_blk); - p = BufferGetPage(b); - - if (gistnospace(p, tup)) { - res = gistSplit(r, b, stk->gs_parent, tup, giststate); - WriteBuffer(b); /* don't forget to release buffer! - 01/31/94 */ - return(res); - } - else { - res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); - off = gistPageAddItem(giststate, r, p, (Item) tup, IndexTupleSize(tup), - InvalidOffsetNumber, LP_USED, &tmpentry, &newtup); - WriteBuffer(b); - ItemPointerSet(&(res->pointerData), stk->gs_blk, off); - gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred, - tmpentry.bytes, giststate); - /* be tidy */ - if (tmpentry.pred != (((char *) tup) + sizeof(IndexTupleData))) - pfree(tmpentry.pred); - if (tup != newtup) - pfree(newtup); - return(res); - } -} + Buffer b; + Page p; + InsertIndexResult res; + OffsetNumber off; + GISTENTRY tmpentry; + IndexTuple newtup; + + b = ReadBuffer(r, stk->gs_blk); + p = BufferGetPage(b); + + if (gistnospace(p, tup)) + { + res = gistSplit(r, b, stk->gs_parent, tup, giststate); + WriteBuffer(b); /* don't forget to release buffer! - + * 01/31/94 */ + return (res); + } + else + { + res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); + off = gistPageAddItem(giststate, r, p, (Item) tup, IndexTupleSize(tup), + InvalidOffsetNumber, LP_USED, &tmpentry, &newtup); + WriteBuffer(b); + ItemPointerSet(&(res->pointerData), stk->gs_blk, off); + gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred, + tmpentry.bytes, giststate); + /* be tidy */ + if (tmpentry.pred != (((char *) tup) + sizeof(IndexTupleData))) + pfree(tmpentry.pred); + if (tup != newtup) + pfree(newtup); + return (res); + } +} static void -gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt, IndexTuple rt) +gistnewroot(GISTSTATE * giststate, Relation r, IndexTuple lt, IndexTuple rt) { - Buffer b; - Page p; - GISTENTRY tmpentry; - IndexTuple newtup; - - b = ReadBuffer(r, GISTP_ROOT); - GISTInitBuffer(b, 0); - p = BufferGetPage(b); - gistPageAddItem(giststate, r, p, (Item) lt, IndexTupleSize(lt), - FirstOffsetNumber, - LP_USED, &tmpentry, &newtup); - /* be tidy */ - if (tmpentry.pred != (((char *) lt) + sizeof(IndexTupleData))) - pfree(tmpentry.pred); - if (lt != newtup) - pfree(newtup); - gistPageAddItem(giststate, r, p, (Item) rt, IndexTupleSize(rt), - OffsetNumberNext(FirstOffsetNumber), LP_USED, - &tmpentry, &newtup); - /* be tidy */ - if (tmpentry.pred != (((char *) rt) + sizeof(IndexTupleData))) - pfree(tmpentry.pred); - if (rt != newtup) - pfree(newtup); - WriteBuffer(b); + Buffer b; + Page p; + GISTENTRY tmpentry; + IndexTuple newtup; + + b = ReadBuffer(r, GISTP_ROOT); + GISTInitBuffer(b, 0); + p = BufferGetPage(b); + gistPageAddItem(giststate, r, p, (Item) lt, IndexTupleSize(lt), + FirstOffsetNumber, + LP_USED, &tmpentry, &newtup); + /* be tidy */ + if (tmpentry.pred != (((char *) lt) + sizeof(IndexTupleData))) + pfree(tmpentry.pred); + if (lt != newtup) + pfree(newtup); + gistPageAddItem(giststate, r, p, (Item) rt, IndexTupleSize(rt), + OffsetNumberNext(FirstOffsetNumber), LP_USED, + &tmpentry, &newtup); + /* be tidy */ + if (tmpentry.pred != (((char *) rt) + sizeof(IndexTupleData))) + pfree(tmpentry.pred); + if (rt != newtup) + pfree(newtup); + WriteBuffer(b); } static void GISTInitBuffer(Buffer b, uint32 f) { - GISTPageOpaque opaque; - Page page; - Size pageSize; - - pageSize = BufferGetPageSize(b); - - page = BufferGetPage(b); - memset(page, 0, (int) pageSize); - PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); - - opaque = (GISTPageOpaque) PageGetSpecialPointer(page); - opaque->flags = f; + GISTPageOpaque opaque; + Page page; + Size pageSize; + + pageSize = BufferGetPageSize(b); + + page = BufferGetPage(b); + memset(page, 0, (int) pageSize); + PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); + + opaque = (GISTPageOpaque) PageGetSpecialPointer(page); + opaque->flags = f; } /* ** find entry with lowest penalty */ -static OffsetNumber -gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ - GISTSTATE *giststate) +static OffsetNumber +gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ + GISTSTATE * giststate) { - OffsetNumber maxoff; - OffsetNumber i; - char *id; - char *datum; - float usize; - OffsetNumber which; - float which_grow; - GISTENTRY entry, identry; - int size, idsize; - - idsize = IndexTupleSize(it) - sizeof(IndexTupleData); - id = ((char *) it) + sizeof(IndexTupleData); - maxoff = PageGetMaxOffsetNumber(p); - which_grow = -1.0; - which = -1; - - gistdentryinit(giststate,&identry,id,(Relation)NULL,(Page)NULL, - (OffsetNumber)0, idsize, FALSE); - - for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { - datum = (char *) PageGetItem(p, PageGetItemId(p, i)); - size = IndexTupleSize(datum) - sizeof(IndexTupleData); - datum += sizeof(IndexTupleData); - gistdentryinit(giststate,&entry,datum,r,p,i,size,FALSE); - (giststate->penaltyFn)(&entry, &identry, &usize); - if (which_grow < 0 || usize < which_grow) { - which = i; - which_grow = usize; - if (which_grow == 0) - break; + OffsetNumber maxoff; + OffsetNumber i; + char *id; + char *datum; + float usize; + OffsetNumber which; + float which_grow; + GISTENTRY entry, + identry; + int size, + idsize; + + idsize = IndexTupleSize(it) - sizeof(IndexTupleData); + id = ((char *) it) + sizeof(IndexTupleData); + maxoff = PageGetMaxOffsetNumber(p); + which_grow = -1.0; + which = -1; + + gistdentryinit(giststate, &identry, id, (Relation) NULL, (Page) NULL, + (OffsetNumber) 0, idsize, FALSE); + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + datum = (char *) PageGetItem(p, PageGetItemId(p, i)); + size = IndexTupleSize(datum) - sizeof(IndexTupleData); + datum += sizeof(IndexTupleData); + gistdentryinit(giststate, &entry, datum, r, p, i, size, FALSE); + (giststate->penaltyFn) (&entry, &identry, &usize); + if (which_grow < 0 || usize < which_grow) + { + which = i; + which_grow = usize; + if (which_grow == 0) + break; + } + if (entry.pred != datum) + pfree(entry.pred); } - if (entry.pred != datum) - pfree(entry.pred); - } - if (identry.pred != id) - pfree(identry.pred); - - return (which); + if (identry.pred != id) + pfree(identry.pred); + + return (which); } static int gistnospace(Page p, IndexTuple it) { - return (PageGetFreeSpace(p) < IndexTupleSize(it)); + return (PageGetFreeSpace(p) < IndexTupleSize(it)); } void -gistfreestack(GISTSTACK *s) +gistfreestack(GISTSTACK * s) { - GISTSTACK *p; - - while (s != (GISTSTACK *) NULL) { - p = s->gs_parent; - pfree(s); - s = p; - } + GISTSTACK *p; + + while (s != (GISTSTACK *) NULL) + { + p = s->gs_parent; + pfree(s); + s = p; + } } -/* -** remove an entry from a page +/* +** remove an entry from a page */ void gistdelete(Relation r, ItemPointer tid) { - BlockNumber blkno; - OffsetNumber offnum; - Buffer buf; - Page page; - - /* must write-lock on delete */ - RelationSetLockForWrite(r); - - blkno = ItemPointerGetBlockNumber(tid); - offnum = ItemPointerGetOffsetNumber(tid); - - /* adjust any scans that will be affected by this deletion */ - gistadjscans(r, GISTOP_DEL, blkno, offnum); - - /* delete the index tuple */ - buf = ReadBuffer(r, blkno); - page = BufferGetPage(buf); - - PageIndexTupleDelete(page, offnum); - - WriteBuffer(buf); - - /* XXX -- two-phase locking, don't release the write lock */ + BlockNumber blkno; + OffsetNumber offnum; + Buffer buf; + Page page; + + /* must write-lock on delete */ + RelationSetLockForWrite(r); + + blkno = ItemPointerGetBlockNumber(tid); + offnum = ItemPointerGetOffsetNumber(tid); + + /* adjust any scans that will be affected by this deletion */ + gistadjscans(r, GISTOP_DEL, blkno, offnum); + + /* delete the index tuple */ + buf = ReadBuffer(r, blkno); + page = BufferGetPage(buf); + + PageIndexTupleDelete(page, offnum); + + WriteBuffer(buf); + + /* XXX -- two-phase locking, don't release the write lock */ } -void -initGISTstate(GISTSTATE *giststate, Relation index) +void +initGISTstate(GISTSTATE * giststate, Relation index) { - RegProcedure consistent_proc, union_proc, compress_proc, decompress_proc; - RegProcedure penalty_proc, picksplit_proc, equal_proc; - func_ptr user_fn; - int pronargs; - HeapTuple htup; - IndexTupleForm itupform; - - consistent_proc = index_getprocid(index, 1, GIST_CONSISTENT_PROC); - union_proc = index_getprocid(index, 1, GIST_UNION_PROC); - compress_proc = index_getprocid(index, 1, GIST_COMPRESS_PROC); - decompress_proc = index_getprocid(index, 1, GIST_DECOMPRESS_PROC); - penalty_proc = index_getprocid(index, 1, GIST_PENALTY_PROC); - picksplit_proc = index_getprocid(index, 1, GIST_PICKSPLIT_PROC); - equal_proc = index_getprocid(index, 1, GIST_EQUAL_PROC); - fmgr_info(consistent_proc, &user_fn, &pronargs); - giststate->consistentFn = user_fn; - fmgr_info(union_proc, &user_fn, &pronargs); - giststate->unionFn = user_fn; - fmgr_info(compress_proc, &user_fn, &pronargs); - giststate->compressFn = user_fn; - fmgr_info(decompress_proc, &user_fn, &pronargs); - giststate->decompressFn = user_fn; - fmgr_info(penalty_proc, &user_fn, &pronargs); - giststate->penaltyFn = user_fn; - fmgr_info(picksplit_proc, &user_fn, &pronargs); - giststate->picksplitFn = user_fn; - fmgr_info(equal_proc, &user_fn, &pronargs); - giststate->equalFn = user_fn; - - /* see if key type is different from type of attribute being indexed */ - htup = SearchSysCacheTuple(INDEXRELID, ObjectIdGetDatum(index->rd_id), - 0,0,0); - itupform = (IndexTupleForm)GETSTRUCT(htup); - if (!HeapTupleIsValid(htup)) - elog(WARN, "initGISTstate: index %d not found", index->rd_id); - giststate->haskeytype = itupform->indhaskeytype; - if (giststate->haskeytype) { - /* key type is different -- is it byval? */ - htup = SearchSysCacheTuple(ATTNUM, - ObjectIdGetDatum(itupform->indexrelid), - UInt16GetDatum(FirstOffsetNumber), - 0,0); - if (!HeapTupleIsValid(htup)) { - elog(WARN, "initGISTstate: no attribute tuple %d %d", - itupform->indexrelid, FirstOffsetNumber); - return; + RegProcedure consistent_proc, + union_proc, + compress_proc, + decompress_proc; + RegProcedure penalty_proc, + picksplit_proc, + equal_proc; + func_ptr user_fn; + int pronargs; + HeapTuple htup; + IndexTupleForm itupform; + + consistent_proc = index_getprocid(index, 1, GIST_CONSISTENT_PROC); + union_proc = index_getprocid(index, 1, GIST_UNION_PROC); + compress_proc = index_getprocid(index, 1, GIST_COMPRESS_PROC); + decompress_proc = index_getprocid(index, 1, GIST_DECOMPRESS_PROC); + penalty_proc = index_getprocid(index, 1, GIST_PENALTY_PROC); + picksplit_proc = index_getprocid(index, 1, GIST_PICKSPLIT_PROC); + equal_proc = index_getprocid(index, 1, GIST_EQUAL_PROC); + fmgr_info(consistent_proc, &user_fn, &pronargs); + giststate->consistentFn = user_fn; + fmgr_info(union_proc, &user_fn, &pronargs); + giststate->unionFn = user_fn; + fmgr_info(compress_proc, &user_fn, &pronargs); + giststate->compressFn = user_fn; + fmgr_info(decompress_proc, &user_fn, &pronargs); + giststate->decompressFn = user_fn; + fmgr_info(penalty_proc, &user_fn, &pronargs); + giststate->penaltyFn = user_fn; + fmgr_info(picksplit_proc, &user_fn, &pronargs); + giststate->picksplitFn = user_fn; + fmgr_info(equal_proc, &user_fn, &pronargs); + giststate->equalFn = user_fn; + + /* see if key type is different from type of attribute being indexed */ + htup = SearchSysCacheTuple(INDEXRELID, ObjectIdGetDatum(index->rd_id), + 0, 0, 0); + itupform = (IndexTupleForm) GETSTRUCT(htup); + if (!HeapTupleIsValid(htup)) + elog(WARN, "initGISTstate: index %d not found", index->rd_id); + giststate->haskeytype = itupform->indhaskeytype; + if (giststate->haskeytype) + { + /* key type is different -- is it byval? */ + htup = SearchSysCacheTuple(ATTNUM, + ObjectIdGetDatum(itupform->indexrelid), + UInt16GetDatum(FirstOffsetNumber), + 0, 0); + if (!HeapTupleIsValid(htup)) + { + elog(WARN, "initGISTstate: no attribute tuple %d %d", + itupform->indexrelid, FirstOffsetNumber); + return; + } + giststate->keytypbyval = (((AttributeTupleForm) htup)->attbyval); } - giststate->keytypbyval = (((AttributeTupleForm)htup)->attbyval); - } - else - giststate->keytypbyval = FALSE; - return; + else + giststate->keytypbyval = FALSE; + return; } @@ -1118,56 +1209,61 @@ initGISTstate(GISTSTATE *giststate, Relation index) ** the key with another key, which may involve generating a new IndexTuple ** if the sizes don't match */ -static IndexTuple +static IndexTuple gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t) { - char * datum = (((char *) t) + sizeof(IndexTupleData)); - - /* if new entry fits in index tuple, copy it in */ - if (entry.bytes < IndexTupleSize(t) - sizeof(IndexTupleData)) { - memcpy(datum, entry.pred, entry.bytes); - /* clear out old size */ - t->t_info &= 0xe000; - /* or in new size */ - t->t_info |= MAXALIGN(entry.bytes + sizeof(IndexTupleData)); - - return(t); - } - else { - /* generate a new index tuple for the compressed entry */ - TupleDesc tupDesc = r->rd_att; - IndexTuple newtup; - char *isnull; - int blank; - - isnull = (char *) palloc(r->rd_rel->relnatts); - for (blank = 0; blank < r->rd_rel->relnatts; blank++) - isnull[blank] = ' '; - newtup = (IndexTuple) index_formtuple(tupDesc, - (Datum *)&(entry.pred), - isnull); - newtup->t_tid = t->t_tid; - pfree(isnull); - return(newtup); - } + char *datum = (((char *) t) + sizeof(IndexTupleData)); + + /* if new entry fits in index tuple, copy it in */ + if (entry.bytes < IndexTupleSize(t) - sizeof(IndexTupleData)) + { + memcpy(datum, entry.pred, entry.bytes); + /* clear out old size */ + t->t_info &= 0xe000; + /* or in new size */ + t->t_info |= MAXALIGN(entry.bytes + sizeof(IndexTupleData)); + + return (t); + } + else + { + /* generate a new index tuple for the compressed entry */ + TupleDesc tupDesc = r->rd_att; + IndexTuple newtup; + char *isnull; + int blank; + + isnull = (char *) palloc(r->rd_rel->relnatts); + for (blank = 0; blank < r->rd_rel->relnatts; blank++) + isnull[blank] = ' '; + newtup = (IndexTuple) index_formtuple(tupDesc, + (Datum *) & (entry.pred), + isnull); + newtup->t_tid = t->t_tid; + pfree(isnull); + return (newtup); + } } - + /* ** initialize a GiST entry with a decompressed version of pred */ void -gistdentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r, - Page pg, OffsetNumber o, int b, bool l) -{ - GISTENTRY *dep; - gistentryinit(*e, pr, r, pg, o, b, l); - if (giststate->haskeytype) { - dep = (GISTENTRY *)((giststate->decompressFn)(e)); - gistentryinit(*e, dep->pred, dep->rel, dep->page, dep->offset, dep->bytes, - dep->leafkey); - if (dep != e) pfree(dep); - } +gistdentryinit(GISTSTATE * giststate, GISTENTRY * e, char *pr, Relation r, + Page pg, OffsetNumber o, int b, bool l) +{ + GISTENTRY *dep; + + gistentryinit(*e, pr, r, pg, o, b, l); + if (giststate->haskeytype) + { + dep = (GISTENTRY *) ((giststate->decompressFn) (e)); + gistentryinit(*e, dep->pred, dep->rel, dep->page, dep->offset, dep->bytes, + dep->leafkey); + if (dep != e) + pfree(dep); + } } @@ -1175,19 +1271,22 @@ gistdentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r, ** initialize a GiST entry with a compressed version of pred */ static void -gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r, - Page pg, OffsetNumber o, int b, bool l) -{ - GISTENTRY *cep; - gistentryinit(*e, pr, r, pg, o, b, l); - if (giststate->haskeytype) { - cep = (GISTENTRY *)((giststate->compressFn)(e)); - gistentryinit(*e, cep->pred, cep->rel, cep->page, cep->offset, cep->bytes, - cep->leafkey); - if (cep != e) pfree(cep); - } +gistcentryinit(GISTSTATE * giststate, GISTENTRY * e, char *pr, Relation r, + Page pg, OffsetNumber o, int b, bool l) +{ + GISTENTRY *cep; + + gistentryinit(*e, pr, r, pg, o, b, l); + if (giststate->haskeytype) + { + cep = (GISTENTRY *) ((giststate->compressFn) (e)); + gistentryinit(*e, cep->pred, cep->rel, cep->page, cep->offset, cep->bytes, + cep->leafkey); + if (cep != e) + pfree(cep); + } } - + #ifdef GISTDEBUG @@ -1200,89 +1299,95 @@ gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r, void _gistdump(Relation r) { - Buffer buf; - Page page; - OffsetNumber offnum, maxoff; - BlockNumber blkno; - BlockNumber nblocks; - GISTPageOpaque po; - IndexTuple itup; - BlockNumber itblkno; - OffsetNumber itoffno; - char *datum; - char *itkey; - - nblocks = RelationGetNumberOfBlocks(r); - for (blkno = 0; blkno < nblocks; blkno++) { - buf = ReadBuffer(r, blkno); - page = BufferGetPage(buf); - po = (GISTPageOpaque) PageGetSpecialPointer(page); - maxoff = PageGetMaxOffsetNumber(page); - printf("Page %d maxoff %d <%s>\n", blkno, maxoff, - (po->flags & F_LEAF ? "LEAF" : "INTERNAL")); - - if (PageIsEmpty(page)) { - ReleaseBuffer(buf); - continue; - } - - for (offnum = FirstOffsetNumber; - offnum <= maxoff; - offnum = OffsetNumberNext(offnum)) { - itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); - itblkno = ItemPointerGetBlockNumber(&(itup->t_tid)); - itoffno = ItemPointerGetOffsetNumber(&(itup->t_tid)); - datum = ((char *) itup); - datum += sizeof(IndexTupleData); - /* get out function for type of key, and out it! */ - itkey = (char *) int_range_out((INTRANGE *)datum); - /* itkey = " unable to print"; */ - printf("\t[%d] size %d heap <%d,%d> key:%s\n", - offnum, IndexTupleSize(itup), itblkno, itoffno, itkey); - pfree(itkey); + Buffer buf; + Page page; + OffsetNumber offnum, + maxoff; + BlockNumber blkno; + BlockNumber nblocks; + GISTPageOpaque po; + IndexTuple itup; + BlockNumber itblkno; + OffsetNumber itoffno; + char *datum; + char *itkey; + + nblocks = RelationGetNumberOfBlocks(r); + for (blkno = 0; blkno < nblocks; blkno++) + { + buf = ReadBuffer(r, blkno); + page = BufferGetPage(buf); + po = (GISTPageOpaque) PageGetSpecialPointer(page); + maxoff = PageGetMaxOffsetNumber(page); + printf("Page %d maxoff %d <%s>\n", blkno, maxoff, + (po->flags & F_LEAF ? "LEAF" : "INTERNAL")); + + if (PageIsEmpty(page)) + { + ReleaseBuffer(buf); + continue; + } + + for (offnum = FirstOffsetNumber; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) + { + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); + itblkno = ItemPointerGetBlockNumber(&(itup->t_tid)); + itoffno = ItemPointerGetOffsetNumber(&(itup->t_tid)); + datum = ((char *) itup); + datum += sizeof(IndexTupleData); + /* get out function for type of key, and out it! */ + itkey = (char *) int_range_out((INTRANGE *) datum); + /* itkey = " unable to print"; */ + printf("\t[%d] size %d heap <%d,%d> key:%s\n", + offnum, IndexTupleSize(itup), itblkno, itoffno, itkey); + pfree(itkey); + } + + ReleaseBuffer(buf); } - - ReleaseBuffer(buf); - } } #ifdef NOT_USED -static char *text_range_out(TXTRANGE *r) +static char * +text_range_out(TXTRANGE * r) { - char *result; - char *lower, *upper; - - if (r == NULL) - return(NULL); - result = (char *)palloc(16 + VARSIZE(TRLOWER(r)) + VARSIZE(TRUPPER(r)) - - 2*VARHDRSZ); - - lower = (char *)palloc(VARSIZE(TRLOWER(r)) + 1 - VARHDRSZ); - memcpy(lower, VARDATA(TRLOWER(r)), VARSIZE(TRLOWER(r)) - VARHDRSZ); - lower[VARSIZE(TRLOWER(r)) - VARHDRSZ] = '\0'; - upper = (char *)palloc(VARSIZE(TRUPPER(r)) + 1 - VARHDRSZ); - memcpy(upper, VARDATA(TRUPPER(r)), VARSIZE(TRUPPER(r)) - VARHDRSZ); - upper[VARSIZE(TRUPPER(r)) - VARHDRSZ] = '\0'; - - sprintf(result, "[%s,%s): %d", lower, upper, r->flag); - pfree(lower); - pfree(upper); - return(result); + char *result; + char *lower, + *upper; + + if (r == NULL) + return (NULL); + result = (char *) palloc(16 + VARSIZE(TRLOWER(r)) + VARSIZE(TRUPPER(r)) + - 2 * VARHDRSZ); + + lower = (char *) palloc(VARSIZE(TRLOWER(r)) + 1 - VARHDRSZ); + memcpy(lower, VARDATA(TRLOWER(r)), VARSIZE(TRLOWER(r)) - VARHDRSZ); + lower[VARSIZE(TRLOWER(r)) - VARHDRSZ] = '\0'; + upper = (char *) palloc(VARSIZE(TRUPPER(r)) + 1 - VARHDRSZ); + memcpy(upper, VARDATA(TRUPPER(r)), VARSIZE(TRUPPER(r)) - VARHDRSZ); + upper[VARSIZE(TRUPPER(r)) - VARHDRSZ] = '\0'; + + sprintf(result, "[%s,%s): %d", lower, upper, r->flag); + pfree(lower); + pfree(upper); + return (result); } + #endif -static char * -int_range_out(INTRANGE *r) +static char * +int_range_out(INTRANGE * r) { - char *result; - - if (r == NULL) - return(NULL); - result = (char *)palloc(80); - sprintf(result, "[%d,%d): %d",r->lower, r->upper, r->flag); - - return(result); -} + char *result; + + if (r == NULL) + return (NULL); + result = (char *) palloc(80); + sprintf(result, "[%d,%d): %d", r->lower, r->upper, r->flag); -#endif /* defined GISTDEBUG */ + return (result); +} +#endif /* defined GISTDEBUG */ diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index ac1697e5ed2..cad4cef267e 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -1,12 +1,12 @@ /*------------------------------------------------------------------------- * * gistget.c-- - * fetch tuples from a GiST scan. + * fetch tuples from a GiST scan. * * * * IDENTIFICATION - * /usr/local/devel/pglite/cvs/src/backend/access/gisr/gistget.c,v 1.9.1 1996/11/21 01:00:00 vadim Exp + * /usr/local/devel/pglite/cvs/src/backend/access/gisr/gistget.c,v 1.9.1 1996/11/21 01:00:00 vadim Exp * *------------------------------------------------------------------------- */ @@ -22,350 +22,392 @@ #include <storage/bufmgr.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static OffsetNumber gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, - ScanDirection dir); +static OffsetNumber +gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, + ScanDirection dir); static RetrieveIndexResult gistscancache(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult gistfirst(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult gistnext(IndexScanDesc s, ScanDirection dir); static ItemPointer gistheapptr(Relation r, ItemPointer itemp); -static bool gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc, - int scanKeySize, ScanKey key, GISTSTATE *giststate, - Relation r, Page p, OffsetNumber offset); +static bool +gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc, + int scanKeySize, ScanKey key, GISTSTATE * giststate, + Relation r, Page p, OffsetNumber offset); RetrieveIndexResult gistgettuple(IndexScanDesc s, ScanDirection dir) { - RetrieveIndexResult res; - - /* if we have it cached in the scan desc, just return the value */ - if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL) + RetrieveIndexResult res; + + /* if we have it cached in the scan desc, just return the value */ + if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL) + return (res); + + /* not cached, so we'll have to do some work */ + if (ItemPointerIsValid(&(s->currentItemData))) + { + res = gistnext(s, dir); + } + else + { + res = gistfirst(s, dir); + } return (res); - - /* not cached, so we'll have to do some work */ - if (ItemPointerIsValid(&(s->currentItemData))) { - res = gistnext(s, dir); - } else { - res = gistfirst(s, dir); - } - return (res); } -static RetrieveIndexResult +static RetrieveIndexResult gistfirst(IndexScanDesc s, ScanDirection dir) { - Buffer b; - Page p; - OffsetNumber n; - OffsetNumber maxoff; - RetrieveIndexResult res; - GISTPageOpaque po; - GISTScanOpaque so; - GISTSTACK *stk; - BlockNumber blk; - IndexTuple it; - - b = ReadBuffer(s->relation, GISTP_ROOT); - p = BufferGetPage(b); - po = (GISTPageOpaque) PageGetSpecialPointer(p); - so = (GISTScanOpaque) s->opaque; - - for (;;) { - maxoff = PageGetMaxOffsetNumber(p); - if (ScanDirectionIsBackward(dir)) - n = gistfindnext(s, p, maxoff, dir); - else - n = gistfindnext(s, p, FirstOffsetNumber, dir); - - while (n < FirstOffsetNumber || n > maxoff) { - - ReleaseBuffer(b); - if (so->s_stack == (GISTSTACK *) NULL) - return ((RetrieveIndexResult) NULL); - - stk = so->s_stack; - b = ReadBuffer(s->relation, stk->gs_blk); - p = BufferGetPage(b); - po = (GISTPageOpaque) PageGetSpecialPointer(p); - maxoff = PageGetMaxOffsetNumber(p); - - if (ScanDirectionIsBackward(dir)) { - n = OffsetNumberPrev(stk->gs_child); - } else { - n = OffsetNumberNext(stk->gs_child); - } - so->s_stack = stk->gs_parent; - pfree(stk); - - n = gistfindnext(s, p, n, dir); - } - if (po->flags & F_LEAF) { - ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n); - - it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); - - res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid)); - - ReleaseBuffer(b); - return (res); - } else { - stk = (GISTSTACK *) palloc(sizeof(GISTSTACK)); - stk->gs_child = n; - stk->gs_blk = BufferGetBlockNumber(b); - stk->gs_parent = so->s_stack; - so->s_stack = stk; - - it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); - blk = ItemPointerGetBlockNumber(&(it->t_tid)); - - ReleaseBuffer(b); - b = ReadBuffer(s->relation, blk); - p = BufferGetPage(b); - po = (GISTPageOpaque) PageGetSpecialPointer(p); + Buffer b; + Page p; + OffsetNumber n; + OffsetNumber maxoff; + RetrieveIndexResult res; + GISTPageOpaque po; + GISTScanOpaque so; + GISTSTACK *stk; + BlockNumber blk; + IndexTuple it; + + b = ReadBuffer(s->relation, GISTP_ROOT); + p = BufferGetPage(b); + po = (GISTPageOpaque) PageGetSpecialPointer(p); + so = (GISTScanOpaque) s->opaque; + + for (;;) + { + maxoff = PageGetMaxOffsetNumber(p); + if (ScanDirectionIsBackward(dir)) + n = gistfindnext(s, p, maxoff, dir); + else + n = gistfindnext(s, p, FirstOffsetNumber, dir); + + while (n < FirstOffsetNumber || n > maxoff) + { + + ReleaseBuffer(b); + if (so->s_stack == (GISTSTACK *) NULL) + return ((RetrieveIndexResult) NULL); + + stk = so->s_stack; + b = ReadBuffer(s->relation, stk->gs_blk); + p = BufferGetPage(b); + po = (GISTPageOpaque) PageGetSpecialPointer(p); + maxoff = PageGetMaxOffsetNumber(p); + + if (ScanDirectionIsBackward(dir)) + { + n = OffsetNumberPrev(stk->gs_child); + } + else + { + n = OffsetNumberNext(stk->gs_child); + } + so->s_stack = stk->gs_parent; + pfree(stk); + + n = gistfindnext(s, p, n, dir); + } + if (po->flags & F_LEAF) + { + ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n); + + it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); + + res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid)); + + ReleaseBuffer(b); + return (res); + } + else + { + stk = (GISTSTACK *) palloc(sizeof(GISTSTACK)); + stk->gs_child = n; + stk->gs_blk = BufferGetBlockNumber(b); + stk->gs_parent = so->s_stack; + so->s_stack = stk; + + it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); + blk = ItemPointerGetBlockNumber(&(it->t_tid)); + + ReleaseBuffer(b); + b = ReadBuffer(s->relation, blk); + p = BufferGetPage(b); + po = (GISTPageOpaque) PageGetSpecialPointer(p); + } } - } } -static RetrieveIndexResult +static RetrieveIndexResult gistnext(IndexScanDesc s, ScanDirection dir) { - Buffer b; - Page p; - OffsetNumber n; - OffsetNumber maxoff; - RetrieveIndexResult res; - GISTPageOpaque po; - GISTScanOpaque so; - GISTSTACK *stk; - BlockNumber blk; - IndexTuple it; - - blk = ItemPointerGetBlockNumber(&(s->currentItemData)); - n = ItemPointerGetOffsetNumber(&(s->currentItemData)); - - if (ScanDirectionIsForward(dir)) { - n = OffsetNumberNext(n); - } else { - n = OffsetNumberPrev(n); - } - - b = ReadBuffer(s->relation, blk); - p = BufferGetPage(b); - po = (GISTPageOpaque) PageGetSpecialPointer(p); - so = (GISTScanOpaque) s->opaque; - - for (;;) { - maxoff = PageGetMaxOffsetNumber(p); - n = gistfindnext(s, p, n, dir); - - while (n < FirstOffsetNumber || n > maxoff) { - - ReleaseBuffer(b); - if (so->s_stack == (GISTSTACK *) NULL) - return ((RetrieveIndexResult) NULL); - - stk = so->s_stack; - b = ReadBuffer(s->relation, stk->gs_blk); - p = BufferGetPage(b); - maxoff = PageGetMaxOffsetNumber(p); - po = (GISTPageOpaque) PageGetSpecialPointer(p); - - if (ScanDirectionIsBackward(dir)) { - n = OffsetNumberPrev(stk->gs_child); - } else { - n = OffsetNumberNext(stk->gs_child); - } - so->s_stack = stk->gs_parent; - pfree(stk); - - n = gistfindnext(s, p, n, dir); + Buffer b; + Page p; + OffsetNumber n; + OffsetNumber maxoff; + RetrieveIndexResult res; + GISTPageOpaque po; + GISTScanOpaque so; + GISTSTACK *stk; + BlockNumber blk; + IndexTuple it; + + blk = ItemPointerGetBlockNumber(&(s->currentItemData)); + n = ItemPointerGetOffsetNumber(&(s->currentItemData)); + + if (ScanDirectionIsForward(dir)) + { + n = OffsetNumberNext(n); + } + else + { + n = OffsetNumberPrev(n); } - if (po->flags & F_LEAF) { - ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n); - - it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); - - res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid)); - - ReleaseBuffer(b); - return (res); - } else { - stk = (GISTSTACK *) palloc(sizeof(GISTSTACK)); - stk->gs_child = n; - stk->gs_blk = BufferGetBlockNumber(b); - stk->gs_parent = so->s_stack; - so->s_stack = stk; - - it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); - blk = ItemPointerGetBlockNumber(&(it->t_tid)); - - ReleaseBuffer(b); - b = ReadBuffer(s->relation, blk); - p = BufferGetPage(b); - po = (GISTPageOpaque) PageGetSpecialPointer(p); - - if (ScanDirectionIsBackward(dir)) { - n = PageGetMaxOffsetNumber(p); - } else { - n = FirstOffsetNumber; - } + + b = ReadBuffer(s->relation, blk); + p = BufferGetPage(b); + po = (GISTPageOpaque) PageGetSpecialPointer(p); + so = (GISTScanOpaque) s->opaque; + + for (;;) + { + maxoff = PageGetMaxOffsetNumber(p); + n = gistfindnext(s, p, n, dir); + + while (n < FirstOffsetNumber || n > maxoff) + { + + ReleaseBuffer(b); + if (so->s_stack == (GISTSTACK *) NULL) + return ((RetrieveIndexResult) NULL); + + stk = so->s_stack; + b = ReadBuffer(s->relation, stk->gs_blk); + p = BufferGetPage(b); + maxoff = PageGetMaxOffsetNumber(p); + po = (GISTPageOpaque) PageGetSpecialPointer(p); + + if (ScanDirectionIsBackward(dir)) + { + n = OffsetNumberPrev(stk->gs_child); + } + else + { + n = OffsetNumberNext(stk->gs_child); + } + so->s_stack = stk->gs_parent; + pfree(stk); + + n = gistfindnext(s, p, n, dir); + } + if (po->flags & F_LEAF) + { + ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n); + + it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); + + res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid)); + + ReleaseBuffer(b); + return (res); + } + else + { + stk = (GISTSTACK *) palloc(sizeof(GISTSTACK)); + stk->gs_child = n; + stk->gs_blk = BufferGetBlockNumber(b); + stk->gs_parent = so->s_stack; + so->s_stack = stk; + + it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); + blk = ItemPointerGetBlockNumber(&(it->t_tid)); + + ReleaseBuffer(b); + b = ReadBuffer(s->relation, blk); + p = BufferGetPage(b); + po = (GISTPageOpaque) PageGetSpecialPointer(p); + + if (ScanDirectionIsBackward(dir)) + { + n = PageGetMaxOffsetNumber(p); + } + else + { + n = FirstOffsetNumber; + } + } } - } } /* Similar to index_keytest, but decompresses the key in the IndexTuple */ -static bool +static bool gistindex_keytest(IndexTuple tuple, - TupleDesc tupdesc, - int scanKeySize, - ScanKey key, - GISTSTATE *giststate, - Relation r, - Page p, - OffsetNumber offset) + TupleDesc tupdesc, + int scanKeySize, + ScanKey key, + GISTSTATE * giststate, + Relation r, + Page p, + OffsetNumber offset) { - bool isNull; - Datum datum; - int test; - GISTENTRY de; - - IncrIndexProcessed(); - - - while (scanKeySize > 0) { - datum = index_getattr(tuple, - 1, - tupdesc, - &isNull); - gistdentryinit(giststate, &de, (char *)datum, r, p, offset, - IndexTupleSize(tuple) - sizeof(IndexTupleData), - FALSE); - - if (isNull) { - /* XXX eventually should check if SK_ISNULL */ - return (false); - } - - if (key[0].sk_flags & SK_COMMUTE) { - test = (*(key[0].sk_func)) - (DatumGetPointer(key[0].sk_argument), - &de, key[0].sk_procedure) ? 1 : 0; - } else { - test = (*(key[0].sk_func)) - (&de, - DatumGetPointer(key[0].sk_argument), - key[0].sk_procedure) ? 1 : 0; - } - - if (!test == !(key[0].sk_flags & SK_NEGATE)) { - return (false); + bool isNull; + Datum datum; + int test; + GISTENTRY de; + + IncrIndexProcessed(); + + + while (scanKeySize > 0) + { + datum = index_getattr(tuple, + 1, + tupdesc, + &isNull); + gistdentryinit(giststate, &de, (char *) datum, r, p, offset, + IndexTupleSize(tuple) - sizeof(IndexTupleData), + FALSE); + + if (isNull) + { + /* XXX eventually should check if SK_ISNULL */ + return (false); + } + + if (key[0].sk_flags & SK_COMMUTE) + { + test = (*(key[0].sk_func)) + (DatumGetPointer(key[0].sk_argument), + &de, key[0].sk_procedure) ? 1 : 0; + } + else + { + test = (*(key[0].sk_func)) + (&de, + DatumGetPointer(key[0].sk_argument), + key[0].sk_procedure) ? 1 : 0; + } + + if (!test == !(key[0].sk_flags & SK_NEGATE)) + { + return (false); + } + + scanKeySize -= 1; + key++; } - - scanKeySize -= 1; - key++; - } - - return (true); + + return (true); } -static OffsetNumber +static OffsetNumber gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir) { - OffsetNumber maxoff; - char *it; - GISTPageOpaque po; - GISTScanOpaque so; - GISTSTATE *giststate; - - maxoff = PageGetMaxOffsetNumber(p); - po = (GISTPageOpaque) PageGetSpecialPointer(p); - so = (GISTScanOpaque) s->opaque; - giststate = so->giststate; - - /* - * If we modified the index during the scan, we may have a pointer to - * a ghost tuple, before the scan. If this is the case, back up one. - */ - - if (so->s_flags & GS_CURBEFORE) { - so->s_flags &= ~GS_CURBEFORE; - n = OffsetNumberPrev(n); - } - - while (n >= FirstOffsetNumber && n <= maxoff) { - it = (char *) PageGetItem(p, PageGetItemId(p, n)); - if (gistindex_keytest((IndexTuple) it, - RelationGetTupleDescriptor(s->relation), - s->numberOfKeys, s->keyData, giststate, - s->relation, p, n)) - break; - - if (ScanDirectionIsBackward(dir)) { - n = OffsetNumberPrev(n); - } else { - n = OffsetNumberNext(n); + OffsetNumber maxoff; + char *it; + GISTPageOpaque po; + GISTScanOpaque so; + GISTSTATE *giststate; + + maxoff = PageGetMaxOffsetNumber(p); + po = (GISTPageOpaque) PageGetSpecialPointer(p); + so = (GISTScanOpaque) s->opaque; + giststate = so->giststate; + + /* + * If we modified the index during the scan, we may have a pointer to + * a ghost tuple, before the scan. If this is the case, back up one. + */ + + if (so->s_flags & GS_CURBEFORE) + { + so->s_flags &= ~GS_CURBEFORE; + n = OffsetNumberPrev(n); } - } - - return (n); + + while (n >= FirstOffsetNumber && n <= maxoff) + { + it = (char *) PageGetItem(p, PageGetItemId(p, n)); + if (gistindex_keytest((IndexTuple) it, + RelationGetTupleDescriptor(s->relation), + s->numberOfKeys, s->keyData, giststate, + s->relation, p, n)) + break; + + if (ScanDirectionIsBackward(dir)) + { + n = OffsetNumberPrev(n); + } + else + { + n = OffsetNumberNext(n); + } + } + + return (n); } -static RetrieveIndexResult +static RetrieveIndexResult gistscancache(IndexScanDesc s, ScanDirection dir) { - RetrieveIndexResult res; - ItemPointer ip; - - if (!(ScanDirectionIsNoMovement(dir) - && ItemPointerIsValid(&(s->currentItemData)))) { - - return ((RetrieveIndexResult) NULL); - } - - ip = gistheapptr(s->relation, &(s->currentItemData)); - - if (ItemPointerIsValid(ip)) - res = FormRetrieveIndexResult(&(s->currentItemData), ip); - else - res = (RetrieveIndexResult) NULL; - - pfree (ip); - - return (res); + RetrieveIndexResult res; + ItemPointer ip; + + if (!(ScanDirectionIsNoMovement(dir) + && ItemPointerIsValid(&(s->currentItemData)))) + { + + return ((RetrieveIndexResult) NULL); + } + + ip = gistheapptr(s->relation, &(s->currentItemData)); + + if (ItemPointerIsValid(ip)) + res = FormRetrieveIndexResult(&(s->currentItemData), ip); + else + res = (RetrieveIndexResult) NULL; + + pfree(ip); + + return (res); } /* - * gistheapptr returns the item pointer to the tuple in the heap relation - * for which itemp is the index relation item pointer. + * gistheapptr returns the item pointer to the tuple in the heap relation + * for which itemp is the index relation item pointer. */ -static ItemPointer +static ItemPointer gistheapptr(Relation r, ItemPointer itemp) { - Buffer b; - Page p; - IndexTuple it; - ItemPointer ip; - OffsetNumber n; - - ip = (ItemPointer) palloc(sizeof(ItemPointerData)); - if (ItemPointerIsValid(itemp)) { - b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp)); - p = BufferGetPage(b); - n = ItemPointerGetOffsetNumber(itemp); - it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); - memmove((char *) ip, (char *) &(it->t_tid), - sizeof(ItemPointerData)); - ReleaseBuffer(b); - } else { - ItemPointerSetInvalid(ip); - } - - return (ip); + Buffer b; + Page p; + IndexTuple it; + ItemPointer ip; + OffsetNumber n; + + ip = (ItemPointer) palloc(sizeof(ItemPointerData)); + if (ItemPointerIsValid(itemp)) + { + b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp)); + p = BufferGetPage(b); + n = ItemPointerGetOffsetNumber(itemp); + it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); + memmove((char *) ip, (char *) &(it->t_tid), + sizeof(ItemPointerData)); + ReleaseBuffer(b); + } + else + { + ItemPointerSetInvalid(ip); + } + + return (ip); } diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c index c877538472c..ec680558d88 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -1,11 +1,11 @@ /*------------------------------------------------------------------------- * * gistscan.c-- - * routines to manage scans on index relations + * routines to manage scans on index relations * * * IDENTIFICATION - * /usr/local/devel/pglite/cvs/src/backend/access/gist/gistscan.c,v 1.7 1995/06/14 00:10:05 jolly Exp + * /usr/local/devel/pglite/cvs/src/backend/access/gist/gistscan.c,v 1.7 1995/06/14 00:10:05 jolly Exp * *------------------------------------------------------------------------- */ @@ -18,375 +18,411 @@ #include <access/rtree.h> #include <storage/bufmgr.h> #include <access/giststrat.h> -#include <storage/lmgr.h> +#include <storage/lmgr.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* routines defined and used here */ -static void gistregscan(IndexScanDesc s); -static void gistdropscan(IndexScanDesc s); -static void gistadjone(IndexScanDesc s, int op, BlockNumber blkno, - OffsetNumber offnum); -static void adjuststack(GISTSTACK *stk, BlockNumber blkno, +static void gistregscan(IndexScanDesc s); +static void gistdropscan(IndexScanDesc s); +static void +gistadjone(IndexScanDesc s, int op, BlockNumber blkno, + OffsetNumber offnum); +static void +adjuststack(GISTSTACK * stk, BlockNumber blkno, OffsetNumber offnum); -static void adjustiptr(IndexScanDesc s, ItemPointer iptr, - int op, BlockNumber blkno, OffsetNumber offnum); +static void +adjustiptr(IndexScanDesc s, ItemPointer iptr, + int op, BlockNumber blkno, OffsetNumber offnum); /* - * Whenever we start a GiST scan in a backend, we register it in private - * space. Then if the GiST index gets updated, we check all registered - * scans and adjust them if the tuple they point at got moved by the - * update. We only need to do this in private space, because when we update - * an GiST we have a write lock on the tree, so no other process can have - * any locks at all on it. A single transaction can have write and read - * locks on the same object, so that's why we need to handle this case. + * Whenever we start a GiST scan in a backend, we register it in private + * space. Then if the GiST index gets updated, we check all registered + * scans and adjust them if the tuple they point at got moved by the + * update. We only need to do this in private space, because when we update + * an GiST we have a write lock on the tree, so no other process can have + * any locks at all on it. A single transaction can have write and read + * locks on the same object, so that's why we need to handle this case. */ -typedef struct GISTScanListData { - IndexScanDesc gsl_scan; - struct GISTScanListData *gsl_next; -} GISTScanListData; +typedef struct GISTScanListData +{ + IndexScanDesc gsl_scan; + struct GISTScanListData *gsl_next; +} GISTScanListData; -typedef GISTScanListData *GISTScanList; +typedef GISTScanListData *GISTScanList; /* pointer to list of local scans on GiSTs */ static GISTScanList GISTScans = (GISTScanList) NULL; - + IndexScanDesc gistbeginscan(Relation r, - bool fromEnd, - uint16 nkeys, - ScanKey key) + bool fromEnd, + uint16 nkeys, + ScanKey key) { - IndexScanDesc s; - - RelationSetLockForRead(r); - s = RelationGetIndexScan(r, fromEnd, nkeys, key); - gistregscan(s); - - return (s); + IndexScanDesc s; + + RelationSetLockForRead(r); + s = RelationGetIndexScan(r, fromEnd, nkeys, key); + gistregscan(s); + + return (s); } void gistrescan(IndexScanDesc s, bool fromEnd, ScanKey key) { - GISTScanOpaque p; - int i; - - if (!IndexScanIsValid(s)) { - elog(WARN, "gistrescan: invalid scan."); - return; - } - - /* - * Clear all the pointers. - */ - - ItemPointerSetInvalid(&s->previousItemData); - ItemPointerSetInvalid(&s->currentItemData); - ItemPointerSetInvalid(&s->nextItemData); - ItemPointerSetInvalid(&s->previousMarkData); - ItemPointerSetInvalid(&s->currentMarkData); - ItemPointerSetInvalid(&s->nextMarkData); - - /* - * Set flags. - */ - if (RelationGetNumberOfBlocks(s->relation) == 0) { - s->flags = ScanUnmarked; - } else if (fromEnd) { - s->flags = ScanUnmarked | ScanUncheckedPrevious; - } else { - s->flags = ScanUnmarked | ScanUncheckedNext; - } - - s->scanFromEnd = fromEnd; - - if (s->numberOfKeys > 0) { - memmove(s->keyData, - key, - s->numberOfKeys * sizeof(ScanKeyData)); - } - - p = (GISTScanOpaque) s->opaque; - if (p != (GISTScanOpaque) NULL) { - gistfreestack(p->s_stack); - gistfreestack(p->s_markstk); - p->s_stack = p->s_markstk = (GISTSTACK *) NULL; - p->s_flags = 0x0; - for (i = 0; i < s->numberOfKeys; i++) + GISTScanOpaque p; + int i; + + if (!IndexScanIsValid(s)) { - s->keyData[i].sk_procedure - = RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno, - s->keyData[i].sk_procedure); - s->keyData[i].sk_func = p->giststate->consistentFn; + elog(WARN, "gistrescan: invalid scan."); + return; + } + + /* + * Clear all the pointers. + */ + + ItemPointerSetInvalid(&s->previousItemData); + ItemPointerSetInvalid(&s->currentItemData); + ItemPointerSetInvalid(&s->nextItemData); + ItemPointerSetInvalid(&s->previousMarkData); + ItemPointerSetInvalid(&s->currentMarkData); + ItemPointerSetInvalid(&s->nextMarkData); + + /* + * Set flags. + */ + if (RelationGetNumberOfBlocks(s->relation) == 0) + { + s->flags = ScanUnmarked; + } + else if (fromEnd) + { + s->flags = ScanUnmarked | ScanUncheckedPrevious; + } + else + { + s->flags = ScanUnmarked | ScanUncheckedNext; + } + + s->scanFromEnd = fromEnd; + + if (s->numberOfKeys > 0) + { + memmove(s->keyData, + key, + s->numberOfKeys * sizeof(ScanKeyData)); + } + + p = (GISTScanOpaque) s->opaque; + if (p != (GISTScanOpaque) NULL) + { + gistfreestack(p->s_stack); + gistfreestack(p->s_markstk); + p->s_stack = p->s_markstk = (GISTSTACK *) NULL; + p->s_flags = 0x0; + for (i = 0; i < s->numberOfKeys; i++) + { + s->keyData[i].sk_procedure + = RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno, + s->keyData[i].sk_procedure); + s->keyData[i].sk_func = p->giststate->consistentFn; + } + } + else + { + /* initialize opaque data */ + p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData)); + p->s_stack = p->s_markstk = (GISTSTACK *) NULL; + p->s_flags = 0x0; + s->opaque = p; + p->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE)); + initGISTstate(p->giststate, s->relation); + if (s->numberOfKeys > 0) + + /* + * * Play games here with the scan key to use the Consistent * + * function for all comparisons: * 1) the sk_procedure field + * will now be used to hold the * strategy number * 2) the + * sk_func field will point to the Consistent function + */ + for (i = 0; i < s->numberOfKeys; i++) + { + + /* + * s->keyData[i].sk_procedure = + * index_getprocid(s->relation, 1, GIST_CONSISTENT_PROC); + */ + s->keyData[i].sk_procedure + = RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno, + s->keyData[i].sk_procedure); + s->keyData[i].sk_func = p->giststate->consistentFn; + } } - } else { - /* initialize opaque data */ - p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData)); - p->s_stack = p->s_markstk = (GISTSTACK *) NULL; - p->s_flags = 0x0; - s->opaque = p; - p->giststate = (GISTSTATE *)palloc(sizeof(GISTSTATE)); - initGISTstate(p->giststate, s->relation); - if (s->numberOfKeys > 0) - /* - ** Play games here with the scan key to use the Consistent - ** function for all comparisons: - ** 1) the sk_procedure field will now be used to hold the - ** strategy number - ** 2) the sk_func field will point to the Consistent function - */ - for (i = 0; i < s->numberOfKeys; i++) { - /* s->keyData[i].sk_procedure - = index_getprocid(s->relation, 1, GIST_CONSISTENT_PROC); */ - s->keyData[i].sk_procedure - = RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno, - s->keyData[i].sk_procedure); - s->keyData[i].sk_func = p->giststate->consistentFn; - } - } } void gistmarkpos(IndexScanDesc s) { - GISTScanOpaque p; - GISTSTACK *o, *n, *tmp; - - s->currentMarkData = s->currentItemData; - p = (GISTScanOpaque) s->opaque; - if (p->s_flags & GS_CURBEFORE) - p->s_flags |= GS_MRKBEFORE; - else - p->s_flags &= ~GS_MRKBEFORE; - - o = (GISTSTACK *) NULL; - n = p->s_stack; - - /* copy the parent stack from the current item data */ - while (n != (GISTSTACK *) NULL) { - tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); - tmp->gs_child = n->gs_child; - tmp->gs_blk = n->gs_blk; - tmp->gs_parent = o; - o = tmp; - n = n->gs_parent; - } - - gistfreestack(p->s_markstk); - p->s_markstk = o; + GISTScanOpaque p; + GISTSTACK *o, + *n, + *tmp; + + s->currentMarkData = s->currentItemData; + p = (GISTScanOpaque) s->opaque; + if (p->s_flags & GS_CURBEFORE) + p->s_flags |= GS_MRKBEFORE; + else + p->s_flags &= ~GS_MRKBEFORE; + + o = (GISTSTACK *) NULL; + n = p->s_stack; + + /* copy the parent stack from the current item data */ + while (n != (GISTSTACK *) NULL) + { + tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); + tmp->gs_child = n->gs_child; + tmp->gs_blk = n->gs_blk; + tmp->gs_parent = o; + o = tmp; + n = n->gs_parent; + } + + gistfreestack(p->s_markstk); + p->s_markstk = o; } void gistrestrpos(IndexScanDesc s) { - GISTScanOpaque p; - GISTSTACK *o, *n, *tmp; - - s->currentItemData = s->currentMarkData; - p = (GISTScanOpaque) s->opaque; - if (p->s_flags & GS_MRKBEFORE) - p->s_flags |= GS_CURBEFORE; - else - p->s_flags &= ~GS_CURBEFORE; - - o = (GISTSTACK *) NULL; - n = p->s_markstk; - - /* copy the parent stack from the current item data */ - while (n != (GISTSTACK *) NULL) { - tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); - tmp->gs_child = n->gs_child; - tmp->gs_blk = n->gs_blk; - tmp->gs_parent = o; - o = tmp; - n = n->gs_parent; - } - - gistfreestack(p->s_stack); - p->s_stack = o; + GISTScanOpaque p; + GISTSTACK *o, + *n, + *tmp; + + s->currentItemData = s->currentMarkData; + p = (GISTScanOpaque) s->opaque; + if (p->s_flags & GS_MRKBEFORE) + p->s_flags |= GS_CURBEFORE; + else + p->s_flags &= ~GS_CURBEFORE; + + o = (GISTSTACK *) NULL; + n = p->s_markstk; + + /* copy the parent stack from the current item data */ + while (n != (GISTSTACK *) NULL) + { + tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK)); + tmp->gs_child = n->gs_child; + tmp->gs_blk = n->gs_blk; + tmp->gs_parent = o; + o = tmp; + n = n->gs_parent; + } + + gistfreestack(p->s_stack); + p->s_stack = o; } void gistendscan(IndexScanDesc s) { - GISTScanOpaque p; - - p = (GISTScanOpaque) s->opaque; - - if (p != (GISTScanOpaque) NULL) { - gistfreestack(p->s_stack); - gistfreestack(p->s_markstk); - pfree (s->opaque); - } - - gistdropscan(s); - /* XXX don't unset read lock -- two-phase locking */ + GISTScanOpaque p; + + p = (GISTScanOpaque) s->opaque; + + if (p != (GISTScanOpaque) NULL) + { + gistfreestack(p->s_stack); + gistfreestack(p->s_markstk); + pfree(s->opaque); + } + + gistdropscan(s); + /* XXX don't unset read lock -- two-phase locking */ } static void gistregscan(IndexScanDesc s) { - GISTScanList l; - - l = (GISTScanList) palloc(sizeof(GISTScanListData)); - l->gsl_scan = s; - l->gsl_next = GISTScans; - GISTScans = l; + GISTScanList l; + + l = (GISTScanList) palloc(sizeof(GISTScanListData)); + l->gsl_scan = s; + l->gsl_next = GISTScans; + GISTScans = l; } static void gistdropscan(IndexScanDesc s) { - GISTScanList l; - GISTScanList prev; - - prev = (GISTScanList) NULL; - - for (l = GISTScans; - l != (GISTScanList) NULL && l->gsl_scan != s; - l = l->gsl_next) { - prev = l; - } - - if (l == (GISTScanList) NULL) - elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s); - - if (prev == (GISTScanList) NULL) - GISTScans = l->gsl_next; - else - prev->gsl_next = l->gsl_next; - - pfree(l); + GISTScanList l; + GISTScanList prev; + + prev = (GISTScanList) NULL; + + for (l = GISTScans; + l != (GISTScanList) NULL && l->gsl_scan != s; + l = l->gsl_next) + { + prev = l; + } + + if (l == (GISTScanList) NULL) + elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s); + + if (prev == (GISTScanList) NULL) + GISTScans = l->gsl_next; + else + prev->gsl_next = l->gsl_next; + + pfree(l); } void gistadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum) { - GISTScanList l; - Oid relid; - - relid = r->rd_id; - for (l = GISTScans; l != (GISTScanList) NULL; l = l->gsl_next) { - if (l->gsl_scan->relation->rd_id == relid) - gistadjone(l->gsl_scan, op, blkno, offnum); - } + GISTScanList l; + Oid relid; + + relid = r->rd_id; + for (l = GISTScans; l != (GISTScanList) NULL; l = l->gsl_next) + { + if (l->gsl_scan->relation->rd_id == relid) + gistadjone(l->gsl_scan, op, blkno, offnum); + } } /* - * gistadjone() -- adjust one scan for update. + * gistadjone() -- adjust one scan for update. * - * By here, the scan passed in is on a modified relation. Op tells - * us what the modification is, and blkno and offind tell us what - * block and offset index were affected. This routine checks the - * current and marked positions, and the current and marked stacks, - * to see if any stored location needs to be changed because of the - * update. If so, we make the change here. + * By here, the scan passed in is on a modified relation. Op tells + * us what the modification is, and blkno and offind tell us what + * block and offset index were affected. This routine checks the + * current and marked positions, and the current and marked stacks, + * to see if any stored location needs to be changed because of the + * update. If so, we make the change here. */ static void gistadjone(IndexScanDesc s, - int op, - BlockNumber blkno, - OffsetNumber offnum) + int op, + BlockNumber blkno, + OffsetNumber offnum) { - GISTScanOpaque so; - - adjustiptr(s, &(s->currentItemData), op, blkno, offnum); - adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); - - so = (GISTScanOpaque) s->opaque; - - if (op == GISTOP_SPLIT) { - adjuststack(so->s_stack, blkno, offnum); - adjuststack(so->s_markstk, blkno, offnum); - } + GISTScanOpaque so; + + adjustiptr(s, &(s->currentItemData), op, blkno, offnum); + adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); + + so = (GISTScanOpaque) s->opaque; + + if (op == GISTOP_SPLIT) + { + adjuststack(so->s_stack, blkno, offnum); + adjuststack(so->s_markstk, blkno, offnum); + } } /* - * adjustiptr() -- adjust current and marked item pointers in the scan + * adjustiptr() -- adjust current and marked item pointers in the scan * - * Depending on the type of update and the place it happened, we - * need to do nothing, to back up one record, or to start over on - * the same page. + * Depending on the type of update and the place it happened, we + * need to do nothing, to back up one record, or to start over on + * the same page. */ static void adjustiptr(IndexScanDesc s, - ItemPointer iptr, - int op, - BlockNumber blkno, - OffsetNumber offnum) + ItemPointer iptr, + int op, + BlockNumber blkno, + OffsetNumber offnum) { - OffsetNumber curoff; - GISTScanOpaque so; - - if (ItemPointerIsValid(iptr)) { - if (ItemPointerGetBlockNumber(iptr) == blkno) { - curoff = ItemPointerGetOffsetNumber(iptr); - so = (GISTScanOpaque) s->opaque; - - switch (op) { - case GISTOP_DEL: - /* back up one if we need to */ - if (curoff >= offnum) { - - if (curoff > FirstOffsetNumber) { - /* just adjust the item pointer */ - ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff)); - } else { - /* remember that we're before the current tuple */ - ItemPointerSet(iptr, blkno, FirstOffsetNumber); - if (iptr == &(s->currentItemData)) - so->s_flags |= GS_CURBEFORE; - else - so->s_flags |= GS_MRKBEFORE; - } + OffsetNumber curoff; + GISTScanOpaque so; + + if (ItemPointerIsValid(iptr)) + { + if (ItemPointerGetBlockNumber(iptr) == blkno) + { + curoff = ItemPointerGetOffsetNumber(iptr); + so = (GISTScanOpaque) s->opaque; + + switch (op) + { + case GISTOP_DEL: + /* back up one if we need to */ + if (curoff >= offnum) + { + + if (curoff > FirstOffsetNumber) + { + /* just adjust the item pointer */ + ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff)); + } + else + { + /* remember that we're before the current tuple */ + ItemPointerSet(iptr, blkno, FirstOffsetNumber); + if (iptr == &(s->currentItemData)) + so->s_flags |= GS_CURBEFORE; + else + so->s_flags |= GS_MRKBEFORE; + } + } + break; + + case GISTOP_SPLIT: + /* back to start of page on split */ + ItemPointerSet(iptr, blkno, FirstOffsetNumber); + if (iptr == &(s->currentItemData)) + so->s_flags &= ~GS_CURBEFORE; + else + so->s_flags &= ~GS_MRKBEFORE; + break; + + default: + elog(WARN, "Bad operation in GiST scan adjust: %d", op); + } } - break; - - case GISTOP_SPLIT: - /* back to start of page on split */ - ItemPointerSet(iptr, blkno, FirstOffsetNumber); - if (iptr == &(s->currentItemData)) - so->s_flags &= ~GS_CURBEFORE; - else - so->s_flags &= ~GS_MRKBEFORE; - break; - - default: - elog(WARN, "Bad operation in GiST scan adjust: %d", op); - } } - } } /* - * adjuststack() -- adjust the supplied stack for a split on a page in - * the index we're scanning. + * adjuststack() -- adjust the supplied stack for a split on a page in + * the index we're scanning. * - * If a page on our parent stack has split, we need to back up to the - * beginning of the page and rescan it. The reason for this is that - * the split algorithm for GiSTs doesn't order tuples in any useful - * way on a single page. This means on that a split, we may wind up - * looking at some heap tuples more than once. This is handled in the - * access method update code for heaps; if we've modified the tuple we - * are looking at already in this transaction, we ignore the update - * request. + * If a page on our parent stack has split, we need to back up to the + * beginning of the page and rescan it. The reason for this is that + * the split algorithm for GiSTs doesn't order tuples in any useful + * way on a single page. This means on that a split, we may wind up + * looking at some heap tuples more than once. This is handled in the + * access method update code for heaps; if we've modified the tuple we + * are looking at already in this transaction, we ignore the update + * request. */ /*ARGSUSED*/ static void -adjuststack(GISTSTACK *stk, - BlockNumber blkno, - OffsetNumber offnum) +adjuststack(GISTSTACK * stk, + BlockNumber blkno, + OffsetNumber offnum) { - while (stk != (GISTSTACK *) NULL) { - if (stk->gs_blk == blkno) - stk->gs_child = FirstOffsetNumber; - - stk = stk->gs_parent; - } + while (stk != (GISTSTACK *) NULL) + { + if (stk->gs_blk == blkno) + stk->gs_child = FirstOffsetNumber; + + stk = stk->gs_parent; + } } diff --git a/src/backend/access/gist/giststrat.c b/src/backend/access/gist/giststrat.c index 8c78ccec3ae..c7a6f9ff784 100644 --- a/src/backend/access/gist/giststrat.c +++ b/src/backend/access/gist/giststrat.c @@ -1,116 +1,117 @@ /*------------------------------------------------------------------------- * * giststrat.c-- - * strategy map data for GiSTs. + * strategy map data for GiSTs. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * /usr/local/devel/pglite/cvs/src/backend/access/gist/giststrat.c,v 1.4 1995/06/14 00:10:05 jolly Exp + * /usr/local/devel/pglite/cvs/src/backend/access/gist/giststrat.c,v 1.4 1995/06/14 00:10:05 jolly Exp * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/gist.h> #include <access/istrat.h> /* - * Note: negate, commute, and negatecommute all assume that operators are - * ordered as follows in the strategy map: + * Note: negate, commute, and negatecommute all assume that operators are + * ordered as follows in the strategy map: * - * contains, contained-by + * contains, contained-by * - * The negate, commute, and negatecommute arrays are used by the planner - * to plan indexed scans over data that appears in the qualificiation in - * a boolean negation, or whose operands appear in the wrong order. For - * example, if the operator "<%" means "contains", and the user says + * The negate, commute, and negatecommute arrays are used by the planner + * to plan indexed scans over data that appears in the qualificiation in + * a boolean negation, or whose operands appear in the wrong order. For + * example, if the operator "<%" means "contains", and the user says * - * where not rel.box <% "(10,10,20,20)"::box + * where not rel.box <% "(10,10,20,20)"::box * - * the planner can plan an index scan by noting that GiST indices have - * an operator in their operator class for negating <%. + * the planner can plan an index scan by noting that GiST indices have + * an operator in their operator class for negating <%. * - * Similarly, if the user says something like + * Similarly, if the user says something like * - * where "(10,10,20,20)"::box <% rel.box + * where "(10,10,20,20)"::box <% rel.box * - * the planner can see that the GiST index on rel.box has an operator in - * its opclass for commuting <%, and plan the scan using that operator. - * This added complexity in the access methods makes the planner a lot easier - * to write. + * the planner can see that the GiST index on rel.box has an operator in + * its opclass for commuting <%, and plan the scan using that operator. + * This added complexity in the access methods makes the planner a lot easier + * to write. */ /* if a op b, what operator tells us if (not a op b)? */ -static StrategyNumber GISTNegate[GISTNStrategies] = { - InvalidStrategy, - InvalidStrategy, - InvalidStrategy - }; +static StrategyNumber GISTNegate[GISTNStrategies] = { + InvalidStrategy, + InvalidStrategy, + InvalidStrategy +}; /* if a op_1 b, what is the operator op_2 such that b op_2 a? */ -static StrategyNumber GISTCommute[GISTNStrategies] = { - InvalidStrategy, - InvalidStrategy, - InvalidStrategy - }; +static StrategyNumber GISTCommute[GISTNStrategies] = { + InvalidStrategy, + InvalidStrategy, + InvalidStrategy +}; /* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */ -static StrategyNumber GISTNegateCommute[GISTNStrategies] = { - InvalidStrategy, - InvalidStrategy, - InvalidStrategy - }; +static StrategyNumber GISTNegateCommute[GISTNStrategies] = { + InvalidStrategy, + InvalidStrategy, + InvalidStrategy +}; /* - * GiSTs do not currently support TermData (see rtree/rtstrat.c for + * GiSTs do not currently support TermData (see rtree/rtstrat.c for * discussion of * TermData) -- such logic must be encoded in the user's Consistent function. */ /* - * If you were sufficiently attentive to detail, you would go through - * the ExpressionData pain above for every one of the strategies - * we defined. I am not. Now we declare the StrategyEvaluationData - * structure that gets shipped around to help the planner and the access - * method decide what sort of scan it should do, based on (a) what the - * user asked for, (b) what operators are defined for a particular opclass, - * and (c) the reams of information we supplied above. + * If you were sufficiently attentive to detail, you would go through + * the ExpressionData pain above for every one of the strategies + * we defined. I am not. Now we declare the StrategyEvaluationData + * structure that gets shipped around to help the planner and the access + * method decide what sort of scan it should do, based on (a) what the + * user asked for, (b) what operators are defined for a particular opclass, + * and (c) the reams of information we supplied above. * - * The idea of all of this initialized data is to make life easier on the - * user when he defines a new operator class to use this access method. - * By filling in all the data, we let him get away with leaving holes in his - * operator class, and still let him use the index. The added complexity - * in the access methods just isn't worth the trouble, though. + * The idea of all of this initialized data is to make life easier on the + * user when he defines a new operator class to use this access method. + * By filling in all the data, we let him get away with leaving holes in his + * operator class, and still let him use the index. The added complexity + * in the access methods just isn't worth the trouble, though. */ static StrategyEvaluationData GISTEvaluationData = { - GISTNStrategies, /* # of strategies */ - (StrategyTransformMap) GISTNegate, /* how to do (not qual) */ - (StrategyTransformMap) GISTCommute, /* how to swap operands */ - (StrategyTransformMap) GISTNegateCommute, /* how to do both */ - { NULL } + GISTNStrategies, /* # of strategies */ + (StrategyTransformMap) GISTNegate, /* how to do (not qual) */ + (StrategyTransformMap) GISTCommute, /* how to swap operands */ + (StrategyTransformMap) GISTNegateCommute, /* how to do both */ + {NULL} }; StrategyNumber RelationGetGISTStrategy(Relation r, - AttrNumber attnum, - RegProcedure proc) + AttrNumber attnum, + RegProcedure proc) { - return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc)); + return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc)); } #ifdef NOT_USED bool RelationInvokeGISTStrategy(Relation r, - AttrNumber attnum, - StrategyNumber s, - Datum left, - Datum right) + AttrNumber attnum, + StrategyNumber s, + Datum left, + Datum right) { - return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s, - left, right)); + return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s, + left, right)); } + #endif diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index 89f81fc56a5..e13539c4ad9 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -1,16 +1,16 @@ /*------------------------------------------------------------------------- * * hash.c-- - * Implementation of Margo Seltzer's Hashing package for postgres. + * Implementation of Margo Seltzer's Hashing package for postgres. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.12 1997/01/10 09:46:13 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.13 1997/09/07 04:37:49 momjian Exp $ * * NOTES - * This file contains only the public interface routines. + * This file contains only the public interface routines. * *------------------------------------------------------------------------- */ @@ -26,452 +26,483 @@ #include <miscadmin.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -bool BuildingHash = false; +bool BuildingHash = false; /* - * hashbuild() -- build a new hash index. + * hashbuild() -- build a new hash index. * - * We use a global variable to record the fact that we're creating - * a new index. This is used to avoid high-concurrency locking, - * since the index won't be visible until this transaction commits - * and since building is guaranteed to be single-threaded. + * We use a global variable to record the fact that we're creating + * a new index. This is used to avoid high-concurrency locking, + * since the index won't be visible until this transaction commits + * and since building is guaranteed to be single-threaded. */ void hashbuild(Relation heap, - Relation index, - int natts, - AttrNumber *attnum, - IndexStrategy istrat, - uint16 pcount, - Datum *params, - FuncIndexInfo *finfo, - PredInfo *predInfo) + Relation index, + int natts, + AttrNumber * attnum, + IndexStrategy istrat, + uint16 pcount, + Datum * params, + FuncIndexInfo * finfo, + PredInfo * predInfo) { - HeapScanDesc hscan; - Buffer buffer; - HeapTuple htup; - IndexTuple itup; - TupleDesc htupdesc, itupdesc; - Datum *attdata; - bool *nulls; - InsertIndexResult res; - int nhtups, nitups; - int i; - HashItem hitem; + HeapScanDesc hscan; + Buffer buffer; + HeapTuple htup; + IndexTuple itup; + TupleDesc htupdesc, + itupdesc; + Datum *attdata; + bool *nulls; + InsertIndexResult res; + int nhtups, + nitups; + int i; + HashItem hitem; + #ifndef OMIT_PARTIAL_INDEX - ExprContext *econtext; - TupleTable tupleTable; - TupleTableSlot *slot; + ExprContext *econtext; + TupleTable tupleTable; + TupleTableSlot *slot; + #endif - Oid hrelid, irelid; - Node *pred, *oldPred; - - /* note that this is a new btree */ - BuildingHash = true; - - pred = predInfo->pred; - oldPred = predInfo->oldPred; - - /* initialize the hash index metadata page (if this is a new index) */ - if (oldPred == NULL) - _hash_metapinit(index); - - /* get tuple descriptors for heap and index relations */ - htupdesc = RelationGetTupleDescriptor(heap); - itupdesc = RelationGetTupleDescriptor(index); - - /* get space for data items that'll appear in the index tuple */ - attdata = (Datum *) palloc(natts * sizeof(Datum)); - nulls = (bool *) palloc(natts * sizeof(bool)); - - /* - * If this is a predicate (partial) index, we will need to evaluate the - * predicate using ExecQual, which requires the current tuple to be in a - * slot of a TupleTable. In addition, ExecQual must have an ExprContext - * referring to that slot. Here, we initialize dummy TupleTable and - * ExprContext objects for this purpose. --Nels, Feb '92 - */ + Oid hrelid, + irelid; + Node *pred, + *oldPred; + + /* note that this is a new btree */ + BuildingHash = true; + + pred = predInfo->pred; + oldPred = predInfo->oldPred; + + /* initialize the hash index metadata page (if this is a new index) */ + if (oldPred == NULL) + _hash_metapinit(index); + + /* get tuple descriptors for heap and index relations */ + htupdesc = RelationGetTupleDescriptor(heap); + itupdesc = RelationGetTupleDescriptor(index); + + /* get space for data items that'll appear in the index tuple */ + attdata = (Datum *) palloc(natts * sizeof(Datum)); + nulls = (bool *) palloc(natts * sizeof(bool)); + + /* + * If this is a predicate (partial) index, we will need to evaluate + * the predicate using ExecQual, which requires the current tuple to + * be in a slot of a TupleTable. In addition, ExecQual must have an + * ExprContext referring to that slot. Here, we initialize dummy + * TupleTable and ExprContext objects for this purpose. --Nels, Feb + * '92 + */ #ifndef OMIT_PARTIAL_INDEX - if (pred != NULL || oldPred != NULL) { - tupleTable = ExecCreateTupleTable(1); - slot = ExecAllocTableSlot(tupleTable); - econtext = makeNode(ExprContext); - FillDummyExprContext(econtext, slot, htupdesc, buffer); - } - else /* quiet the compiler */ + if (pred != NULL || oldPred != NULL) + { + tupleTable = ExecCreateTupleTable(1); + slot = ExecAllocTableSlot(tupleTable); + econtext = makeNode(ExprContext); + FillDummyExprContext(econtext, slot, htupdesc, buffer); + } + else +/* quiet the compiler */ { econtext = NULL; tupleTable = 0; slot = 0; } -#endif /* OMIT_PARTIAL_INDEX */ - - /* start a heap scan */ - hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL); - htup = heap_getnext(hscan, 0, &buffer); - - /* build the index */ - nhtups = nitups = 0; - - for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer)) { - - nhtups++; - - /* - * If oldPred != NULL, this is an EXTEND INDEX command, so skip - * this tuple if it was already in the existing partial index - */ - if (oldPred != NULL) { - /*SetSlotContents(slot, htup); */ +#endif /* OMIT_PARTIAL_INDEX */ + + /* start a heap scan */ + hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL); + htup = heap_getnext(hscan, 0, &buffer); + + /* build the index */ + nhtups = nitups = 0; + + for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer)) + { + + nhtups++; + + /* + * If oldPred != NULL, this is an EXTEND INDEX command, so skip + * this tuple if it was already in the existing partial index + */ + if (oldPred != NULL) + { + /* SetSlotContents(slot, htup); */ #ifndef OMIT_PARTIAL_INDEX - slot->val = htup; - if (ExecQual((List*)oldPred, econtext) == true) { + slot->val = htup; + if (ExecQual((List *) oldPred, econtext) == true) + { + nitups++; + continue; + } +#endif /* OMIT_PARTIAL_INDEX */ + } + + /* + * Skip this tuple if it doesn't satisfy the partial-index + * predicate + */ + if (pred != NULL) + { +#ifndef OMIT_PARTIAL_INDEX + /* SetSlotContents(slot, htup); */ + slot->val = htup; + if (ExecQual((List *) pred, econtext) == false) + continue; +#endif /* OMIT_PARTIAL_INDEX */ + } + nitups++; - continue; - } -#endif /* OMIT_PARTIAL_INDEX */ + + /* + * For the current heap tuple, extract all the attributes we use + * in this index, and note which are null. + */ + for (i = 1; i <= natts; i++) + { + int attoff; + bool attnull; + + /* + * Offsets are from the start of the tuple, and are + * zero-based; indices are one-based. The next call returns i + * - 1. That's data hiding for you. + */ + + /* attoff = i - 1 */ + attoff = AttrNumberGetAttrOffset(i); + + /* + * below, attdata[attoff] set to equal some datum & attnull is + * changed to indicate whether or not the attribute is null + * for this tuple + */ + attdata[attoff] = GetIndexValue(htup, + htupdesc, + attoff, + attnum, + finfo, + &attnull, + buffer); + nulls[attoff] = (attnull ? 'n' : ' '); + } + + /* form an index tuple and point it at the heap tuple */ + itup = index_formtuple(itupdesc, attdata, nulls); + + /* + * If the single index key is null, we don't insert it into the + * index. Hash tables support scans on '='. Relational algebra + * says that A = B returns null if either A or B is null. This + * means that no qualification used in an index scan could ever + * return true on a null attribute. It also means that indices + * can't be used by ISNULL or NOTNULL scans, but that's an + * artifact of the strategy map architecture chosen in 1986, not + * of the way nulls are handled here. + */ + + if (itup->t_info & INDEX_NULL_MASK) + { + pfree(itup); + continue; + } + + itup->t_tid = htup->t_ctid; + hitem = _hash_formitem(itup); + res = _hash_doinsert(index, hitem); + pfree(hitem); + pfree(itup); + pfree(res); } - - /* Skip this tuple if it doesn't satisfy the partial-index predicate */ - if (pred != NULL) { + + /* okay, all heap tuples are indexed */ + heap_endscan(hscan); + + if (pred != NULL || oldPred != NULL) + { #ifndef OMIT_PARTIAL_INDEX - /*SetSlotContents(slot, htup); */ - slot->val = htup; - if (ExecQual((List*)pred, econtext) == false) - continue; -#endif /* OMIT_PARTIAL_INDEX */ -} - - nitups++; - - /* - * For the current heap tuple, extract all the attributes - * we use in this index, and note which are null. - */ - for (i = 1; i <= natts; i++) { - int attoff; - bool attnull; - - /* - * Offsets are from the start of the tuple, and are - * zero-based; indices are one-based. The next call - * returns i - 1. That's data hiding for you. - */ - - /* attoff = i - 1 */ - attoff = AttrNumberGetAttrOffset(i); - - /* below, attdata[attoff] set to equal some datum & - * attnull is changed to indicate whether or not the attribute - * is null for this tuple - */ - attdata[attoff] = GetIndexValue(htup, - htupdesc, - attoff, - attnum, - finfo, - &attnull, - buffer); - nulls[attoff] = (attnull ? 'n' : ' '); + ExecDestroyTupleTable(tupleTable, true); + pfree(econtext); +#endif /* OMIT_PARTIAL_INDEX */ } - - /* form an index tuple and point it at the heap tuple */ - itup = index_formtuple(itupdesc, attdata, nulls); - + /* - * If the single index key is null, we don't insert it into - * the index. Hash tables support scans on '='. - * Relational algebra says that A = B - * returns null if either A or B is null. This - * means that no qualification used in an index scan could ever - * return true on a null attribute. It also means that indices - * can't be used by ISNULL or NOTNULL scans, but that's an - * artifact of the strategy map architecture chosen in 1986, not - * of the way nulls are handled here. + * Since we just counted the tuples in the heap, we update its stats + * in pg_class to guarantee that the planner takes advantage of the + * index we just created. Finally, only update statistics during + * normal index definitions, not for indices on system catalogs + * created during bootstrap processing. We must close the relations + * before updatings statistics to guarantee that the relcache entries + * are flushed when we increment the command counter in UpdateStats(). */ - - if (itup->t_info & INDEX_NULL_MASK) { - pfree(itup); - continue; - } - - itup->t_tid = htup->t_ctid; - hitem = _hash_formitem(itup); - res = _hash_doinsert(index, hitem); - pfree(hitem); - pfree(itup); - pfree(res); - } - - /* okay, all heap tuples are indexed */ - heap_endscan(hscan); - - if (pred != NULL || oldPred != NULL) { -#ifndef OMIT_PARTIAL_INDEX - ExecDestroyTupleTable(tupleTable, true); - pfree(econtext); -#endif /* OMIT_PARTIAL_INDEX */ - } - - /* - * Since we just counted the tuples in the heap, we update its - * stats in pg_class to guarantee that the planner takes advantage - * of the index we just created. Finally, only update statistics - * during normal index definitions, not for indices on system catalogs - * created during bootstrap processing. We must close the relations - * before updatings statistics to guarantee that the relcache entries - * are flushed when we increment the command counter in UpdateStats(). - */ - if (IsNormalProcessingMode()) + if (IsNormalProcessingMode()) { - hrelid = heap->rd_id; - irelid = index->rd_id; - heap_close(heap); - index_close(index); - UpdateStats(hrelid, nhtups, true); - UpdateStats(irelid, nitups, false); - if (oldPred != NULL) { - if (nitups == nhtups) pred = NULL; - UpdateIndexPredicate(irelid, oldPred, pred); - } + hrelid = heap->rd_id; + irelid = index->rd_id; + heap_close(heap); + index_close(index); + UpdateStats(hrelid, nhtups, true); + UpdateStats(irelid, nitups, false); + if (oldPred != NULL) + { + if (nitups == nhtups) + pred = NULL; + UpdateIndexPredicate(irelid, oldPred, pred); + } } - - /* be tidy */ - pfree(nulls); - pfree(attdata); - - /* all done */ - BuildingHash = false; + + /* be tidy */ + pfree(nulls); + pfree(attdata); + + /* all done */ + BuildingHash = false; } /* - * hashinsert() -- insert an index tuple into a hash table. + * hashinsert() -- insert an index tuple into a hash table. * - * Hash on the index tuple's key, find the appropriate location - * for the new tuple, put it there, and return an InsertIndexResult - * to the caller. + * Hash on the index tuple's key, find the appropriate location + * for the new tuple, put it there, and return an InsertIndexResult + * to the caller. */ InsertIndexResult -hashinsert(Relation rel, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) +hashinsert(Relation rel, Datum * datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) { - HashItem hitem; - IndexTuple itup; - InsertIndexResult res; - - - /* generate an index tuple */ - itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls); - itup->t_tid = *ht_ctid; - - if (itup->t_info & INDEX_NULL_MASK) - return ((InsertIndexResult) NULL); - - hitem = _hash_formitem(itup); - - res = _hash_doinsert(rel, hitem); - - pfree(hitem); - pfree(itup); - - return (res); + HashItem hitem; + IndexTuple itup; + InsertIndexResult res; + + + /* generate an index tuple */ + itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls); + itup->t_tid = *ht_ctid; + + if (itup->t_info & INDEX_NULL_MASK) + return ((InsertIndexResult) NULL); + + hitem = _hash_formitem(itup); + + res = _hash_doinsert(rel, hitem); + + pfree(hitem); + pfree(itup); + + return (res); } /* - * hashgettuple() -- Get the next tuple in the scan. + * hashgettuple() -- Get the next tuple in the scan. */ -char * +char * hashgettuple(IndexScanDesc scan, ScanDirection dir) { - RetrieveIndexResult res; - - /* - * If we've already initialized this scan, we can just advance it - * in the appropriate direction. If we haven't done so yet, we - * call a routine to get the first item in the scan. - */ - - if (ItemPointerIsValid(&(scan->currentItemData))) - res = _hash_next(scan, dir); - else - res = _hash_first(scan, dir); - - return ((char *) res); + RetrieveIndexResult res; + + /* + * If we've already initialized this scan, we can just advance it in + * the appropriate direction. If we haven't done so yet, we call a + * routine to get the first item in the scan. + */ + + if (ItemPointerIsValid(&(scan->currentItemData))) + res = _hash_next(scan, dir); + else + res = _hash_first(scan, dir); + + return ((char *) res); } /* - * hashbeginscan() -- start a scan on a hash index + * hashbeginscan() -- start a scan on a hash index */ -char * +char * hashbeginscan(Relation rel, - bool fromEnd, - uint16 keysz, - ScanKey scankey) + bool fromEnd, + uint16 keysz, + ScanKey scankey) { - IndexScanDesc scan; - HashScanOpaque so; - - scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey); - so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData)); - so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer; - scan->opaque = so; - scan->flags = 0x0; - - /* register scan in case we change pages it's using */ - _hash_regscan(scan); - - return ((char *) scan); + IndexScanDesc scan; + HashScanOpaque so; + + scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey); + so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData)); + so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer; + scan->opaque = so; + scan->flags = 0x0; + + /* register scan in case we change pages it's using */ + _hash_regscan(scan); + + return ((char *) scan); } /* - * hashrescan() -- rescan an index relation + * hashrescan() -- rescan an index relation */ void hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey) { - ItemPointer iptr; - HashScanOpaque so; - - so = (HashScanOpaque) scan->opaque; - - /* we hold a read lock on the current page in the scan */ - if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { - _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); - so->hashso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { - _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); - so->hashso_mrkbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - /* reset the scan key */ - if (scan->numberOfKeys > 0) { - memmove(scan->keyData, - scankey, - scan->numberOfKeys * sizeof(ScanKeyData)); - } + ItemPointer iptr; + HashScanOpaque so; + + so = (HashScanOpaque) scan->opaque; + + /* we hold a read lock on the current page in the scan */ + if (ItemPointerIsValid(iptr = &(scan->currentItemData))) + { + _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); + so->hashso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) + { + _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); + so->hashso_mrkbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + /* reset the scan key */ + if (scan->numberOfKeys > 0) + { + memmove(scan->keyData, + scankey, + scan->numberOfKeys * sizeof(ScanKeyData)); + } } /* - * hashendscan() -- close down a scan + * hashendscan() -- close down a scan */ void hashendscan(IndexScanDesc scan) { - - ItemPointer iptr; - HashScanOpaque so; - - so = (HashScanOpaque) scan->opaque; - - /* release any locks we still hold */ - if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { - _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); - so->hashso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { - if (BufferIsValid(so->hashso_mrkbuf)) - _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); - so->hashso_mrkbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - /* don't need scan registered anymore */ - _hash_dropscan(scan); - - /* be tidy */ - pfree (scan->opaque); + + ItemPointer iptr; + HashScanOpaque so; + + so = (HashScanOpaque) scan->opaque; + + /* release any locks we still hold */ + if (ItemPointerIsValid(iptr = &(scan->currentItemData))) + { + _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); + so->hashso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) + { + if (BufferIsValid(so->hashso_mrkbuf)) + _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); + so->hashso_mrkbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + /* don't need scan registered anymore */ + _hash_dropscan(scan); + + /* be tidy */ + pfree(scan->opaque); } /* - * hashmarkpos() -- save current scan position + * hashmarkpos() -- save current scan position * */ void hashmarkpos(IndexScanDesc scan) { - ItemPointer iptr; - HashScanOpaque so; - - /* see if we ever call this code. if we do, then so_mrkbuf a - * useful element in the scan->opaque structure. if this procedure - * is never called, so_mrkbuf should be removed from the scan->opaque - * structure. - */ - elog(NOTICE, "Hashmarkpos() called."); - - so = (HashScanOpaque) scan->opaque; - - /* release lock on old marked data, if any */ - if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { - _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); - so->hashso_mrkbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - /* bump lock on currentItemData and copy to currentMarkData */ - if (ItemPointerIsValid(&(scan->currentItemData))) { - so->hashso_mrkbuf = _hash_getbuf(scan->relation, - BufferGetBlockNumber(so->hashso_curbuf), - HASH_READ); - scan->currentMarkData = scan->currentItemData; - } + ItemPointer iptr; + HashScanOpaque so; + + /* + * see if we ever call this code. if we do, then so_mrkbuf a useful + * element in the scan->opaque structure. if this procedure is never + * called, so_mrkbuf should be removed from the scan->opaque + * structure. + */ + elog(NOTICE, "Hashmarkpos() called."); + + so = (HashScanOpaque) scan->opaque; + + /* release lock on old marked data, if any */ + if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) + { + _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ); + so->hashso_mrkbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + /* bump lock on currentItemData and copy to currentMarkData */ + if (ItemPointerIsValid(&(scan->currentItemData))) + { + so->hashso_mrkbuf = _hash_getbuf(scan->relation, + BufferGetBlockNumber(so->hashso_curbuf), + HASH_READ); + scan->currentMarkData = scan->currentItemData; + } } /* - * hashrestrpos() -- restore scan to last saved position + * hashrestrpos() -- restore scan to last saved position */ void hashrestrpos(IndexScanDesc scan) { - ItemPointer iptr; - HashScanOpaque so; - - /* see if we ever call this code. if we do, then so_mrkbuf a - * useful element in the scan->opaque structure. if this procedure - * is never called, so_mrkbuf should be removed from the scan->opaque - * structure. - */ - elog(NOTICE, "Hashrestrpos() called."); - - so = (HashScanOpaque) scan->opaque; - - /* release lock on current data, if any */ - if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { - _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); - so->hashso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - /* bump lock on currentMarkData and copy to currentItemData */ - if (ItemPointerIsValid(&(scan->currentMarkData))) { - so->hashso_curbuf = - _hash_getbuf(scan->relation, - BufferGetBlockNumber(so->hashso_mrkbuf), - HASH_READ); - - scan->currentItemData = scan->currentMarkData; - } + ItemPointer iptr; + HashScanOpaque so; + + /* + * see if we ever call this code. if we do, then so_mrkbuf a useful + * element in the scan->opaque structure. if this procedure is never + * called, so_mrkbuf should be removed from the scan->opaque + * structure. + */ + elog(NOTICE, "Hashrestrpos() called."); + + so = (HashScanOpaque) scan->opaque; + + /* release lock on current data, if any */ + if (ItemPointerIsValid(iptr = &(scan->currentItemData))) + { + _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ); + so->hashso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + /* bump lock on currentMarkData and copy to currentItemData */ + if (ItemPointerIsValid(&(scan->currentMarkData))) + { + so->hashso_curbuf = + _hash_getbuf(scan->relation, + BufferGetBlockNumber(so->hashso_mrkbuf), + HASH_READ); + + scan->currentItemData = scan->currentMarkData; + } } /* stubs */ void hashdelete(Relation rel, ItemPointer tid) { - /* adjust any active scans that will be affected by this deletion */ - _hash_adjscans(rel, tid); - - /* delete the data from the page */ - _hash_pagedel(rel, tid); -} + /* adjust any active scans that will be affected by this deletion */ + _hash_adjscans(rel, tid); + /* delete the data from the page */ + _hash_pagedel(rel, tid); +} diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c index 5862800b21d..a3cbaa1a94c 100644 --- a/src/backend/access/hash/hashfunc.c +++ b/src/backend/access/hash/hashfunc.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * hashfunc.c-- - * Comparison functions for hash access method. + * Comparison functions for hash access method. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.3 1996/11/10 02:57:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.4 1997/09/07 04:37:53 momjian Exp $ * * NOTES - * These functions are stored in pg_amproc. For each operator class - * defined on hash tables, they compute the hash value of the argument. + * These functions are stored in pg_amproc. For each operator class + * defined on hash tables, they compute the hash value of the argument. * *------------------------------------------------------------------------- */ @@ -20,206 +20,223 @@ #include "access/hash.h" -uint32 hashint2(int16 key) +uint32 +hashint2(int16 key) { - return ((uint32) ~key); + return ((uint32) ~ key); } -uint32 hashint4(uint32 key) +uint32 +hashint4(uint32 key) { - return (~key); + return (~key); } /* Hash function from Chris Torek. */ -uint32 hashfloat4(float32 keyp) +uint32 +hashfloat4(float32 keyp) { - int len; - int loop; - uint32 h; - char *kp = (char *) keyp; + int len; + int loop; + uint32 h; + char *kp = (char *) keyp; - len = sizeof(float32data); + len = sizeof(float32data); -#define HASH4a h = (h << 5) - h + *kp++; -#define HASH4b h = (h << 5) + h + *kp++; +#define HASH4a h = (h << 5) - h + *kp++; +#define HASH4b h = (h << 5) + h + *kp++; #define HASH4 HASH4b - h = 0; - if (len > 0) { - loop = (len + 8 - 1) >> 3; - - switch (len & (8 - 1)) { - case 0: - do { /* All fall throughs */ - HASH4; - case 7: - HASH4; - case 6: - HASH4; - case 5: - HASH4; - case 4: - HASH4; - case 3: - HASH4; - case 2: - HASH4; - case 1: - HASH4; - } while (--loop); + h = 0; + if (len > 0) + { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) + { + case 0: + do + { /* All fall throughs */ + HASH4; + case 7: + HASH4; + case 6: + HASH4; + case 5: + HASH4; + case 4: + HASH4; + case 3: + HASH4; + case 2: + HASH4; + case 1: + HASH4; + } while (--loop); + } } - } - return (h); -} + return (h); +} -uint32 hashfloat8(float64 keyp) +uint32 +hashfloat8(float64 keyp) { - int len; - int loop; - uint32 h; - char *kp = (char *) keyp; + int len; + int loop; + uint32 h; + char *kp = (char *) keyp; - len = sizeof(float64data); + len = sizeof(float64data); -#define HASH4a h = (h << 5) - h + *kp++; -#define HASH4b h = (h << 5) + h + *kp++; +#define HASH4a h = (h << 5) - h + *kp++; +#define HASH4b h = (h << 5) + h + *kp++; #define HASH4 HASH4b - h = 0; - if (len > 0) { - loop = (len + 8 - 1) >> 3; - - switch (len & (8 - 1)) { - case 0: - do { /* All fall throughs */ - HASH4; - case 7: - HASH4; - case 6: - HASH4; - case 5: - HASH4; - case 4: - HASH4; - case 3: - HASH4; - case 2: - HASH4; - case 1: - HASH4; - } while (--loop); + h = 0; + if (len > 0) + { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) + { + case 0: + do + { /* All fall throughs */ + HASH4; + case 7: + HASH4; + case 6: + HASH4; + case 5: + HASH4; + case 4: + HASH4; + case 3: + HASH4; + case 2: + HASH4; + case 1: + HASH4; + } while (--loop); + } } - } - return (h); -} + return (h); +} -uint32 hashoid(Oid key) +uint32 +hashoid(Oid key) { - return ((uint32) ~key); + return ((uint32) ~ key); } -uint32 hashchar(char key) +uint32 +hashchar(char key) { - int len; - uint32 h; + int len; + uint32 h; + + len = sizeof(char); - len = sizeof(char); +#define PRIME1 37 +#define PRIME2 1048583 -#define PRIME1 37 -#define PRIME2 1048583 + h = 0; + /* Convert char to integer */ + h = h * PRIME1 ^ (key - ' '); + h %= PRIME2; - h = 0; - /* Convert char to integer */ - h = h * PRIME1 ^ (key - ' '); - h %= PRIME2; - - return (h); + return (h); } -uint32 hashchar2(uint16 intkey) +uint32 +hashchar2(uint16 intkey) { - uint32 h; - int len; - char *key = (char *) &intkey; - - h = 0; - len = sizeof(uint16); - /* Convert string to integer */ - while (len--) - h = h * PRIME1 ^ (*key++ - ' '); - h %= PRIME2; - - return (h); + uint32 h; + int len; + char *key = (char *) &intkey; + + h = 0; + len = sizeof(uint16); + /* Convert string to integer */ + while (len--) + h = h * PRIME1 ^ (*key++ - ' '); + h %= PRIME2; + + return (h); } -uint32 hashchar4(uint32 intkey) +uint32 +hashchar4(uint32 intkey) { - uint32 h; - int len; - char *key = (char *) &intkey; - - h = 0; - len = sizeof(uint32); - /* Convert string to integer */ - while (len--) - h = h * PRIME1 ^ (*key++ - ' '); - h %= PRIME2; - - return (h); + uint32 h; + int len; + char *key = (char *) &intkey; + + h = 0; + len = sizeof(uint32); + /* Convert string to integer */ + while (len--) + h = h * PRIME1 ^ (*key++ - ' '); + h %= PRIME2; + + return (h); } -uint32 hashchar8(char *key) +uint32 +hashchar8(char *key) { - uint32 h; - int len; - - h = 0; - len = sizeof(char8); - /* Convert string to integer */ - while (len--) - h = h * PRIME1 ^ (*key++ - ' '); - h %= PRIME2; - - return (h); + uint32 h; + int len; + + h = 0; + len = sizeof(char8); + /* Convert string to integer */ + while (len--) + h = h * PRIME1 ^ (*key++ - ' '); + h %= PRIME2; + + return (h); } -uint32 hashname(NameData *n) +uint32 +hashname(NameData * n) { - uint32 h; - int len; - char *key; - - key = n->data; - - h = 0; - len = NAMEDATALEN; - /* Convert string to integer */ - while (len--) - h = h * PRIME1 ^ (*key++ - ' '); - h %= PRIME2; - - return (h); + uint32 h; + int len; + char *key; + + key = n->data; + + h = 0; + len = NAMEDATALEN; + /* Convert string to integer */ + while (len--) + h = h * PRIME1 ^ (*key++ - ' '); + h %= PRIME2; + + return (h); } -uint32 hashchar16(char *key) +uint32 +hashchar16(char *key) { - uint32 h; - int len; - - h = 0; - len = sizeof(char16); - /* Convert string to integer */ - while (len--) - h = h * PRIME1 ^ (*key++ - ' '); - h %= PRIME2; - - return (h); + uint32 h; + int len; + + h = 0; + len = sizeof(char16); + /* Convert string to integer */ + while (len--) + h = h * PRIME1 ^ (*key++ - ' '); + h %= PRIME2; + + return (h); } @@ -234,45 +251,49 @@ uint32 hashchar16(char *key) * * "OZ's original sdbm hash" */ -uint32 hashtext(struct varlena *key) +uint32 +hashtext(struct varlena * key) { - int keylen; - char *keydata; - uint32 n; - int loop; - - keydata = VARDATA(key); - keylen = VARSIZE(key); - - /* keylen includes the four bytes in which string keylength is stored */ - keylen -= sizeof(VARSIZE(key)); - -#define HASHC n = *keydata++ + 65599 * n - - n = 0; - if (keylen > 0) { - loop = (keylen + 8 - 1) >> 3; - - switch (keylen & (8 - 1)) { - case 0: - do { /* All fall throughs */ - HASHC; - case 7: - HASHC; - case 6: - HASHC; - case 5: - HASHC; - case 4: - HASHC; - case 3: - HASHC; - case 2: - HASHC; - case 1: - HASHC; - } while (--loop); + int keylen; + char *keydata; + uint32 n; + int loop; + + keydata = VARDATA(key); + keylen = VARSIZE(key); + + /* keylen includes the four bytes in which string keylength is stored */ + keylen -= sizeof(VARSIZE(key)); + +#define HASHC n = *keydata++ + 65599 * n + + n = 0; + if (keylen > 0) + { + loop = (keylen + 8 - 1) >> 3; + + switch (keylen & (8 - 1)) + { + case 0: + do + { /* All fall throughs */ + HASHC; + case 7: + HASHC; + case 6: + HASHC; + case 5: + HASHC; + case 4: + HASHC; + case 3: + HASHC; + case 2: + HASHC; + case 1: + HASHC; + } while (--loop); + } } - } - return (n); -} + return (n); +} diff --git a/src/backend/access/hash/hashinsert.c b/src/backend/access/hash/hashinsert.c index f1233c68b2d..4829093589a 100644 --- a/src/backend/access/hash/hashinsert.c +++ b/src/backend/access/hash/hashinsert.c @@ -1,19 +1,19 @@ /*------------------------------------------------------------------------- * * hashinsert.c-- - * Item insertion in hash tables for Postgres. + * Item insertion in hash tables for Postgres. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/hashinsert.c,v 1.8 1997/08/12 22:51:30 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashinsert.c,v 1.9 1997/09/07 04:37:56 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/hash.h> #include <storage/bufmgr.h> #include <utils/memutils.h> @@ -22,211 +22,221 @@ static InsertIndexResult _hash_insertonpg(Relation rel, Buffer buf, int keysz, S static OffsetNumber _hash_pgaddtup(Relation rel, Buffer buf, int keysz, ScanKey itup_scankey, Size itemsize, HashItem hitem); /* - * _hash_doinsert() -- Handle insertion of a single HashItem in the table. + * _hash_doinsert() -- Handle insertion of a single HashItem in the table. * - * This routine is called by the public interface routines, hashbuild - * and hashinsert. By here, hashitem is filled in, and has a unique - * (xid, seqno) pair. The datum to be used as a "key" is in the - * hashitem. + * This routine is called by the public interface routines, hashbuild + * and hashinsert. By here, hashitem is filled in, and has a unique + * (xid, seqno) pair. The datum to be used as a "key" is in the + * hashitem. */ InsertIndexResult _hash_doinsert(Relation rel, HashItem hitem) { - Buffer buf; - Buffer metabuf; - BlockNumber blkno; - HashMetaPage metap; - IndexTuple itup; - InsertIndexResult res; - ScanKey itup_scankey; - int natts; - Page page; - - metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - - /* we need a scan key to do our search, so build one */ - itup = &(hitem->hash_itup); - if ((natts = rel->rd_rel->relnatts) != 1) - elog(WARN, "Hash indices valid for only one index key."); - itup_scankey = _hash_mkscankey(rel, itup, metap); - - /* - * find the first page in the bucket chain containing this key and - * place it in buf. _hash_search obtains a read lock for us. - */ - _hash_search(rel, natts, itup_scankey, &buf, metap); - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE); - - /* - * trade in our read lock for a write lock so that we can do the - * insertion. - */ - blkno = BufferGetBlockNumber(buf); - _hash_relbuf(rel, buf, HASH_READ); - buf = _hash_getbuf(rel, blkno, HASH_WRITE); - - - /* - * XXX btree comment (haven't decided what to do in hash): don't - * think the bucket can be split while we're reading the metapage. - * - * If the page was split between the time that we surrendered our - * read lock and acquired our write lock, then this page may no - * longer be the right place for the key we want to insert. - */ - - /* do the insertion */ - res = _hash_insertonpg(rel, buf, natts, itup_scankey, - hitem, metabuf); - - /* be tidy */ - _hash_freeskey(itup_scankey); - - return (res); + Buffer buf; + Buffer metabuf; + BlockNumber blkno; + HashMetaPage metap; + IndexTuple itup; + InsertIndexResult res; + ScanKey itup_scankey; + int natts; + Page page; + + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + + /* we need a scan key to do our search, so build one */ + itup = &(hitem->hash_itup); + if ((natts = rel->rd_rel->relnatts) != 1) + elog(WARN, "Hash indices valid for only one index key."); + itup_scankey = _hash_mkscankey(rel, itup, metap); + + /* + * find the first page in the bucket chain containing this key and + * place it in buf. _hash_search obtains a read lock for us. + */ + _hash_search(rel, natts, itup_scankey, &buf, metap); + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE); + + /* + * trade in our read lock for a write lock so that we can do the + * insertion. + */ + blkno = BufferGetBlockNumber(buf); + _hash_relbuf(rel, buf, HASH_READ); + buf = _hash_getbuf(rel, blkno, HASH_WRITE); + + + /* + * XXX btree comment (haven't decided what to do in hash): don't think + * the bucket can be split while we're reading the metapage. + * + * If the page was split between the time that we surrendered our read + * lock and acquired our write lock, then this page may no longer be + * the right place for the key we want to insert. + */ + + /* do the insertion */ + res = _hash_insertonpg(rel, buf, natts, itup_scankey, + hitem, metabuf); + + /* be tidy */ + _hash_freeskey(itup_scankey); + + return (res); } /* - * _hash_insertonpg() -- Insert a tuple on a particular page in the table. + * _hash_insertonpg() -- Insert a tuple on a particular page in the table. * - * This recursive procedure does the following things: + * This recursive procedure does the following things: * - * + if necessary, splits the target page. - * + inserts the tuple. + * + if necessary, splits the target page. + * + inserts the tuple. * - * On entry, we must have the right buffer on which to do the - * insertion, and the buffer must be pinned and locked. On return, - * we will have dropped both the pin and the write lock on the buffer. + * On entry, we must have the right buffer on which to do the + * insertion, and the buffer must be pinned and locked. On return, + * we will have dropped both the pin and the write lock on the buffer. * */ -static InsertIndexResult +static InsertIndexResult _hash_insertonpg(Relation rel, - Buffer buf, - int keysz, - ScanKey scankey, - HashItem hitem, - Buffer metabuf) + Buffer buf, + int keysz, + ScanKey scankey, + HashItem hitem, + Buffer metabuf) { - InsertIndexResult res; - Page page; - BlockNumber itup_blkno; - OffsetNumber itup_off; - int itemsz; - HashPageOpaque pageopaque; - bool do_expand = false; - Buffer ovflbuf; - HashMetaPage metap; - Bucket bucket; - - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - pageopaque = (HashPageOpaque) PageGetSpecialPointer(page); - bucket = pageopaque->hasho_bucket; - - itemsz = IndexTupleDSize(hitem->hash_itup) - + (sizeof(HashItemData) - sizeof(IndexTupleData)); - itemsz = DOUBLEALIGN(itemsz); - - while (PageGetFreeSpace(page) < itemsz) { - /* - * no space on this page; check for an overflow page - */ - if (BlockNumberIsValid(pageopaque->hasho_nextblkno)) { - /* - * ovfl page exists; go get it. if it doesn't have room, - * we'll find out next pass through the loop test above. - */ - ovflbuf = _hash_getbuf(rel, pageopaque->hasho_nextblkno, - HASH_WRITE); - _hash_relbuf(rel, buf, HASH_WRITE); - buf = ovflbuf; - page = BufferGetPage(buf); - } else { - /* - * we're at the end of the bucket chain and we haven't - * found a page with enough room. allocate a new overflow - * page. - */ - do_expand = true; - ovflbuf = _hash_addovflpage(rel, &metabuf, buf); - _hash_relbuf(rel, buf, HASH_WRITE); - buf = ovflbuf; - page = BufferGetPage(buf); - - if (PageGetFreeSpace(page) < itemsz) { - /* it doesn't fit on an empty page -- give up */ - elog(WARN, "hash item too large"); - } - } - _hash_checkpage(page, LH_OVERFLOW_PAGE); + InsertIndexResult res; + Page page; + BlockNumber itup_blkno; + OffsetNumber itup_off; + int itemsz; + HashPageOpaque pageopaque; + bool do_expand = false; + Buffer ovflbuf; + HashMetaPage metap; + Bucket bucket; + + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); pageopaque = (HashPageOpaque) PageGetSpecialPointer(page); - Assert(pageopaque->hasho_bucket == bucket); - } - - itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem); - itup_blkno = BufferGetBlockNumber(buf); - - /* by here, the new tuple is inserted */ - res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); - - ItemPointerSet(&(res->pointerData), itup_blkno, itup_off); - - if (res != NULL) { - /* - * Increment the number of keys in the table. - * We switch lock access type just for a moment - * to allow greater accessibility to the metapage. - */ - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, - HASH_READ, HASH_WRITE); - metap->hashm_nkeys += 1; - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, - HASH_WRITE, HASH_READ); - - } - - _hash_wrtbuf(rel, buf); - - if (do_expand || - (metap->hashm_nkeys / (metap->hashm_maxbucket + 1)) - > metap->hashm_ffactor) { - _hash_expandtable(rel, metabuf); - } - _hash_relbuf(rel, metabuf, HASH_READ); - return (res); -} + bucket = pageopaque->hasho_bucket; + + itemsz = IndexTupleDSize(hitem->hash_itup) + + (sizeof(HashItemData) - sizeof(IndexTupleData)); + itemsz = DOUBLEALIGN(itemsz); + + while (PageGetFreeSpace(page) < itemsz) + { + + /* + * no space on this page; check for an overflow page + */ + if (BlockNumberIsValid(pageopaque->hasho_nextblkno)) + { + + /* + * ovfl page exists; go get it. if it doesn't have room, + * we'll find out next pass through the loop test above. + */ + ovflbuf = _hash_getbuf(rel, pageopaque->hasho_nextblkno, + HASH_WRITE); + _hash_relbuf(rel, buf, HASH_WRITE); + buf = ovflbuf; + page = BufferGetPage(buf); + } + else + { + + /* + * we're at the end of the bucket chain and we haven't found a + * page with enough room. allocate a new overflow page. + */ + do_expand = true; + ovflbuf = _hash_addovflpage(rel, &metabuf, buf); + _hash_relbuf(rel, buf, HASH_WRITE); + buf = ovflbuf; + page = BufferGetPage(buf); + + if (PageGetFreeSpace(page) < itemsz) + { + /* it doesn't fit on an empty page -- give up */ + elog(WARN, "hash item too large"); + } + } + _hash_checkpage(page, LH_OVERFLOW_PAGE); + pageopaque = (HashPageOpaque) PageGetSpecialPointer(page); + Assert(pageopaque->hasho_bucket == bucket); + } + + itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem); + itup_blkno = BufferGetBlockNumber(buf); + + /* by here, the new tuple is inserted */ + res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); + + ItemPointerSet(&(res->pointerData), itup_blkno, itup_off); + + if (res != NULL) + { + + /* + * Increment the number of keys in the table. We switch lock + * access type just for a moment to allow greater accessibility to + * the metapage. + */ + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, + HASH_READ, HASH_WRITE); + metap->hashm_nkeys += 1; + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, + HASH_WRITE, HASH_READ); + + } + + _hash_wrtbuf(rel, buf); + + if (do_expand || + (metap->hashm_nkeys / (metap->hashm_maxbucket + 1)) + > metap->hashm_ffactor) + { + _hash_expandtable(rel, metabuf); + } + _hash_relbuf(rel, metabuf, HASH_READ); + return (res); +} /* - * _hash_pgaddtup() -- add a tuple to a particular page in the index. + * _hash_pgaddtup() -- add a tuple to a particular page in the index. * - * This routine adds the tuple to the page as requested, and keeps the - * write lock and reference associated with the page's buffer. It is - * an error to call pgaddtup() without a write lock and reference. + * This routine adds the tuple to the page as requested, and keeps the + * write lock and reference associated with the page's buffer. It is + * an error to call pgaddtup() without a write lock and reference. */ -static OffsetNumber +static OffsetNumber _hash_pgaddtup(Relation rel, - Buffer buf, - int keysz, - ScanKey itup_scankey, - Size itemsize, - HashItem hitem) + Buffer buf, + int keysz, + ScanKey itup_scankey, + Size itemsize, + HashItem hitem) { - OffsetNumber itup_off; - Page page; - - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - - itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page)); - PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED); - - /* write the buffer, but hold our lock */ - _hash_wrtnorelbuf(rel, buf); - - return (itup_off); + OffsetNumber itup_off; + Page page; + + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + + itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page)); + PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED); + + /* write the buffer, but hold our lock */ + _hash_wrtnorelbuf(rel, buf); + + return (itup_off); } diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.c index d976c4818c8..b6882d4d3e1 100644 --- a/src/backend/access/hash/hashovfl.c +++ b/src/backend/access/hash/hashovfl.c @@ -1,400 +1,423 @@ /*------------------------------------------------------------------------- * * hashovfl.c-- - * Overflow page management code for the Postgres hash access method + * Overflow page management code for the Postgres hash access method * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/hashovfl.c,v 1.9 1997/08/12 22:51:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashovfl.c,v 1.10 1997/09/07 04:37:57 momjian Exp $ * * NOTES - * Overflow pages look like ordinary relation pages. + * Overflow pages look like ordinary relation pages. * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/hash.h> #include <storage/bufmgr.h> #include <utils/memutils.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static OverflowPageAddress _hash_getovfladdr(Relation rel, Buffer *metabufp); -static uint32 _hash_firstfreebit(uint32 map); +static OverflowPageAddress _hash_getovfladdr(Relation rel, Buffer * metabufp); +static uint32 _hash_firstfreebit(uint32 map); /* - * _hash_addovflpage + * _hash_addovflpage + * + * Add an overflow page to the page currently pointed to by the buffer + * argument 'buf'. * - * Add an overflow page to the page currently pointed to by the buffer - * argument 'buf'. + * *Metabufp has a read lock upon entering the function; buf has a + * write lock. * - * *Metabufp has a read lock upon entering the function; buf has a - * write lock. - * */ Buffer -_hash_addovflpage(Relation rel, Buffer *metabufp, Buffer buf) +_hash_addovflpage(Relation rel, Buffer * metabufp, Buffer buf) { - - OverflowPageAddress oaddr; - BlockNumber ovflblkno; - Buffer ovflbuf; - HashMetaPage metap; - HashPageOpaque ovflopaque; - HashPageOpaque pageopaque; - Page page; - Page ovflpage; - - /* this had better be the last page in a bucket chain */ - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - pageopaque = (HashPageOpaque) PageGetSpecialPointer(page); - Assert(!BlockNumberIsValid(pageopaque->hasho_nextblkno)); - - metap = (HashMetaPage) BufferGetPage(*metabufp); - _hash_checkpage((Page) metap, LH_META_PAGE); - - /* allocate an empty overflow page */ - oaddr = _hash_getovfladdr(rel, metabufp); - if (oaddr == InvalidOvflAddress) { - elog(WARN, "_hash_addovflpage: problem with _hash_getovfladdr."); - } - ovflblkno = OADDR_TO_BLKNO(OADDR_OF(SPLITNUM(oaddr), OPAGENUM(oaddr))); - Assert(BlockNumberIsValid(ovflblkno)); - ovflbuf = _hash_getbuf(rel, ovflblkno, HASH_WRITE); - Assert(BufferIsValid(ovflbuf)); - ovflpage = BufferGetPage(ovflbuf); - - /* initialize the new overflow page */ - _hash_pageinit(ovflpage, BufferGetPageSize(ovflbuf)); - ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage); - ovflopaque->hasho_prevblkno = BufferGetBlockNumber(buf); - ovflopaque->hasho_nextblkno = InvalidBlockNumber; - ovflopaque->hasho_flag = LH_OVERFLOW_PAGE; - ovflopaque->hasho_oaddr = oaddr; - ovflopaque->hasho_bucket = pageopaque->hasho_bucket; - _hash_wrtnorelbuf(rel, ovflbuf); - - /* logically chain overflow page to previous page */ - pageopaque->hasho_nextblkno = ovflblkno; - _hash_wrtnorelbuf(rel, buf); - return (ovflbuf); + + OverflowPageAddress oaddr; + BlockNumber ovflblkno; + Buffer ovflbuf; + HashMetaPage metap; + HashPageOpaque ovflopaque; + HashPageOpaque pageopaque; + Page page; + Page ovflpage; + + /* this had better be the last page in a bucket chain */ + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + pageopaque = (HashPageOpaque) PageGetSpecialPointer(page); + Assert(!BlockNumberIsValid(pageopaque->hasho_nextblkno)); + + metap = (HashMetaPage) BufferGetPage(*metabufp); + _hash_checkpage((Page) metap, LH_META_PAGE); + + /* allocate an empty overflow page */ + oaddr = _hash_getovfladdr(rel, metabufp); + if (oaddr == InvalidOvflAddress) + { + elog(WARN, "_hash_addovflpage: problem with _hash_getovfladdr."); + } + ovflblkno = OADDR_TO_BLKNO(OADDR_OF(SPLITNUM(oaddr), OPAGENUM(oaddr))); + Assert(BlockNumberIsValid(ovflblkno)); + ovflbuf = _hash_getbuf(rel, ovflblkno, HASH_WRITE); + Assert(BufferIsValid(ovflbuf)); + ovflpage = BufferGetPage(ovflbuf); + + /* initialize the new overflow page */ + _hash_pageinit(ovflpage, BufferGetPageSize(ovflbuf)); + ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage); + ovflopaque->hasho_prevblkno = BufferGetBlockNumber(buf); + ovflopaque->hasho_nextblkno = InvalidBlockNumber; + ovflopaque->hasho_flag = LH_OVERFLOW_PAGE; + ovflopaque->hasho_oaddr = oaddr; + ovflopaque->hasho_bucket = pageopaque->hasho_bucket; + _hash_wrtnorelbuf(rel, ovflbuf); + + /* logically chain overflow page to previous page */ + pageopaque->hasho_nextblkno = ovflblkno; + _hash_wrtnorelbuf(rel, buf); + return (ovflbuf); } /* - * _hash_getovfladdr() + * _hash_getovfladdr() * - * Find an available overflow page and return its address. + * Find an available overflow page and return its address. * - * When we enter this function, we have a read lock on *metabufp which - * we change to a write lock immediately. Before exiting, the write lock - * is exchanged for a read lock. + * When we enter this function, we have a read lock on *metabufp which + * we change to a write lock immediately. Before exiting, the write lock + * is exchanged for a read lock. * */ -static OverflowPageAddress -_hash_getovfladdr(Relation rel, Buffer *metabufp) +static OverflowPageAddress +_hash_getovfladdr(Relation rel, Buffer * metabufp) { - HashMetaPage metap; - Buffer mapbuf = 0; - BlockNumber blkno; - PageOffset offset; - OverflowPageAddress oaddr; - SplitNumber splitnum; - uint32 *freep = NULL; - uint32 max_free; - uint32 bit; - uint32 first_page; - uint32 free_bit; - uint32 free_page; - uint32 in_use_bits; - uint32 i, j; - - metap = (HashMetaPage) _hash_chgbufaccess(rel, metabufp, HASH_READ, HASH_WRITE); - - splitnum = metap->OVFL_POINT; - max_free = metap->SPARES[splitnum]; - - free_page = (max_free - 1) >> (metap->BSHIFT + BYTE_TO_BIT); - free_bit = (max_free - 1) & (BMPGSZ_BIT(metap) - 1); - - /* Look through all the free maps to find the first free block */ - first_page = metap->LAST_FREED >> (metap->BSHIFT + BYTE_TO_BIT); - for ( i = first_page; i <= free_page; i++ ) { - Page mappage; - - blkno = metap->hashm_mapp[i]; - mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE); - mappage = BufferGetPage(mapbuf); - _hash_checkpage(mappage, LH_BITMAP_PAGE); - freep = HashPageGetBitmap(mappage); - Assert(freep); - - if (i == free_page) - in_use_bits = free_bit; - else - in_use_bits = BMPGSZ_BIT(metap) - 1; - - if (i == first_page) { - bit = metap->LAST_FREED & (BMPGSZ_BIT(metap) - 1); - j = bit / BITS_PER_MAP; - bit = bit & ~(BITS_PER_MAP - 1); - } else { - bit = 0; - j = 0; + HashMetaPage metap; + Buffer mapbuf = 0; + BlockNumber blkno; + PageOffset offset; + OverflowPageAddress oaddr; + SplitNumber splitnum; + uint32 *freep = NULL; + uint32 max_free; + uint32 bit; + uint32 first_page; + uint32 free_bit; + uint32 free_page; + uint32 in_use_bits; + uint32 i, + j; + + metap = (HashMetaPage) _hash_chgbufaccess(rel, metabufp, HASH_READ, HASH_WRITE); + + splitnum = metap->OVFL_POINT; + max_free = metap->SPARES[splitnum]; + + free_page = (max_free - 1) >> (metap->BSHIFT + BYTE_TO_BIT); + free_bit = (max_free - 1) & (BMPGSZ_BIT(metap) - 1); + + /* Look through all the free maps to find the first free block */ + first_page = metap->LAST_FREED >> (metap->BSHIFT + BYTE_TO_BIT); + for (i = first_page; i <= free_page; i++) + { + Page mappage; + + blkno = metap->hashm_mapp[i]; + mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE); + mappage = BufferGetPage(mapbuf); + _hash_checkpage(mappage, LH_BITMAP_PAGE); + freep = HashPageGetBitmap(mappage); + Assert(freep); + + if (i == free_page) + in_use_bits = free_bit; + else + in_use_bits = BMPGSZ_BIT(metap) - 1; + + if (i == first_page) + { + bit = metap->LAST_FREED & (BMPGSZ_BIT(metap) - 1); + j = bit / BITS_PER_MAP; + bit = bit & ~(BITS_PER_MAP - 1); + } + else + { + bit = 0; + j = 0; + } + for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) + if (freep[j] != ALL_SET) + goto found; + } + + /* No Free Page Found - have to allocate a new page */ + metap->LAST_FREED = metap->SPARES[splitnum]; + metap->SPARES[splitnum]++; + offset = metap->SPARES[splitnum] - + (splitnum ? metap->SPARES[splitnum - 1] : 0); + +#define OVMSG "HASH: Out of overflow pages. Out of luck.\n" + + if (offset > SPLITMASK) + { + if (++splitnum >= NCACHED) + { + elog(WARN, OVMSG); + } + metap->OVFL_POINT = splitnum; + metap->SPARES[splitnum] = metap->SPARES[splitnum - 1]; + metap->SPARES[splitnum - 1]--; + offset = 0; } - for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) - if (freep[j] != ALL_SET) - goto found; - } - - /* No Free Page Found - have to allocate a new page */ - metap->LAST_FREED = metap->SPARES[splitnum]; - metap->SPARES[splitnum]++; - offset = metap->SPARES[splitnum] - - (splitnum ? metap->SPARES[splitnum - 1] : 0); - -#define OVMSG "HASH: Out of overflow pages. Out of luck.\n" - - if (offset > SPLITMASK) { - if (++splitnum >= NCACHED) { - elog(WARN, OVMSG); + + /* Check if we need to allocate a new bitmap page */ + if (free_bit == BMPGSZ_BIT(metap) - 1) + { + /* won't be needing old map page */ + + _hash_relbuf(rel, mapbuf, HASH_WRITE); + + free_page++; + if (free_page >= NCACHED) + { + elog(WARN, OVMSG); + } + + /* + * This is tricky. The 1 indicates that you want the new page + * allocated with 1 clear bit. Actually, you are going to + * allocate 2 pages from this map. The first is going to be the + * map page, the second is the overflow page we were looking for. + * The init_bitmap routine automatically, sets the first bit of + * itself to indicate that the bitmap itself is in use. We would + * explicitly set the second bit, but don't have to if we tell + * init_bitmap not to leave it clear in the first place. + */ + if (_hash_initbitmap(rel, metap, OADDR_OF(splitnum, offset), + 1, free_page)) + { + elog(WARN, "overflow_page: problem with _hash_initbitmap."); + } + metap->SPARES[splitnum]++; + offset++; + if (offset > SPLITMASK) + { + if (++splitnum >= NCACHED) + { + elog(WARN, OVMSG); + } + metap->OVFL_POINT = splitnum; + metap->SPARES[splitnum] = metap->SPARES[splitnum - 1]; + metap->SPARES[splitnum - 1]--; + offset = 0; + } } - metap->OVFL_POINT = splitnum; - metap->SPARES[splitnum] = metap->SPARES[splitnum-1]; - metap->SPARES[splitnum-1]--; - offset = 0; - } - - /* Check if we need to allocate a new bitmap page */ - if (free_bit == BMPGSZ_BIT(metap) - 1) { - /* won't be needing old map page */ - - _hash_relbuf(rel, mapbuf, HASH_WRITE); - - free_page++; - if (free_page >= NCACHED) { - elog(WARN, OVMSG); + else + { + + /* + * Free_bit addresses the last used bit. Bump it to address the + * first available bit. + */ + free_bit++; + SETBIT(freep, free_bit); + _hash_wrtbuf(rel, mapbuf); } - + + /* Calculate address of the new overflow page */ + oaddr = OADDR_OF(splitnum, offset); + _hash_chgbufaccess(rel, metabufp, HASH_WRITE, HASH_READ); + return (oaddr); + +found: + bit = bit + _hash_firstfreebit(freep[j]); + SETBIT(freep, bit); + _hash_wrtbuf(rel, mapbuf); + /* - * This is tricky. The 1 indicates that you want the new page - * allocated with 1 clear bit. Actually, you are going to < |