diff options
author | Bruce Momjian | 1997-09-07 05:04:48 +0000 |
---|---|---|
committer | Bruce Momjian | 1997-09-07 05:04:48 +0000 |
commit | 1ccd423235a48739d6f7a4d7889705b5f9ecc69b (patch) | |
tree | 8001c4e839dfad8f29ceda7f8c5f5dbb8759b564 /src/backend | |
parent | 8fecd4febf8357f3cc20383ed29ced484877d5ac (diff) |
Massive commit to run PGINDENT on all *.c and *.h files.
Diffstat (limited to 'src/backend')
357 files changed, 124869 insertions, 113750 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 - * 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. + * Bits are addressed starting with 0, but overflow pages are + * addressed beginning at 1. Bit is a bit addressnumber, so we need to + * increment it to convert it to a page number. */ - if (_hash_initbitmap(rel, metap, OADDR_OF(splitnum, offset), - 1, free_page)) { - elog(WARN, "overflow_page: problem with _hash_initbitmap."); + + bit = 1 + bit + (i * BMPGSZ_BIT(metap)); + if (bit >= metap->LAST_FREED) + { + metap->LAST_FREED = bit - 1; } - metap->SPARES[splitnum]++; - offset++; - if (offset > SPLITMASK) { - if (++splitnum >= NCACHED) { + + /* Calculate the split number for this page */ + for (i = 0; (i < splitnum) && (bit > metap->SPARES[i]); i++) + ; + offset = (i ? bit - metap->SPARES[i - 1] : bit); + if (offset >= SPLITMASK) + { elog(WARN, OVMSG); - } - metap->OVFL_POINT = splitnum; - metap->SPARES[splitnum] = metap->SPARES[splitnum-1]; - metap->SPARES[splitnum-1]--; - offset = 0; } - } 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); - - /* - * Bits are addressed starting with 0, but overflow pages are addressed - * beginning at 1. Bit is a bit addressnumber, so we need to increment - * it to convert it to a page number. - */ - - bit = 1 + bit + (i * BMPGSZ_BIT(metap)); - if (bit >= metap->LAST_FREED) { - metap->LAST_FREED = bit - 1; - } - - /* Calculate the split number for this page */ - for (i = 0; (i < splitnum) && (bit > metap->SPARES[i]); i++) - ; - offset = (i ? bit - metap->SPARES[i - 1] : bit); - if (offset >= SPLITMASK) { - elog(WARN, OVMSG); - } - - /* initialize this page */ - oaddr = OADDR_OF(i, offset); - _hash_chgbufaccess(rel, metabufp, HASH_WRITE, HASH_READ); - return (oaddr); + + /* initialize this page */ + oaddr = OADDR_OF(i, offset); + _hash_chgbufaccess(rel, metabufp, HASH_WRITE, HASH_READ); + return (oaddr); } /* - * _hash_firstfreebit() + * _hash_firstfreebit() + * + * Return the first bit that is not set in the argument 'map'. This + * function is used to find an available overflow page within a + * splitnumber. * - * Return the first bit that is not set in the argument 'map'. This - * function is used to find an available overflow page within a - * splitnumber. - * */ -static uint32 +static uint32 _hash_firstfreebit(uint32 map) { - uint32 i, mask; - - mask = 0x1; - for (i = 0; i < BITS_PER_MAP; i++) { - if (!(mask & map)) - return (i); - mask = mask << 1; - } - return (i); + uint32 i, + mask; + + mask = 0x1; + for (i = 0; i < BITS_PER_MAP; i++) + { + if (!(mask & map)) + return (i); + mask = mask << 1; + } + return (i); } /* - * _hash_freeovflpage() - + * _hash_freeovflpage() - * - * Mark this overflow page as free and return a buffer with - * the page that follows it (which may be defined as - * InvalidBuffer). + * Mark this overflow page as free and return a buffer with + * the page that follows it (which may be defined as + * InvalidBuffer). * */ Buffer _hash_freeovflpage(Relation rel, Buffer ovflbuf) { - HashMetaPage metap; - Buffer metabuf; - Buffer mapbuf; - BlockNumber prevblkno; - BlockNumber blkno; - BlockNumber nextblkno; - HashPageOpaque ovflopaque; - Page ovflpage; - Page mappage; - OverflowPageAddress addr; - SplitNumber splitnum; - uint32 *freep; - uint32 ovflpgno; - int32 bitmappage, bitmapbit; - Bucket bucket; - - metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE); - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - - ovflpage = BufferGetPage(ovflbuf); - _hash_checkpage(ovflpage, LH_OVERFLOW_PAGE); - ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage); - addr = ovflopaque->hasho_oaddr; - nextblkno = ovflopaque->hasho_nextblkno; - prevblkno = ovflopaque->hasho_prevblkno; - bucket = ovflopaque->hasho_bucket; - memset(ovflpage, 0, BufferGetPageSize(ovflbuf)); - _hash_wrtbuf(rel, ovflbuf); - - /* - * fix up the bucket chain. this is a doubly-linked list, so we - * must fix up the bucket chain members behind and ahead of the - * overflow page being deleted. - * - * XXX this should look like: - * - lock prev/next - * - modify/write prev/next (how to do write ordering with a - * doubly-linked list?) - * - unlock prev/next - */ - if (BlockNumberIsValid(prevblkno)) { - Buffer prevbuf = _hash_getbuf(rel, prevblkno, HASH_WRITE); - Page prevpage = BufferGetPage(prevbuf); - HashPageOpaque prevopaque = - (HashPageOpaque) PageGetSpecialPointer(prevpage); - - _hash_checkpage(prevpage, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - Assert(prevopaque->hasho_bucket == bucket); - prevopaque->hasho_nextblkno = nextblkno; - _hash_wrtbuf(rel, prevbuf); - } - if (BlockNumberIsValid(nextblkno)) { - Buffer nextbuf = _hash_getbuf(rel, nextblkno, HASH_WRITE); - Page nextpage = BufferGetPage(nextbuf); - HashPageOpaque nextopaque = - (HashPageOpaque) PageGetSpecialPointer(nextpage); - - _hash_checkpage(nextpage, LH_OVERFLOW_PAGE); - Assert(nextopaque->hasho_bucket == bucket); - nextopaque->hasho_prevblkno = prevblkno; - _hash_wrtbuf(rel, nextbuf); - } - - /* - * Fix up the overflow page bitmap that tracks this particular - * overflow page. The bitmap can be found in the MetaPageData - * array element hashm_mapp[bitmappage]. - */ - splitnum = (addr >> SPLITSHIFT); - ovflpgno = - (splitnum ? metap->SPARES[splitnum - 1] : 0) + (addr & SPLITMASK) - 1; - - if (ovflpgno < metap->LAST_FREED) { - metap->LAST_FREED = ovflpgno; - } - - bitmappage = (ovflpgno >> (metap->BSHIFT + BYTE_TO_BIT)); - bitmapbit = ovflpgno & (BMPGSZ_BIT(metap) - 1); - - blkno = metap->hashm_mapp[bitmappage]; - mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE); - mappage = BufferGetPage(mapbuf); - _hash_checkpage(mappage, LH_BITMAP_PAGE); - freep = HashPageGetBitmap(mappage); - CLRBIT(freep, bitmapbit); - _hash_wrtbuf(rel, mapbuf); - - _hash_relbuf(rel, metabuf, HASH_WRITE); - - /* - * now instantiate the page that replaced this one, - * if it exists, and return that buffer with a write lock. - */ - if (BlockNumberIsValid(nextblkno)) { - return (_hash_getbuf(rel, nextblkno, HASH_WRITE)); - } else { - return (InvalidBuffer); - } + HashMetaPage metap; + Buffer metabuf; + Buffer mapbuf; + BlockNumber prevblkno; + BlockNumber blkno; + BlockNumber nextblkno; + HashPageOpaque ovflopaque; + Page ovflpage; + Page mappage; + OverflowPageAddress addr; + SplitNumber splitnum; + uint32 *freep; + uint32 ovflpgno; + int32 bitmappage, + bitmapbit; + Bucket bucket; + + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE); + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + + ovflpage = BufferGetPage(ovflbuf); + _hash_checkpage(ovflpage, LH_OVERFLOW_PAGE); + ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage); + addr = ovflopaque->hasho_oaddr; + nextblkno = ovflopaque->hasho_nextblkno; + prevblkno = ovflopaque->hasho_prevblkno; + bucket = ovflopaque->hasho_bucket; + memset(ovflpage, 0, BufferGetPageSize(ovflbuf)); + _hash_wrtbuf(rel, ovflbuf); + + /* + * fix up the bucket chain. this is a doubly-linked list, so we must + * fix up the bucket chain members behind and ahead of the overflow + * page being deleted. + * + * XXX this should look like: - lock prev/next - modify/write prev/next + * (how to do write ordering with a doubly-linked list?) - unlock + * prev/next + */ + if (BlockNumberIsValid(prevblkno)) + { + Buffer prevbuf = _hash_getbuf(rel, prevblkno, HASH_WRITE); + Page prevpage = BufferGetPage(prevbuf); + HashPageOpaque prevopaque = + (HashPageOpaque) PageGetSpecialPointer(prevpage); + + _hash_checkpage(prevpage, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + Assert(prevopaque->hasho_bucket == bucket); + prevopaque->hasho_nextblkno = nextblkno; + _hash_wrtbuf(rel, prevbuf); + } + if (BlockNumberIsValid(nextblkno)) + { + Buffer nextbuf = _hash_getbuf(rel, nextblkno, HASH_WRITE); + Page nextpage = BufferGetPage(nextbuf); + HashPageOpaque nextopaque = + (HashPageOpaque) PageGetSpecialPointer(nextpage); + + _hash_checkpage(nextpage, LH_OVERFLOW_PAGE); + Assert(nextopaque->hasho_bucket == bucket); + nextopaque->hasho_prevblkno = prevblkno; + _hash_wrtbuf(rel, nextbuf); + } + + /* + * Fix up the overflow page bitmap that tracks this particular + * overflow page. The bitmap can be found in the MetaPageData array + * element hashm_mapp[bitmappage]. + */ + splitnum = (addr >> SPLITSHIFT); + ovflpgno = + (splitnum ? metap->SPARES[splitnum - 1] : 0) + (addr & SPLITMASK) - 1; + + if (ovflpgno < metap->LAST_FREED) + { + metap->LAST_FREED = ovflpgno; + } + + bitmappage = (ovflpgno >> (metap->BSHIFT + BYTE_TO_BIT)); + bitmapbit = ovflpgno & (BMPGSZ_BIT(metap) - 1); + + blkno = metap->hashm_mapp[bitmappage]; + mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE); + mappage = BufferGetPage(mapbuf); + _hash_checkpage(mappage, LH_BITMAP_PAGE); + freep = HashPageGetBitmap(mappage); + CLRBIT(freep, bitmapbit); + _hash_wrtbuf(rel, mapbuf); + + _hash_relbuf(rel, metabuf, HASH_WRITE); + + /* + * now instantiate the page that replaced this one, if it exists, and + * return that buffer with a write lock. + */ + if (BlockNumberIsValid(nextblkno)) + { + return (_hash_getbuf(rel, nextblkno, HASH_WRITE)); + } + else + { + return (InvalidBuffer); + } } /* - * _hash_initbitmap() - * - * Initialize a new bitmap page. The metapage has a write-lock upon - * entering the function. + * _hash_initbitmap() + * + * Initialize a new bitmap page. The metapage has a write-lock upon + * entering the function. * * 'pnum' is the OverflowPageAddress of the new bitmap page. * 'nbits' is how many bits to clear (i.e., make available) in the new @@ -404,211 +427,219 @@ _hash_freeovflpage(Relation rel, Buffer ovflbuf) * metapage's array of bitmap page OverflowPageAddresses. */ -#define INT_MASK ((1 << INT_TO_BIT) -1) +#define INT_MASK ((1 << INT_TO_BIT) -1) int32 _hash_initbitmap(Relation rel, - HashMetaPage metap, - int32 pnum, - int32 nbits, - int32 ndx) + HashMetaPage metap, + int32 pnum, + int32 nbits, + int32 ndx) { - Buffer buf; - BlockNumber blkno; - Page pg; - HashPageOpaque op; - uint32 *freep; - int clearbytes, clearints; - - blkno = OADDR_TO_BLKNO(pnum); - buf = _hash_getbuf(rel, blkno, HASH_WRITE); - pg = BufferGetPage(buf); - _hash_pageinit(pg, BufferGetPageSize(buf)); - op = (HashPageOpaque) PageGetSpecialPointer(pg); - op->hasho_oaddr = InvalidOvflAddress; - op->hasho_prevblkno = InvalidBlockNumber; - op->hasho_nextblkno = InvalidBlockNumber; - op->hasho_flag = LH_BITMAP_PAGE; - op->hasho_bucket = -1; - - freep = HashPageGetBitmap(pg); - - /* set all of the bits above 'nbits' to 1 */ - clearints = ((nbits - 1) >> INT_TO_BIT) + 1; - clearbytes = clearints << INT_TO_BYTE; - memset((char *) freep, 0, clearbytes); - memset(((char *) freep) + clearbytes, 0xFF, - BMPGSZ_BYTE(metap) - clearbytes); - freep[clearints - 1] = ALL_SET << (nbits & INT_MASK); - - /* bit 0 represents the new bitmap page */ - SETBIT(freep, 0); - - /* metapage already has a write lock */ - metap->hashm_nmaps++; - metap->hashm_mapp[ndx] = blkno; - - /* write out the new bitmap page (releasing its locks) */ - _hash_wrtbuf(rel, buf); - - return (0); + Buffer buf; + BlockNumber blkno; + Page pg; + HashPageOpaque op; + uint32 *freep; + int clearbytes, + clearints; + + blkno = OADDR_TO_BLKNO(pnum); + buf = _hash_getbuf(rel, blkno, HASH_WRITE); + pg = BufferGetPage(buf); + _hash_pageinit(pg, BufferGetPageSize(buf)); + op = (HashPageOpaque) PageGetSpecialPointer(pg); + op->hasho_oaddr = InvalidOvflAddress; + op->hasho_prevblkno = InvalidBlockNumber; + op->hasho_nextblkno = InvalidBlockNumber; + op->hasho_flag = LH_BITMAP_PAGE; + op->hasho_bucket = -1; + + freep = HashPageGetBitmap(pg); + + /* set all of the bits above 'nbits' to 1 */ + clearints = ((nbits - 1) >> INT_TO_BIT) + 1; + clearbytes = clearints << INT_TO_BYTE; + memset((char *) freep, 0, clearbytes); + memset(((char *) freep) + clearbytes, 0xFF, + BMPGSZ_BYTE(metap) - clearbytes); + freep[clearints - 1] = ALL_SET << (nbits & INT_MASK); + + /* bit 0 represents the new bitmap page */ + SETBIT(freep, 0); + + /* metapage already has a write lock */ + metap->hashm_nmaps++; + metap->hashm_mapp[ndx] = blkno; + + /* write out the new bitmap page (releasing its locks) */ + _hash_wrtbuf(rel, buf); + + return (0); } /* - * _hash_squeezebucket(rel, bucket) + * _hash_squeezebucket(rel, bucket) * - * Try to squeeze the tuples onto pages occuring earlier in the - * bucket chain in an attempt to free overflow pages. When we start - * the "squeezing", the page from which we start taking tuples (the - * "read" page) is the last bucket in the bucket chain and the page - * onto which we start squeezing tuples (the "write" page) is the - * first page in the bucket chain. The read page works backward and - * the write page works forward; the procedure terminates when the - * read page and write page are the same page. + * Try to squeeze the tuples onto pages occuring earlier in the + * bucket chain in an attempt to free overflow pages. When we start + * the "squeezing", the page from which we start taking tuples (the + * "read" page) is the last bucket in the bucket chain and the page + * onto which we start squeezing tuples (the "write" page) is the + * first page in the bucket chain. The read page works backward and + * the write page works forward; the procedure terminates when the + * read page and write page are the same page. */ void _hash_squeezebucket(Relation rel, - HashMetaPage metap, - Bucket bucket) + HashMetaPage metap, + Bucket bucket) { - Buffer wbuf; - Buffer rbuf = 0; - BlockNumber wblkno; - BlockNumber rblkno; - Page wpage; - Page rpage; - HashPageOpaque wopaque; - HashPageOpaque ropaque; - OffsetNumber woffnum; - OffsetNumber roffnum; - HashItem hitem; - int itemsz; - -/* elog(DEBUG, "_hash_squeezebucket: squeezing bucket %d", bucket); */ - - /* - * start squeezing into the base bucket page. - */ - wblkno = BUCKET_TO_BLKNO(bucket); - wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE); - wpage = BufferGetPage(wbuf); - _hash_checkpage(wpage, LH_BUCKET_PAGE); - wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage); - - /* - * if there aren't any overflow pages, there's nothing to squeeze. - */ - if (!BlockNumberIsValid(wopaque->hasho_nextblkno)) { - _hash_relbuf(rel, wbuf, HASH_WRITE); - return; - } - - /* - * find the last page in the bucket chain by starting at the base - * bucket page and working forward. - * - * XXX if chains tend to be long, we should probably move forward - * using HASH_READ and then _hash_chgbufaccess to HASH_WRITE when - * we reach the end. if they are short we probably don't care - * very much. if the hash function is working at all, they had - * better be short.. - */ - ropaque = wopaque; - do { - rblkno = ropaque->hasho_nextblkno; - if (ropaque != wopaque) { - _hash_relbuf(rel, rbuf, HASH_WRITE); - } - rbuf = _hash_getbuf(rel, rblkno, HASH_WRITE); - rpage = BufferGetPage(rbuf); - _hash_checkpage(rpage, LH_OVERFLOW_PAGE); - Assert(!PageIsEmpty(rpage)); - ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage); - Assert(ropaque->hasho_bucket == bucket); - } while (BlockNumberIsValid(ropaque->hasho_nextblkno)); - - /* - * squeeze the tuples. - */ - roffnum = FirstOffsetNumber; - for(;;) { - hitem = (HashItem) PageGetItem(rpage, PageGetItemId(rpage, roffnum)); - itemsz = IndexTupleDSize(hitem->hash_itup) - + (sizeof(HashItemData) - sizeof(IndexTupleData)); - itemsz = DOUBLEALIGN(itemsz); - + Buffer wbuf; + Buffer rbuf = 0; + BlockNumber wblkno; + BlockNumber rblkno; + Page wpage; + Page rpage; + HashPageOpaque wopaque; + HashPageOpaque ropaque; + OffsetNumber woffnum; + OffsetNumber roffnum; + HashItem hitem; + int itemsz; + +/* elog(DEBUG, "_hash_squeezebucket: squeezing bucket %d", bucket); */ + /* - * walk up the bucket chain, looking for a page big enough for - * this item. + * start squeezing into the base bucket page. */ - while (PageGetFreeSpace(wpage) < itemsz) { - wblkno = wopaque->hasho_nextblkno; + wblkno = BUCKET_TO_BLKNO(bucket); + wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE); + wpage = BufferGetPage(wbuf); + _hash_checkpage(wpage, LH_BUCKET_PAGE); + wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage); - _hash_wrtbuf(rel, wbuf); - - if (!BlockNumberIsValid(wblkno) || (rblkno == wblkno)) { - _hash_wrtbuf(rel, rbuf); - /* wbuf is already released */ + /* + * if there aren't any overflow pages, there's nothing to squeeze. + */ + if (!BlockNumberIsValid(wopaque->hasho_nextblkno)) + { + _hash_relbuf(rel, wbuf, HASH_WRITE); return; - } - - wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE); - wpage = BufferGetPage(wbuf); - _hash_checkpage(wpage, LH_OVERFLOW_PAGE); - Assert(!PageIsEmpty(wpage)); - wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage); - Assert(wopaque->hasho_bucket == bucket); } - - /* - * if we're here, we have found room so insert on the "write" - * page. - */ - woffnum = OffsetNumberNext(PageGetMaxOffsetNumber(wpage)); - PageAddItem(wpage, (Item) hitem, itemsz, woffnum, LP_USED); - - /* - * delete the tuple from the "read" page. - * PageIndexTupleDelete repacks the ItemId array, so 'roffnum' - * will be "advanced" to the "next" ItemId. + + /* + * find the last page in the bucket chain by starting at the base + * bucket page and working forward. + * + * XXX if chains tend to be long, we should probably move forward using + * HASH_READ and then _hash_chgbufaccess to HASH_WRITE when we reach + * the end. if they are short we probably don't care very much. if + * the hash function is working at all, they had better be short.. */ - PageIndexTupleDelete(rpage, roffnum); - _hash_wrtnorelbuf(rel, rbuf); - + ropaque = wopaque; + do + { + rblkno = ropaque->hasho_nextblkno; + if (ropaque != wopaque) + { + _hash_relbuf(rel, rbuf, HASH_WRITE); + } + rbuf = _hash_getbuf(rel, rblkno, HASH_WRITE); + rpage = BufferGetPage(rbuf); + _hash_checkpage(rpage, LH_OVERFLOW_PAGE); + Assert(!PageIsEmpty(rpage)); + ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage); + Assert(ropaque->hasho_bucket == bucket); + } while (BlockNumberIsValid(ropaque->hasho_nextblkno)); + /* - * if the "read" page is now empty because of the deletion, - * free it. + * squeeze the tuples. */ - if (PageIsEmpty(rpage) && (ropaque->hasho_flag & LH_OVERFLOW_PAGE)) { - rblkno = ropaque->hasho_prevblkno; - Assert(BlockNumberIsValid(rblkno)); - - /* - * free this overflow page. the extra _hash_relbuf is - * because _hash_freeovflpage gratuitously returns the - * next page (we want the previous page and will get it - * ourselves later). - */ - rbuf = _hash_freeovflpage(rel, rbuf); - if (BufferIsValid(rbuf)) { - _hash_relbuf(rel, rbuf, HASH_WRITE); - } - - if (rblkno == wblkno) { - /* rbuf is already released */ - _hash_wrtbuf(rel, wbuf); - return; - } - - rbuf = _hash_getbuf(rel, rblkno, HASH_WRITE); - rpage = BufferGetPage(rbuf); - _hash_checkpage(rpage, LH_OVERFLOW_PAGE); - Assert(!PageIsEmpty(rpage)); - ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage); - Assert(ropaque->hasho_bucket == bucket); - - roffnum = FirstOffsetNumber; + roffnum = FirstOffsetNumber; + for (;;) + { + hitem = (HashItem) PageGetItem(rpage, PageGetItemId(rpage, roffnum)); + itemsz = IndexTupleDSize(hitem->hash_itup) + + (sizeof(HashItemData) - sizeof(IndexTupleData)); + itemsz = DOUBLEALIGN(itemsz); + + /* + * walk up the bucket chain, looking for a page big enough for + * this item. + */ + while (PageGetFreeSpace(wpage) < itemsz) + { + wblkno = wopaque->hasho_nextblkno; + + _hash_wrtbuf(rel, wbuf); + + if (!BlockNumberIsValid(wblkno) || (rblkno == wblkno)) + { + _hash_wrtbuf(rel, rbuf); + /* wbuf is already released */ + return; + } + + wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE); + wpage = BufferGetPage(wbuf); + _hash_checkpage(wpage, LH_OVERFLOW_PAGE); + Assert(!PageIsEmpty(wpage)); + wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage); + Assert(wopaque->hasho_bucket == bucket); + } + + /* + * if we're here, we have found room so insert on the "write" + * page. + */ + woffnum = OffsetNumberNext(PageGetMaxOffsetNumber(wpage)); + PageAddItem(wpage, (Item) hitem, itemsz, woffnum, LP_USED); + + /* + * delete the tuple from the "read" page. PageIndexTupleDelete + * repacks the ItemId array, so 'roffnum' will be "advanced" to + * the "next" ItemId. + */ + PageIndexTupleDelete(rpage, roffnum); + _hash_wrtnorelbuf(rel, rbuf); + + /* + * if the "read" page is now empty because of the deletion, free + * it. + */ + if (PageIsEmpty(rpage) && (ropaque->hasho_flag & LH_OVERFLOW_PAGE)) + { + rblkno = ropaque->hasho_prevblkno; + Assert(BlockNumberIsValid(rblkno)); + + /* + * free this overflow page. the extra _hash_relbuf is because + * _hash_freeovflpage gratuitously returns the next page (we + * want the previous page and will get it ourselves later). + */ + rbuf = _hash_freeovflpage(rel, rbuf); + if (BufferIsValid(rbuf)) + { + _hash_relbuf(rel, rbuf, HASH_WRITE); + } + + if (rblkno == wblkno) + { + /* rbuf is already released */ + _hash_wrtbuf(rel, wbuf); + return; + } + + rbuf = _hash_getbuf(rel, rblkno, HASH_WRITE); + rpage = BufferGetPage(rbuf); + _hash_checkpage(rpage, LH_OVERFLOW_PAGE); + Assert(!PageIsEmpty(rpage)); + ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage); + Assert(ropaque->hasho_bucket == bucket); + + roffnum = FirstOffsetNumber; + } } - } } diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index 49c8f03f524..6c819b652d2 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -1,30 +1,30 @@ /*------------------------------------------------------------------------- * * hashpage.c-- - * Hash table page management code for the Postgres hash access method + * Hash table 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/hashpage.c,v 1.9 1997/08/18 20:51:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashpage.c,v 1.10 1997/09/07 04:38:00 momjian Exp $ * * NOTES - * Postgres hash pages look like ordinary relation pages. The opaque - * data at high addresses includes information about the page including - * whether a page is an overflow page or a true bucket, the block - * numbers of the preceding and following pages, and the overflow - * address of the page if it is an overflow page. + * Postgres hash pages look like ordinary relation pages. The opaque + * data at high addresses includes information about the page including + * whether a page is an overflow page or a true bucket, the block + * numbers of the preceding and following pages, and the overflow + * address of the page if it is an overflow page. * - * The first page in a hash relation, page zero, is special -- it stores - * information describing the hash table; it is referred to as teh - * "meta page." Pages one and higher store the actual data. + * The first page in a hash relation, page zero, is special -- it stores + * information describing the hash table; it is referred to as teh + * "meta page." Pages one and higher store the actual data. * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/hash.h> #include <storage/bufmgr.h> #include <miscadmin.h> @@ -33,411 +33,429 @@ #include <access/genam.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static void _hash_setpagelock(Relation rel, BlockNumber blkno, int access); -static void _hash_unsetpagelock(Relation rel, BlockNumber blkno, int access); -static void _hash_splitpage(Relation rel, Buffer metabuf, Bucket obucket, Bucket nbucket); - -/* - * We use high-concurrency locking on hash indices. There are two cases in - * which we don't do locking. One is when we're building the index. - * Since the creating transaction has not committed, no one can see - * the index, and there's no reason to share locks. The second case - * is when we're just starting up the database system. We use some - * special-purpose initialization code in the relation cache manager - * (see utils/cache/relcache.c) to allow us to do indexed scans on - * the system catalogs before we'd normally be able to. This happens - * before the lock table is fully initialized, so we can't use it. - * Strictly speaking, this violates 2pl, but we don't do 2pl on the - * system catalogs anyway. +static void _hash_setpagelock(Relation rel, BlockNumber blkno, int access); +static void _hash_unsetpagelock(Relation rel, BlockNumber blkno, int access); +static void _hash_splitpage(Relation rel, Buffer metabuf, Bucket obucket, Bucket nbucket); + +/* + * We use high-concurrency locking on hash indices. There are two cases in + * which we don't do locking. One is when we're building the index. + * Since the creating transaction has not committed, no one can see + * the index, and there's no reason to share locks. The second case + * is when we're just starting up the database system. We use some + * special-purpose initialization code in the relation cache manager + * (see utils/cache/relcache.c) to allow us to do indexed scans on + * the system catalogs before we'd normally be able to. This happens + * before the lock table is fully initialized, so we can't use it. + * Strictly speaking, this violates 2pl, but we don't do 2pl on the + * system catalogs anyway. */ -#define USELOCKING (!BuildingHash && !IsInitProcessingMode()) +#define USELOCKING (!BuildingHash && !IsInitProcessingMode()) /* - * _hash_metapinit() -- Initialize the metadata page of a hash index, - * the two buckets that we begin with and the initial - * bitmap page. + * _hash_metapinit() -- Initialize the metadata page of a hash index, + * the two buckets that we begin with and the initial + * bitmap page. */ void _hash_metapinit(Relation rel) { - HashMetaPage metap; - HashPageOpaque pageopaque; - Buffer metabuf; - Buffer buf; - Page pg; - int nbuckets; - uint32 nelem; /* number elements */ - uint32 lg2nelem; /* _hash_log2(nelem) */ - uint32 nblocks; - uint16 i; - - /* can't be sharing this with anyone, now... */ - if (USELOCKING) - RelationSetLockForWrite(rel); - - if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0) { - elog(WARN, "Cannot initialize non-empty hash table %s", - RelationGetRelationName(rel)); - } - - metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE); - pg = BufferGetPage(metabuf); - metap = (HashMetaPage) pg; - _hash_pageinit(pg, BufferGetPageSize(metabuf)); - - metap->hashm_magic = HASH_MAGIC; - metap->hashm_version = HASH_VERSION; - metap->hashm_nkeys = 0; - metap->hashm_nmaps = 0; - metap->hashm_ffactor = DEFAULT_FFACTOR; - metap->hashm_bsize = BufferGetPageSize(metabuf); - metap->hashm_bshift = _hash_log2(metap->hashm_bsize); - for (i = metap->hashm_bshift; i > 0; --i) { - if ((1 << i) < (metap->hashm_bsize - - (DOUBLEALIGN(sizeof(PageHeaderData)) + - DOUBLEALIGN(sizeof(HashPageOpaqueData))))) { - break; + HashMetaPage metap; + HashPageOpaque pageopaque; + Buffer metabuf; + Buffer buf; + Page pg; + int nbuckets; + uint32 nelem; /* number elements */ + uint32 lg2nelem; /* _hash_log2(nelem) */ + uint32 nblocks; + uint16 i; + + /* can't be sharing this with anyone, now... */ + if (USELOCKING) + RelationSetLockForWrite(rel); + + if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0) + { + elog(WARN, "Cannot initialize non-empty hash table %s", + RelationGetRelationName(rel)); + } + + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE); + pg = BufferGetPage(metabuf); + metap = (HashMetaPage) pg; + _hash_pageinit(pg, BufferGetPageSize(metabuf)); + + metap->hashm_magic = HASH_MAGIC; + metap->hashm_version = HASH_VERSION; + metap->hashm_nkeys = 0; + metap->hashm_nmaps = 0; + metap->hashm_ffactor = DEFAULT_FFACTOR; + metap->hashm_bsize = BufferGetPageSize(metabuf); + metap->hashm_bshift = _hash_log2(metap->hashm_bsize); + for (i = metap->hashm_bshift; i > 0; --i) + { + if ((1 << i) < (metap->hashm_bsize - + (DOUBLEALIGN(sizeof(PageHeaderData)) + + DOUBLEALIGN(sizeof(HashPageOpaqueData))))) + { + break; + } } - } - Assert(i); - metap->hashm_bmsize = 1 << i; - metap->hashm_procid = index_getprocid(rel, 1, HASHPROC); - - /* - * Make nelem = 2 rather than 0 so that we end up allocating space - * for the next greater power of two number of buckets. - */ - nelem = 2; - lg2nelem = 1; /*_hash_log2(MAX(nelem, 2)) */ - nbuckets = 2; /*1 << lg2nelem */ - - memset((char *) metap->hashm_spares, 0, sizeof(metap->hashm_spares)); - memset((char *) metap->hashm_mapp, 0, sizeof(metap->hashm_mapp)); - - metap->hashm_spares[lg2nelem] = 2; /* lg2nelem + 1 */ - metap->hashm_spares[lg2nelem + 1] = 2; /* lg2nelem + 1 */ - metap->hashm_ovflpoint = 1; /* lg2nelem */ - metap->hashm_lastfreed = 2; - - metap->hashm_maxbucket = metap->hashm_lowmask = 1; /* nbuckets - 1 */ - metap->hashm_highmask = 3; /* (nbuckets << 1) - 1 */ - - pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg); - pageopaque->hasho_oaddr = InvalidOvflAddress; - pageopaque->hasho_prevblkno = InvalidBlockNumber; - pageopaque->hasho_nextblkno = InvalidBlockNumber; - pageopaque->hasho_flag = LH_META_PAGE; - pageopaque->hasho_bucket = -1; - - /* - * First bitmap page is at: splitpoint lg2nelem page offset 1 which - * turns out to be page 3. Couldn't initialize page 3 until we created - * the first two buckets above. - */ - if (_hash_initbitmap(rel, metap, OADDR_OF(lg2nelem, 1), lg2nelem + 1, 0)) - elog(WARN, "Problem with _hash_initbitmap."); - - /* all done */ - _hash_wrtnorelbuf(rel, metabuf); - - /* - * initialize the first two buckets - */ - for (i = 0; i <= 1; i++) { - buf = _hash_getbuf(rel, BUCKET_TO_BLKNO(i), HASH_WRITE); - pg = BufferGetPage(buf); - _hash_pageinit(pg, BufferGetPageSize(buf)); + Assert(i); + metap->hashm_bmsize = 1 << i; + metap->hashm_procid = index_getprocid(rel, 1, HASHPROC); + + /* + * Make nelem = 2 rather than 0 so that we end up allocating space for + * the next greater power of two number of buckets. + */ + nelem = 2; + lg2nelem = 1; /* _hash_log2(MAX(nelem, 2)) */ + nbuckets = 2; /* 1 << lg2nelem */ + + memset((char *) metap->hashm_spares, 0, sizeof(metap->hashm_spares)); + memset((char *) metap->hashm_mapp, 0, sizeof(metap->hashm_mapp)); + + metap->hashm_spares[lg2nelem] = 2; /* lg2nelem + 1 */ + metap->hashm_spares[lg2nelem + 1] = 2; /* lg2nelem + 1 */ + metap->hashm_ovflpoint = 1; /* lg2nelem */ + metap->hashm_lastfreed = 2; + + metap->hashm_maxbucket = metap->hashm_lowmask = 1; /* nbuckets - 1 */ + metap->hashm_highmask = 3; /* (nbuckets << 1) - 1 */ + pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg); pageopaque->hasho_oaddr = InvalidOvflAddress; pageopaque->hasho_prevblkno = InvalidBlockNumber; pageopaque->hasho_nextblkno = InvalidBlockNumber; - pageopaque->hasho_flag = LH_BUCKET_PAGE; - pageopaque->hasho_bucket = i; - _hash_wrtbuf(rel, buf); - } - - _hash_relbuf(rel, metabuf, HASH_WRITE); - - if (USELOCKING) - RelationUnsetLockForWrite(rel); + pageopaque->hasho_flag = LH_META_PAGE; + pageopaque->hasho_bucket = -1; + + /* + * First bitmap page is at: splitpoint lg2nelem page offset 1 which + * turns out to be page 3. Couldn't initialize page 3 until we + * created the first two buckets above. + */ + if (_hash_initbitmap(rel, metap, OADDR_OF(lg2nelem, 1), lg2nelem + 1, 0)) + elog(WARN, "Problem with _hash_initbitmap."); + + /* all done */ + _hash_wrtnorelbuf(rel, metabuf); + + /* + * initialize the first two buckets + */ + for (i = 0; i <= 1; i++) + { + buf = _hash_getbuf(rel, BUCKET_TO_BLKNO(i), HASH_WRITE); + pg = BufferGetPage(buf); + _hash_pageinit(pg, BufferGetPageSize(buf)); + pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg); + pageopaque->hasho_oaddr = InvalidOvflAddress; + pageopaque->hasho_prevblkno = InvalidBlockNumber; + pageopaque->hasho_nextblkno = InvalidBlockNumber; + pageopaque->hasho_flag = LH_BUCKET_PAGE; + pageopaque->hasho_bucket = i; + _hash_wrtbuf(rel, buf); + } + + _hash_relbuf(rel, metabuf, HASH_WRITE); + + if (USELOCKING) + RelationUnsetLockForWrite(rel); } /* - * _hash_getbuf() -- Get a buffer by block number for read or write. + * _hash_getbuf() -- Get a buffer by block number for read or write. * - * When this routine returns, the appropriate lock is set on the - * requested buffer its reference count is correct. + * When this routine returns, the appropriate lock is set on the + * requested buffer its reference count is correct. * - * XXX P_NEW is not used because, unlike the tree structures, we - * need the bucket blocks to be at certain block numbers. we must - * depend on the caller to call _hash_pageinit on the block if it - * knows that this is a new block. + * XXX P_NEW is not used because, unlike the tree structures, we + * need the bucket blocks to be at certain block numbers. we must + * depend on the caller to call _hash_pageinit on the block if it + * knows that this is a new block. */ Buffer _hash_getbuf(Relation rel, BlockNumber blkno, int access) { - Buffer buf; - - if (blkno == P_NEW) { - elog(WARN, "_hash_getbuf: internal error: hash AM does not use P_NEW"); - } - switch (access) { - case HASH_WRITE: - case HASH_READ: - _hash_setpagelock(rel, blkno, access); - break; - default: - elog(WARN, "_hash_getbuf: invalid access (%d) on new blk: %s", - access, RelationGetRelationName(rel)); - break; - } - buf = ReadBuffer(rel, blkno); - - /* ref count and lock type are correct */ - return (buf); + Buffer buf; + + if (blkno == P_NEW) + { + elog(WARN, "_hash_getbuf: internal error: hash AM does not use P_NEW"); + } + switch (access) + { + case HASH_WRITE: + case HASH_READ: + _hash_setpagelock(rel, blkno, access); + break; + default: + elog(WARN, "_hash_getbuf: invalid access (%d) on new blk: %s", + access, RelationGetRelationName(rel)); + break; + } + buf = ReadBuffer(rel, blkno); + + /* ref count and lock type are correct */ + return (buf); } /* - * _hash_relbuf() -- release a locked buffer. + * _hash_relbuf() -- release a locked buffer. */ void _hash_relbuf(Relation rel, Buffer buf, int access) { - BlockNumber blkno; - - blkno = BufferGetBlockNumber(buf); - - switch (access) { - case HASH_WRITE: - case HASH_READ: - _hash_unsetpagelock(rel, blkno, access); - break; - default: - elog(WARN, "_hash_relbuf: invalid access (%d) on blk %x: %s", - access, blkno, RelationGetRelationName(rel)); - } - - ReleaseBuffer(buf); + BlockNumber blkno; + + blkno = BufferGetBlockNumber(buf); + + switch (access) + { + case HASH_WRITE: + case HASH_READ: + _hash_unsetpagelock(rel, blkno, access); + break; + default: + elog(WARN, "_hash_relbuf: invalid access (%d) on blk %x: %s", + access, blkno, RelationGetRelationName(rel)); + } + + ReleaseBuffer(buf); } /* - * _hash_wrtbuf() -- write a hash page to disk. + * _hash_wrtbuf() -- write a hash page to disk. * - * This routine releases the lock held on the buffer and our reference - * to it. It is an error to call _hash_wrtbuf() without a write lock - * or a reference to the buffer. + * This routine releases the lock held on the buffer and our reference + * to it. It is an error to call _hash_wrtbuf() without a write lock + * or a reference to the buffer. */ void _hash_wrtbuf(Relation rel, Buffer buf) { - BlockNumber blkno; - - blkno = BufferGetBlockNumber(buf); - WriteBuffer(buf); - _hash_unsetpagelock(rel, blkno, HASH_WRITE); + BlockNumber blkno; + + blkno = BufferGetBlockNumber(buf); + WriteBuffer(buf); + _hash_unsetpagelock(rel, blkno, HASH_WRITE); } /* - * _hash_wrtnorelbuf() -- write a hash page to disk, but do not release - * our reference or lock. + * _hash_wrtnorelbuf() -- write a hash page to disk, but do not release + * our reference or lock. * - * It is an error to call _hash_wrtnorelbuf() without a write lock - * or a reference to the buffer. + * It is an error to call _hash_wrtnorelbuf() without a write lock + * or a reference to the buffer. */ void _hash_wrtnorelbuf(Relation rel, Buffer buf) { - BlockNumber blkno; - - blkno = BufferGetBlockNumber(buf); - WriteNoReleaseBuffer(buf); + BlockNumber blkno; + + blkno = BufferGetBlockNumber(buf); + WriteNoReleaseBuffer(buf); } Page _hash_chgbufaccess(Relation rel, - Buffer *bufp, - int from_access, - int to_access) + Buffer * bufp, + int from_access, + int to_access) { - BlockNumber blkno; - - blkno = BufferGetBlockNumber(*bufp); - - switch (from_access) { - case HASH_WRITE: - _hash_wrtbuf(rel, *bufp); - break; - case HASH_READ: - _hash_relbuf(rel, *bufp, from_access); - break; - default: - elog(WARN, "_hash_chgbufaccess: invalid access (%d) on blk %x: %s", - from_access, blkno, RelationGetRelationName(rel)); - break; - } - *bufp = _hash_getbuf(rel, blkno, to_access); - return (BufferGetPage(*bufp)); + BlockNumber blkno; + + blkno = BufferGetBlockNumber(*bufp); + + switch (from_access) + { + case HASH_WRITE: + _hash_wrtbuf(rel, *bufp); + break; + case HASH_READ: + _hash_relbuf(rel, *bufp, from_access); + break; + default: + elog(WARN, "_hash_chgbufaccess: invalid access (%d) on blk %x: %s", + from_access, blkno, RelationGetRelationName(rel)); + break; + } + *bufp = _hash_getbuf(rel, blkno, to_access); + return (BufferGetPage(*bufp)); } /* - * _hash_pageinit() -- Initialize a new page. + * _hash_pageinit() -- Initialize a new page. */ void _hash_pageinit(Page page, Size size) { - Assert(((PageHeader) page)->pd_lower == 0); - Assert(((PageHeader) page)->pd_upper == 0); - Assert(((PageHeader) page)->pd_special == 0); - - /* - * Cargo-cult programming -- don't really need this to be zero, but - * creating new pages is an infrequent occurrence and it makes me feel - * good when I know they're empty. - */ - memset(page, 0, size); - - PageInit(page, size, sizeof(HashPageOpaqueData)); + Assert(((PageHeader) page)->pd_lower == 0); + Assert(((PageHeader) page)->pd_upper == 0); + Assert(((PageHeader) page)->pd_special == 0); + + /* + * Cargo-cult programming -- don't really need this to be zero, but + * creating new pages is an infrequent occurrence and it makes me feel + * good when I know they're empty. + */ + memset(page, 0, size); + + PageInit(page, size, sizeof(HashPageOpaqueData)); } static void _hash_setpagelock(Relation rel, - BlockNumber blkno, - int access) + BlockNumber blkno, + int access) { - ItemPointerData iptr; - - if (USELOCKING) { - ItemPointerSet(&iptr, blkno, 1); - - switch (access) { - case HASH_WRITE: - RelationSetSingleWLockPage(rel, &iptr); - break; - case HASH_READ: - RelationSetSingleRLockPage(rel, &iptr); - break; - default: - elog(WARN, "_hash_setpagelock: invalid access (%d) on blk %x: %s", - access, blkno, RelationGetRelationName(rel)); - break; + ItemPointerData iptr; + + if (USELOCKING) + { + ItemPointerSet(&iptr, blkno, 1); + + switch (access) + { + case HASH_WRITE: + RelationSetSingleWLockPage(rel, &iptr); + break; + case HASH_READ: + RelationSetSingleRLockPage(rel, &iptr); + break; + default: + elog(WARN, "_hash_setpagelock: invalid access (%d) on blk %x: %s", + access, blkno, RelationGetRelationName(rel)); + break; + } } - } } static void _hash_unsetpagelock(Relation rel, - BlockNumber blkno, - int access) + BlockNumber blkno, + int access) { - ItemPointerData iptr; - - if (USELOCKING) { - ItemPointerSet(&iptr, blkno, 1); - - switch (access) { - case HASH_WRITE: - RelationUnsetSingleWLockPage(rel, &iptr); - break; - case HASH_READ: - RelationUnsetSingleRLockPage(rel, &iptr); - break; - default: - elog(WARN, "_hash_unsetpagelock: invalid access (%d) on blk %x: %s", - access, blkno, RelationGetRelationName(rel)); - break; + ItemPointerData iptr; + + if (USELOCKING) + { + ItemPointerSet(&iptr, blkno, 1); + + switch (access) + { + case HASH_WRITE: + RelationUnsetSingleWLockPage(rel, &iptr); + break; + case HASH_READ: + RelationUnsetSingleRLockPage(rel, &iptr); + break; + default: + elog(WARN, "_hash_unsetpagelock: invalid access (%d) on blk %x: %s", + access, blkno, RelationGetRelationName(rel)); + break; + } } - } } void _hash_pagedel(Relation rel, ItemPointer tid) { - Buffer buf; - Buffer metabuf; - Page page; - BlockNumber blkno; - OffsetNumber offno; - HashMetaPage metap; - HashPageOpaque opaque; - - blkno = ItemPointerGetBlockNumber(tid); - offno = ItemPointerGetOffsetNumber(tid); - - buf = _hash_getbuf(rel, blkno, HASH_WRITE); - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - opaque = (HashPageOpaque) PageGetSpecialPointer(page); - - PageIndexTupleDelete(page, offno); - _hash_wrtnorelbuf(rel, buf); - - if (PageIsEmpty(page) && (opaque->hasho_flag & LH_OVERFLOW_PAGE)) { - buf = _hash_freeovflpage(rel, buf); - if (BufferIsValid(buf)) { - _hash_relbuf(rel, buf, HASH_WRITE); + Buffer buf; + Buffer metabuf; + Page page; + BlockNumber blkno; + OffsetNumber offno; + HashMetaPage metap; + HashPageOpaque opaque; + + blkno = ItemPointerGetBlockNumber(tid); + offno = ItemPointerGetOffsetNumber(tid); + + buf = _hash_getbuf(rel, blkno, HASH_WRITE); + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + + PageIndexTupleDelete(page, offno); + _hash_wrtnorelbuf(rel, buf); + + if (PageIsEmpty(page) && (opaque->hasho_flag & LH_OVERFLOW_PAGE)) + { + buf = _hash_freeovflpage(rel, buf); + if (BufferIsValid(buf)) + { + _hash_relbuf(rel, buf, HASH_WRITE); + } } - } else { - _hash_relbuf(rel, buf, HASH_WRITE); - } - - metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE); - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - ++metap->hashm_nkeys; - _hash_wrtbuf(rel, metabuf); + else + { + _hash_relbuf(rel, buf, HASH_WRITE); + } + + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE); + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + ++metap->hashm_nkeys; + _hash_wrtbuf(rel, metabuf); } void _hash_expandtable(Relation rel, Buffer metabuf) { - HashMetaPage metap; - Bucket old_bucket; - Bucket new_bucket; - uint32 spare_ndx; - -/* elog(DEBUG, "_hash_expandtable: expanding..."); */ - - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE); - new_bucket = ++metap->MAX_BUCKET; - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ); - old_bucket = (metap->MAX_BUCKET & metap->LOW_MASK); - - /* - * If the split point is increasing (MAX_BUCKET's log base 2 - * * increases), we need to copy the current contents of the spare - * split bucket to the next bucket. - */ - spare_ndx = _hash_log2(metap->MAX_BUCKET + 1); - if (spare_ndx > metap->OVFL_POINT) { - - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE); - metap->SPARES[spare_ndx] = metap->SPARES[metap->OVFL_POINT]; - metap->OVFL_POINT = spare_ndx; - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ); - } - - if (new_bucket > metap->HIGH_MASK) { - - /* Starting a new doubling */ - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE); - metap->LOW_MASK = metap->HIGH_MASK; - metap->HIGH_MASK = new_bucket | metap->LOW_MASK; - metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ); - - } - /* Relocate records to the new bucket */ - _hash_splitpage(rel, metabuf, old_bucket, new_bucket); + HashMetaPage metap; + Bucket old_bucket; + Bucket new_bucket; + uint32 spare_ndx; + +/* elog(DEBUG, "_hash_expandtable: expanding..."); */ + + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE); + new_bucket = ++metap->MAX_BUCKET; + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ); + old_bucket = (metap->MAX_BUCKET & metap->LOW_MASK); + + /* + * If the split point is increasing (MAX_BUCKET's log base 2 * + * increases), we need to copy the current contents of the spare split + * bucket to the next bucket. + */ + spare_ndx = _hash_log2(metap->MAX_BUCKET + 1); + if (spare_ndx > metap->OVFL_POINT) + { + + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE); + metap->SPARES[spare_ndx] = metap->SPARES[metap->OVFL_POINT]; + metap->OVFL_POINT = spare_ndx; + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ); + } + + if (new_bucket > metap->HIGH_MASK) + { + + /* Starting a new doubling */ + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE); + metap->LOW_MASK = metap->HIGH_MASK; + metap->HIGH_MASK = new_bucket | metap->LOW_MASK; + metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ); + + } + /* Relocate records to the new bucket */ + _hash_splitpage(rel, metabuf, old_bucket, new_bucket); } @@ -450,224 +468,243 @@ _hash_expandtable(Relation rel, Buffer metabuf) */ static void _hash_splitpage(Relation rel, - Buffer metabuf, - Bucket obucket, - Bucket nbucket) + Buffer metabuf, + Bucket obucket, + Bucket nbucket) { - Bucket bucket; - Buffer obuf; - Buffer nbuf; - Buffer ovflbuf; - BlockNumber oblkno; - BlockNumber nblkno; - bool null; - Datum datum; - HashItem hitem; - HashPageOpaque oopaque; - HashPageOpaque nopaque; - HashMetaPage metap; - IndexTuple itup; - int itemsz; - OffsetNumber ooffnum; - OffsetNumber noffnum; - OffsetNumber omaxoffnum; - Page opage; - Page npage; - TupleDesc itupdesc; - -/* elog(DEBUG, "_hash_splitpage: splitting %d into %d,%d", - obucket, obucket, nbucket); + Bucket bucket; + Buffer obuf; + Buffer nbuf; + Buffer ovflbuf; + BlockNumber oblkno; + BlockNumber nblkno; + bool null; + Datum datum; + HashItem hitem; + HashPageOpaque oopaque; + HashPageOpaque nopaque; + HashMetaPage metap; + IndexTuple itup; + int itemsz; + OffsetNumber ooffnum; + OffsetNumber noffnum; + OffsetNumber omaxoffnum; + Page opage; + Page npage; + TupleDesc itupdesc; + +/* elog(DEBUG, "_hash_splitpage: splitting %d into %d,%d", + obucket, obucket, nbucket); */ - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - - /* get the buffers & pages */ - oblkno = BUCKET_TO_BLKNO(obucket); - nblkno = BUCKET_TO_BLKNO(nbucket); - obuf = _hash_getbuf(rel, oblkno, HASH_WRITE); - nbuf = _hash_getbuf(rel, nblkno, HASH_WRITE); - opage = BufferGetPage(obuf); - npage = BufferGetPage(nbuf); - - /* initialize the new bucket */ - _hash_pageinit(npage, BufferGetPageSize(nbuf)); - nopaque = (HashPageOpaque) PageGetSpecialPointer(npage); - nopaque->hasho_prevblkno = InvalidBlockNumber; - nopaque->hasho_nextblkno = InvalidBlockNumber; - nopaque->hasho_flag = LH_BUCKET_PAGE; - nopaque->hasho_oaddr = InvalidOvflAddress; - nopaque->hasho_bucket = nbucket; - _hash_wrtnorelbuf(rel, nbuf); - - /* - * make sure the old bucket isn't empty. advance 'opage' and - * friends through the overflow bucket chain until we find a - * non-empty page. - * - * XXX we should only need this once, if we are careful to - * preserve the invariant that overflow pages are never empty. - */ - _hash_checkpage(opage, LH_BUCKET_PAGE); - oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); - if (PageIsEmpty(opage)) { - oblkno = oopaque->hasho_nextblkno; - _hash_relbuf(rel, obuf, HASH_WRITE); - if (!BlockNumberIsValid(oblkno)) { - /* - * the old bucket is completely empty; of course, the new - * bucket will be as well, but since it's a base bucket - * page we don't care. - */ - _hash_relbuf(rel, nbuf, HASH_WRITE); - return; - } + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + + /* get the buffers & pages */ + oblkno = BUCKET_TO_BLKNO(obucket); + nblkno = BUCKET_TO_BLKNO(nbucket); obuf = _hash_getbuf(rel, oblkno, HASH_WRITE); + nbuf = _hash_getbuf(rel, nblkno, HASH_WRITE); opage = BufferGetPage(obuf); - _hash_checkpage(opage, LH_OVERFLOW_PAGE); - if (PageIsEmpty(opage)) { - elog(WARN, "_hash_splitpage: empty overflow page %d", oblkno); - } - oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); - } - - /* - * we are now guaranteed that 'opage' is not empty. partition the - * tuples in the old bucket between the old bucket and the new - * bucket, advancing along their respective overflow bucket chains - * and adding overflow pages as needed. - */ - ooffnum = FirstOffsetNumber; - omaxoffnum = PageGetMaxOffsetNumber(opage); - for (;;) { + npage = BufferGetPage(nbuf); + + /* initialize the new bucket */ + _hash_pageinit(npage, BufferGetPageSize(nbuf)); + nopaque = (HashPageOpaque) PageGetSpecialPointer(npage); + nopaque->hasho_prevblkno = InvalidBlockNumber; + nopaque->hasho_nextblkno = InvalidBlockNumber; + nopaque->hasho_flag = LH_BUCKET_PAGE; + nopaque->hasho_oaddr = InvalidOvflAddress; + nopaque->hasho_bucket = nbucket; + _hash_wrtnorelbuf(rel, nbuf); + /* - * at each iteration through this loop, each of these variables - * should be up-to-date: obuf opage oopaque ooffnum omaxoffnum + * make sure the old bucket isn't empty. advance 'opage' and friends + * through the overflow bucket chain until we find a non-empty page. + * + * XXX we should only need this once, if we are careful to preserve the + * invariant that overflow pages are never empty. */ + _hash_checkpage(opage, LH_BUCKET_PAGE); + oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); + if (PageIsEmpty(opage)) + { + oblkno = oopaque->hasho_nextblkno; + _hash_relbuf(rel, obuf, HASH_WRITE); + if (!BlockNumberIsValid(oblkno)) + { - /* check if we're at the end of the page */ - if (ooffnum > omaxoffnum) { - /* at end of page, but check for overflow page */ - oblkno = oopaque->hasho_nextblkno; - if (BlockNumberIsValid(oblkno)) { - /* - * we ran out of tuples on this particular page, but - * we have more overflow pages; re-init values. - */ - _hash_wrtbuf(rel, obuf); + /* + * the old bucket is completely empty; of course, the new + * bucket will be as well, but since it's a base bucket page + * we don't care. + */ + _hash_relbuf(rel, nbuf, HASH_WRITE); + return; + } obuf = _hash_getbuf(rel, oblkno, HASH_WRITE); opage = BufferGetPage(obuf); _hash_checkpage(opage, LH_OVERFLOW_PAGE); - oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); - - /* we're guaranteed that an ovfl page has at least 1 tuple */ - if (PageIsEmpty(opage)) { - elog(WARN, "_hash_splitpage: empty ovfl page %d!", - oblkno); + if (PageIsEmpty(opage)) + { + elog(WARN, "_hash_splitpage: empty overflow page %d", oblkno); } - ooffnum = FirstOffsetNumber; - omaxoffnum = PageGetMaxOffsetNumber(opage); - } else { + oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); + } + + /* + * we are now guaranteed that 'opage' is not empty. partition the + * tuples in the old bucket between the old bucket and the new bucket, + * advancing along their respective overflow bucket chains and adding + * overflow pages as needed. + */ + ooffnum = FirstOffsetNumber; + omaxoffnum = PageGetMaxOffsetNumber(opage); + for (;;) + { + /* - * we're at the end of the bucket chain, so now we're - * really done with everything. before quitting, call - * _hash_squeezebucket to ensure the tuples in the - * bucket (including the overflow pages) are packed as - * tightly as possible. + * at each iteration through this loop, each of these variables + * should be up-to-date: obuf opage oopaque ooffnum omaxoffnum */ - _hash_wrtbuf(rel, obuf); - _hash_wrtbuf(rel, nbuf); - _hash_squeezebucket(rel, metap, obucket); - return; - } - } - - /* hash on the tuple */ - hitem = (HashItem) PageGetItem(opage, PageGetItemId(opage, ooffnum)); - itup = &(hitem->hash_itup); - itupdesc = RelationGetTupleDescriptor(rel); - datum = index_getattr(itup, 1, itupdesc, &null); - bucket = _hash_call(rel, metap, datum); - - if (bucket == nbucket) { - /* - * insert the tuple into the new bucket. if it doesn't - * fit on the current page in the new bucket, we must - * allocate a new overflow page and place the tuple on - * that page instead. - */ - itemsz = IndexTupleDSize(hitem->hash_itup) - + (sizeof(HashItemData) - sizeof(IndexTupleData)); - - itemsz = DOUBLEALIGN(itemsz); - - if (PageGetFreeSpace(npage) < itemsz) { - ovflbuf = _hash_addovflpage(rel, &metabuf, nbuf); - _hash_wrtbuf(rel, nbuf); - nbuf = ovflbuf; - npage = BufferGetPage(nbuf); - _hash_checkpage(npage, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - } - - noffnum = OffsetNumberNext(PageGetMaxOffsetNumber(npage)); - PageAddItem(npage, (Item) hitem, itemsz, noffnum, LP_USED); - _hash_wrtnorelbuf(rel, nbuf); - - /* - * now delete the tuple from the old bucket. after this - * section of code, 'ooffnum' will actually point to the - * ItemId to which we would point if we had advanced it - * before the deletion (PageIndexTupleDelete repacks the - * ItemId array). this also means that 'omaxoffnum' is - * exactly one less than it used to be, so we really can - * just decrement it instead of calling - * PageGetMaxOffsetNumber. - */ - PageIndexTupleDelete(opage, ooffnum); - _hash_wrtnorelbuf(rel, obuf); - omaxoffnum = OffsetNumberPrev(omaxoffnum); - - /* - * tidy up. if the old page was an overflow page and it - * is now empty, we must free it (we want to preserve the - * invariant that overflow pages cannot be empty). - */ - if (PageIsEmpty(opage) && - (oopaque->hasho_flag & LH_OVERFLOW_PAGE)) { - obuf = _hash_freeovflpage(rel, obuf); - - /* check that we're not through the bucket chain */ - if (BufferIsInvalid(obuf)) { - _hash_wrtbuf(rel, nbuf); - _hash_squeezebucket(rel, metap, obucket); - return; + + /* check if we're at the end of the page */ + if (ooffnum > omaxoffnum) + { + /* at end of page, but check for overflow page */ + oblkno = oopaque->hasho_nextblkno; + if (BlockNumberIsValid(oblkno)) + { + + /* + * we ran out of tuples on this particular page, but we + * have more overflow pages; re-init values. + */ + _hash_wrtbuf(rel, obuf); + obuf = _hash_getbuf(rel, oblkno, HASH_WRITE); + opage = BufferGetPage(obuf); + _hash_checkpage(opage, LH_OVERFLOW_PAGE); + oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); + + /* we're guaranteed that an ovfl page has at least 1 tuple */ + if (PageIsEmpty(opage)) + { + elog(WARN, "_hash_splitpage: empty ovfl page %d!", + oblkno); + } + ooffnum = FirstOffsetNumber; + omaxoffnum = PageGetMaxOffsetNumber(opage); + } + else + { + + /* + * we're at the end of the bucket chain, so now we're + * really done with everything. before quitting, call + * _hash_squeezebucket to ensure the tuples in the bucket + * (including the overflow pages) are packed as tightly as + * possible. + */ + _hash_wrtbuf(rel, obuf); + _hash_wrtbuf(rel, nbuf); + _hash_squeezebucket(rel, metap, obucket); + return; + } } - - /* - * re-init. again, we're guaranteed that an ovfl page - * has at least one tuple. - */ - opage = BufferGetPage(obuf); - _hash_checkpage(opage, LH_OVERFLOW_PAGE); - oblkno = BufferGetBlockNumber(obuf); - oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); - if (PageIsEmpty(opage)) { - elog(WARN, "_hash_splitpage: empty overflow page %d", - oblkno); + + /* hash on the tuple */ + hitem = (HashItem) PageGetItem(opage, PageGetItemId(opage, ooffnum)); + itup = &(hitem->hash_itup); + itupdesc = RelationGetTupleDescriptor(rel); + datum = index_getattr(itup, 1, itupdesc, &null); + bucket = _hash_call(rel, metap, datum); + + if (bucket == nbucket) + { + + /* + * insert the tuple into the new bucket. if it doesn't fit on + * the current page in the new bucket, we must allocate a new + * overflow page and place the tuple on that page instead. + */ + itemsz = IndexTupleDSize(hitem->hash_itup) + + (sizeof(HashItemData) - sizeof(IndexTupleData)); + + itemsz = DOUBLEALIGN(itemsz); + + if (PageGetFreeSpace(npage) < itemsz) + { + ovflbuf = _hash_addovflpage(rel, &metabuf, nbuf); + _hash_wrtbuf(rel, nbuf); + nbuf = ovflbuf; + npage = BufferGetPage(nbuf); + _hash_checkpage(npage, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + } + + noffnum = OffsetNumberNext(PageGetMaxOffsetNumber(npage)); + PageAddItem(npage, (Item) hitem, itemsz, noffnum, LP_USED); + _hash_wrtnorelbuf(rel, nbuf); + + /* + * now delete the tuple from the old bucket. after this + * section of code, 'ooffnum' will actually point to the + * ItemId to which we would point if we had advanced it before + * the deletion (PageIndexTupleDelete repacks the ItemId + * array). this also means that 'omaxoffnum' is exactly one + * less than it used to be, so we really can just decrement it + * instead of calling PageGetMaxOffsetNumber. + */ + PageIndexTupleDelete(opage, ooffnum); + _hash_wrtnorelbuf(rel, obuf); + omaxoffnum = OffsetNumberPrev(omaxoffnum); + + /* + * tidy up. if the old page was an overflow page and it is + * now empty, we must free it (we want to preserve the + * invariant that overflow pages cannot be empty). + */ + if (PageIsEmpty(opage) && + (oopaque->hasho_flag & LH_OVERFLOW_PAGE)) + { + obuf = _hash_freeovflpage(rel, obuf); + + /* check that we're not through the bucket chain */ + if (BufferIsInvalid(obuf)) + { + _hash_wrtbuf(rel, nbuf); + _hash_squeezebucket(rel, metap, obucket); + return; + } + + /* + * re-init. again, we're guaranteed that an ovfl page has + * at least one tuple. + */ + opage = BufferGetPage(obuf); + _hash_checkpage(opage, LH_OVERFLOW_PAGE); + oblkno = BufferGetBlockNumber(obuf); + oopaque = (HashPageOpaque) PageGetSpecialPointer(opage); + if (PageIsEmpty(opage)) + { + elog(WARN, "_hash_splitpage: empty overflow page %d", + oblkno); + } + ooffnum = FirstOffsetNumber; + omaxoffnum = PageGetMaxOffsetNumber(opage); + } + } + else + { + + /* + * the tuple stays on this page. we didn't move anything, so + * we didn't delete anything and therefore we don't have to + * change 'omaxoffnum'. + * + * XXX any hash value from [0, nbucket-1] will map to this + * bucket, which doesn't make sense to me. + */ + ooffnum = OffsetNumberNext(ooffnum); } - ooffnum = FirstOffsetNumber; - omaxoffnum = PageGetMaxOffsetNumber(opage); - } - } else { - /* - * the tuple stays on this page. we didn't move anything, - * so we didn't delete anything and therefore we don't - * have to change 'omaxoffnum'. - * - * XXX any hash value from [0, nbucket-1] will map to this - * bucket, which doesn't make sense to me. - */ - ooffnum = OffsetNumberNext(ooffnum); } - } - /*NOTREACHED*/ + /* NOTREACHED */ } diff --git a/src/backend/access/hash/hashscan.c b/src/backend/access/hash/hashscan.c index bd776d68c0d..79fa33f747c 100644 --- a/src/backend/access/hash/hashscan.c +++ b/src/backend/access/hash/hashscan.c @@ -1,160 +1,167 @@ /*------------------------------------------------------------------------- * * hashscan.c-- - * manage scans on hash tables + * manage scans on hash tables * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.8 1996/11/15 18:36:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.9 1997/09/07 04:38:01 momjian Exp $ * * NOTES - * Because we can be doing an index scan on a relation while we - * update it, we need to avoid missing data that moves around in - * the index. The routines and global variables in this file - * guarantee that all scans in the local address space stay - * correctly positioned. This is all we need to worry about, since - * write locking guarantees that no one else will be on the same - * page at the same time as we are. + * Because we can be doing an index scan on a relation while we + * update it, we need to avoid missing data that moves around in + * the index. The routines and global variables in this file + * guarantee that all scans in the local address space stay + * correctly positioned. This is all we need to worry about, since + * write locking guarantees that no one else will be on the same + * page at the same time as we are. * - * The scheme is to manage a list of active scans in the current - * backend. Whenever we add or remove records from an index, we - * check the list of active scans to see if any has been affected. - * A scan is affected only if it is on the same relation, and the - * same page, as the update. + * The scheme is to manage a list of active scans in the current + * backend. Whenever we add or remove records from an index, we + * check the list of active scans to see if any has been affected. + * A scan is affected only if it is on the same relation, and the + * same page, as the update. * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/hash.h> -static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); -static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); +static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); +static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); -typedef struct HashScanListData { - IndexScanDesc hashsl_scan; - struct HashScanListData *hashsl_next; -} HashScanListData; +typedef struct HashScanListData +{ + IndexScanDesc hashsl_scan; + struct HashScanListData *hashsl_next; +} HashScanListData; -typedef HashScanListData *HashScanList; +typedef HashScanListData *HashScanList; -static HashScanList HashScans = (HashScanList) NULL; +static HashScanList HashScans = (HashScanList) NULL; /* - * _Hash_regscan() -- register a new scan. + * _Hash_regscan() -- register a new scan. */ void _hash_regscan(IndexScanDesc scan) { - HashScanList new_el; - - new_el = (HashScanList) palloc(sizeof(HashScanListData)); - new_el->hashsl_scan = scan; - new_el->hashsl_next = HashScans; - HashScans = new_el; + HashScanList new_el; + + new_el = (HashScanList) palloc(sizeof(HashScanListData)); + new_el->hashsl_scan = scan; + new_el->hashsl_next = HashScans; + HashScans = new_el; } /* - * _hash_dropscan() -- drop a scan from the scan list + * _hash_dropscan() -- drop a scan from the scan list */ void _hash_dropscan(IndexScanDesc scan) { - HashScanList chk, last; - - last = (HashScanList) NULL; - for (chk = HashScans; - chk != (HashScanList) NULL && chk->hashsl_scan != scan; - chk = chk->hashsl_next) { - last = chk; - } - - if (chk == (HashScanList) NULL) - elog(WARN, "hash scan list trashed; can't find 0x%lx", scan); - - if (last == (HashScanList) NULL) - HashScans = chk->hashsl_next; - else - last->hashsl_next = chk->hashsl_next; - - pfree (chk); + HashScanList chk, + last; + + last = (HashScanList) NULL; + for (chk = HashScans; + chk != (HashScanList) NULL && chk->hashsl_scan != scan; + chk = chk->hashsl_next) + { + last = chk; + } + + if (chk == (HashScanList) NULL) + elog(WARN, "hash scan list trashed; can't find 0x%lx", scan); + + if (last == (HashScanList) NULL) + HashScans = chk->hashsl_next; + else + last->hashsl_next = chk->hashsl_next; + + pfree(chk); } void _hash_adjscans(Relation rel, ItemPointer tid) { - HashScanList l; - Oid relid; - - relid = rel->rd_id; - for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next) { - if (relid == l->hashsl_scan->relation->rd_id) - _hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid), - ItemPointerGetOffsetNumber(tid)); - } + HashScanList l; + Oid relid; + + relid = rel->rd_id; + for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next) + { + if (relid == l->hashsl_scan->relation->rd_id) + _hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid), + ItemPointerGetOffsetNumber(tid)); + } } static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) { - ItemPointer current; - Buffer buf; - Buffer metabuf; - HashScanOpaque so; - - if (!_hash_scantouched(scan, blkno, offno)) - return; - - metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ); - - so = (HashScanOpaque) scan->opaque; - buf = so->hashso_curbuf; - - current = &(scan->currentItemData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) { - _hash_step(scan, &buf, BackwardScanDirection, metabuf); - so->hashso_curbuf = buf; - } - - current = &(scan->currentMarkData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) { - ItemPointerData tmp; - tmp = *current; - *current = scan->currentItemData; - scan->currentItemData = tmp; - _hash_step(scan, &buf, BackwardScanDirection, metabuf); - so->hashso_mrkbuf = buf; - tmp = *current; - *current = scan->currentItemData; - scan->currentItemData = tmp; - } + ItemPointer current; + Buffer buf; + Buffer metabuf; + HashScanOpaque so; + + if (!_hash_scantouched(scan, blkno, offno)) + return; + + metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ); + + so = (HashScanOpaque) scan->opaque; + buf = so->hashso_curbuf; + + current = &(scan->currentItemData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + { + _hash_step(scan, &buf, BackwardScanDirection, metabuf); + so->hashso_curbuf = buf; + } + + current = &(scan->currentMarkData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + { + ItemPointerData tmp; + + tmp = *current; + *current = scan->currentItemData; + scan->currentItemData = tmp; + _hash_step(scan, &buf, BackwardScanDirection, metabuf); + so->hashso_mrkbuf = buf; + tmp = *current; + *current = scan->currentItemData; + scan->currentItemData = tmp; + } } -static bool +static bool _hash_scantouched(IndexScanDesc scan, - BlockNumber blkno, - OffsetNumber offno) + BlockNumber blkno, + OffsetNumber offno) { - ItemPointer current; - - current = &(scan->currentItemData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) - return (true); - - current = &(scan->currentMarkData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) - return (true); - - return (false); + ItemPointer current; + + current = &(scan->currentItemData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + return (true); + + current = &(scan->currentMarkData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + return (true); + + return (false); } diff --git a/src/backend/access/hash/hashsearch.c b/src/backend/access/hash/hashsearch.c index bc67b7f5aac..0a42ad05065 100644 --- a/src/backend/access/hash/hashsearch.c +++ b/src/backend/access/hash/hashsearch.c @@ -1,423 +1,467 @@ /*------------------------------------------------------------------------- * * hashsearch.c-- - * search code for postgres hash tables + * search code for postgres hash tables * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.10 1997/06/28 05:45:40 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.11 1997/09/07 04:38:02 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/hash.h> #include <storage/bufmgr.h> #ifndef HAVE_MEMMOVE -# include "regex/utils.h" +#include "regex/utils.h" #else -# include <string.h> -#endif +#include <string.h> +#endif /* - * _hash_search() -- Finds the page/bucket that the contains the - * scankey and loads it into *bufP. the buffer has a read lock. + * _hash_search() -- Finds the page/bucket that the contains the + * scankey and loads it into *bufP. the buffer has a read lock. */ void _hash_search(Relation rel, - int keysz, - ScanKey scankey, - Buffer *bufP, - HashMetaPage metap) + int keysz, + ScanKey scankey, + Buffer * bufP, + HashMetaPage metap) { - BlockNumber blkno; - Datum keyDatum; - Bucket bucket; - - if (scankey == (ScanKey) NULL || - (keyDatum = scankey[0].sk_argument) == (Datum) NULL) { - /* - * If the scankey argument is NULL, all tuples will satisfy - * the scan so we start the scan at the first bucket (bucket - * 0). - */ - bucket = 0; - } else { - bucket = _hash_call(rel, metap, keyDatum); - } - - blkno = BUCKET_TO_BLKNO(bucket); - - *bufP = _hash_getbuf(rel, blkno, HASH_READ); + BlockNumber blkno; + Datum keyDatum; + Bucket bucket; + + if (scankey == (ScanKey) NULL || + (keyDatum = scankey[0].sk_argument) == (Datum) NULL) + { + + /* + * If the scankey argument is NULL, all tuples will satisfy the + * scan so we start the scan at the first bucket (bucket 0). + */ + bucket = 0; + } + else + { + bucket = _hash_call(rel, metap, keyDatum); + } + + blkno = BUCKET_TO_BLKNO(bucket); + + *bufP = _hash_getbuf(rel, blkno, HASH_READ); } /* - * _hash_next() -- Get the next item in a scan. + * _hash_next() -- Get the next item in a scan. * - * On entry, we have a valid currentItemData in the scan, and a - * read lock on the page that contains that item. We do not have - * the page pinned. We return the next item in the scan. On - * exit, we have the page containing the next item locked but not - * pinned. + * On entry, we have a valid currentItemData in the scan, and a + * read lock on the page that contains that item. We do not have + * the page pinned. We return the next item in the scan. On + * exit, we have the page containing the next item locked but not + * pinned. */ RetrieveIndexResult _hash_next(IndexScanDesc scan, ScanDirection dir) { - Relation rel; - Buffer buf; - Buffer metabuf; - Page page; - OffsetNumber offnum; - RetrieveIndexResult res; - ItemPointer current; - HashItem hitem; - IndexTuple itup; - HashScanOpaque so; - - rel = scan->relation; - so = (HashScanOpaque) scan->opaque; - current = &(scan->currentItemData); - - metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); - - /* - * XXX 10 may 91: somewhere there's a bug in our management of the - * cached buffer for this scan. wei discovered it. the following - * is a workaround so he can work until i figure out what's going on. - */ - - if (!BufferIsValid(so->hashso_curbuf)) { - so->hashso_curbuf = _hash_getbuf(rel, - ItemPointerGetBlockNumber(current), - HASH_READ); - } - - /* we still have the buffer pinned and locked */ - buf = so->hashso_curbuf; - - /* - * step to next valid tuple. note that _hash_step releases our - * lock on 'metabuf'; if we switch to a new 'buf' while looking - * for the next tuple, we come back with a lock on that buffer. - */ - if (!_hash_step(scan, &buf, dir, metabuf)) { - return ((RetrieveIndexResult) NULL); - } - - /* if we're here, _hash_step found a valid tuple */ - current = &(scan->currentItemData); - offnum = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &hitem->hash_itup; - res = FormRetrieveIndexResult(current, &(itup->t_tid)); - - return (res); + Relation rel; + Buffer buf; + Buffer metabuf; + Page page; + OffsetNumber offnum; + RetrieveIndexResult res; + ItemPointer current; + HashItem hitem; + IndexTuple itup; + HashScanOpaque so; + + rel = scan->relation; + so = (HashScanOpaque) scan->opaque; + current = &(scan->currentItemData); + + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); + + /* + * XXX 10 may 91: somewhere there's a bug in our management of the + * cached buffer for this scan. wei discovered it. the following is + * a workaround so he can work until i figure out what's going on. + */ + + if (!BufferIsValid(so->hashso_curbuf)) + { + so->hashso_curbuf = _hash_getbuf(rel, + ItemPointerGetBlockNumber(current), + HASH_READ); + } + + /* we still have the buffer pinned and locked */ + buf = so->hashso_curbuf; + + /* + * step to next valid tuple. note that _hash_step releases our lock + * on 'metabuf'; if we switch to a new 'buf' while looking for the + * next tuple, we come back with a lock on that buffer. + */ + if (!_hash_step(scan, &buf, dir, metabuf)) + { + return ((RetrieveIndexResult) NULL); + } + + /* if we're here, _hash_step found a valid tuple */ + current = &(scan->currentItemData); + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &hitem->hash_itup; + res = FormRetrieveIndexResult(current, &(itup->t_tid)); + + return (res); } static void _hash_readnext(Relation rel, - Buffer *bufp, Page *pagep, HashPageOpaque *opaquep) + Buffer * bufp, Page * pagep, HashPageOpaque * opaquep) { - BlockNumber blkno; - - blkno = (*opaquep)->hasho_nextblkno; - _hash_relbuf(rel, *bufp, HASH_READ); - *bufp = InvalidBuffer; - if (BlockNumberIsValid(blkno)) { - *bufp = _hash_getbuf(rel, blkno, HASH_READ); - *pagep = BufferGetPage(*bufp); - _hash_checkpage(*pagep, LH_OVERFLOW_PAGE); - *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep); - Assert(!PageIsEmpty(*pagep)); - } + BlockNumber blkno; + + blkno = (*opaquep)->hasho_nextblkno; + _hash_relbuf(rel, *bufp, HASH_READ); + *bufp = InvalidBuffer; + if (BlockNumberIsValid(blkno)) + { + *bufp = _hash_getbuf(rel, blkno, HASH_READ); + *pagep = BufferGetPage(*bufp); + _hash_checkpage(*pagep, LH_OVERFLOW_PAGE); + *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep); + Assert(!PageIsEmpty(*pagep)); + } } static void _hash_readprev(Relation rel, - Buffer *bufp, Page *pagep, HashPageOpaque *opaquep) + Buffer * bufp, Page * pagep, HashPageOpaque * opaquep) { - BlockNumber blkno; - - blkno = (*opaquep)->hasho_prevblkno; - _hash_relbuf(rel, *bufp, HASH_READ); - *bufp = InvalidBuffer; - if (BlockNumberIsValid(blkno)) { - *bufp = _hash_getbuf(rel, blkno, HASH_READ); - *pagep = BufferGetPage(*bufp); - _hash_checkpage(*pagep, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep); - if (PageIsEmpty(*pagep)) { - Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE); - _hash_relbuf(rel, *bufp, HASH_READ); - *bufp = InvalidBuffer; + BlockNumber blkno; + + blkno = (*opaquep)->hasho_prevblkno; + _hash_relbuf(rel, *bufp, HASH_READ); + *bufp = InvalidBuffer; + if (BlockNumberIsValid(blkno)) + { + *bufp = _hash_getbuf(rel, blkno, HASH_READ); + *pagep = BufferGetPage(*bufp); + _hash_checkpage(*pagep, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep); + if (PageIsEmpty(*pagep)) + { + Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE); + _hash_relbuf(rel, *bufp, HASH_READ); + *bufp = InvalidBuffer; + } } - } } /* - * _hash_first() -- Find the first item in a scan. + * _hash_first() -- Find the first item in a scan. * - * Return the RetrieveIndexResult of the first item in the tree that - * satisfies the qualificatin associated with the scan descriptor. On - * exit, the page containing the current index tuple is read locked - * and pinned, and the scan's opaque data entry is updated to - * include the buffer. + * Return the RetrieveIndexResult of the first item in the tree that + * satisfies the qualificatin associated with the scan descriptor. On + * exit, the page containing the current index tuple is read locked + * and pinned, and the scan's opaque data entry is updated to + * include the buffer. */ RetrieveIndexResult _hash_first(IndexScanDesc scan, ScanDirection dir) { - Relation rel; - Buffer buf; - Buffer metabuf; - Page page; - HashPageOpaque opaque; - HashMetaPage metap; - HashItem hitem; - IndexTuple itup; - ItemPointer current; - OffsetNumber offnum; - RetrieveIndexResult res; - HashScanOpaque so; - - rel = scan->relation; - so = (HashScanOpaque) scan->opaque; - current = &(scan->currentItemData); - - metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - - /* - * XXX -- The attribute number stored in the scan key is the attno - * in the heap relation. We need to transmogrify this into - * the index relation attno here. For the moment, we have - * hardwired attno == 1. - */ - - /* find the correct bucket page and load it into buf */ - _hash_search(rel, 1, scan->keyData, &buf, metap); - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE); - opaque = (HashPageOpaque) PageGetSpecialPointer(page); - - /* - * if we are scanning forward, we need to find the first non-empty - * page (if any) in the bucket chain. since overflow pages are - * never empty, this had better be either the bucket page or the - * first overflow page. - * - * if we are scanning backward, we always go all the way to the - * end of the bucket chain. - */ - if (PageIsEmpty(page)) { - if (BlockNumberIsValid(opaque->hasho_nextblkno)) { - _hash_readnext(rel, &buf, &page, &opaque); - } else { - ItemPointerSetInvalid(current); - so->hashso_curbuf = InvalidBuffer; - /* - * If there is no scankeys, all tuples will satisfy - * the scan - so we continue in _hash_step to get - * tuples from all buckets. - vadim 04/29/97 - */ - if ( scan->numberOfKeys >= 1 ) - { - _hash_relbuf(rel, buf, HASH_READ); - _hash_relbuf(rel, metabuf, HASH_READ); - return ((RetrieveIndexResult) NULL); - } + Relation rel; + Buffer buf; + Buffer metabuf; + Page page; + HashPageOpaque opaque; + HashMetaPage metap; + HashItem hitem; + IndexTuple itup; + ItemPointer current; + OffsetNumber offnum; + RetrieveIndexResult res; + HashScanOpaque so; + + rel = scan->relation; + so = (HashScanOpaque) scan->opaque; + current = &(scan->currentItemData); + + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ); + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + + /* + * XXX -- The attribute number stored in the scan key is the attno in + * the heap relation. We need to transmogrify this into the index + * relation attno here. For the moment, we have hardwired attno == 1. + */ + + /* find the correct bucket page and load it into buf */ + _hash_search(rel, 1, scan->keyData, &buf, metap); + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE); + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + + /* + * if we are scanning forward, we need to find the first non-empty + * page (if any) in the bucket chain. since overflow pages are never + * empty, this had better be either the bucket page or the first + * overflow page. + * + * if we are scanning backward, we always go all the way to the end of + * the bucket chain. + */ + if (PageIsEmpty(page)) + { + if (BlockNumberIsValid(opaque->hasho_nextblkno)) + { + _hash_readnext(rel, &buf, &page, &opaque); + } + else + { + ItemPointerSetInvalid(current); + so->hashso_curbuf = InvalidBuffer; + + /* + * If there is no scankeys, all tuples will satisfy the scan - + * so we continue in _hash_step to get tuples from all + * buckets. - vadim 04/29/97 + */ + if (scan->numberOfKeys >= 1) + { + _hash_relbuf(rel, buf, HASH_READ); + _hash_relbuf(rel, metabuf, HASH_READ); + return ((RetrieveIndexResult) NULL); + } + } } - } - if (ScanDirectionIsBackward(dir)) { - while (BlockNumberIsValid(opaque->hasho_nextblkno)) { - _hash_readnext(rel, &buf, &page, &opaque); + if (ScanDirectionIsBackward(dir)) + { + while (BlockNumberIsValid(opaque->hasho_nextblkno)) + { + _hash_readnext(rel, &buf, &page, &opaque); + } + } + + if (!_hash_step(scan, &buf, dir, metabuf)) + { + return ((RetrieveIndexResult) NULL); } - } - - if (!_hash_step(scan, &buf, dir, metabuf)) { - return ((RetrieveIndexResult) NULL); - } - - /* if we're here, _hash_step found a valid tuple */ - current = &(scan->currentItemData); - offnum = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &hitem->hash_itup; - res = FormRetrieveIndexResult(current, &(itup->t_tid)); - - return (res); + + /* if we're here, _hash_step found a valid tuple */ + current = &(scan->currentItemData); + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &hitem->hash_itup; + res = FormRetrieveIndexResult(current, &(itup->t_tid)); + + return (res); } /* - * _hash_step() -- step to the next valid item in a scan in the bucket. + * _hash_step() -- step to the next valid item in a scan in the bucket. * - * If no valid record exists in the requested direction, return - * false. Else, return true and set the CurrentItemData for the - * scan to the right thing. - * - * 'bufP' points to the buffer which contains the current page - * that we'll step through. + * If no valid record exists in the requested direction, return + * false. Else, return true and set the CurrentItemData for the + * scan to the right thing. * - * 'metabuf' is released when this returns. + * 'bufP' points to the buffer which contains the current page + * that we'll step through. + * + * 'metabuf' is released when this returns. */ bool -_hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf) +_hash_step(IndexScanDesc scan, Buffer * bufP, ScanDirection dir, Buffer metabuf) { - Relation rel; - ItemPointer current; - HashScanOpaque so; - int allbuckets; - HashMetaPage metap; - Buffer buf; - Page page; - HashPageOpaque opaque; - OffsetNumber maxoff; - OffsetNumber offnum; - Bucket bucket; - BlockNumber blkno; - HashItem hitem; - IndexTuple itup; - - rel = scan->relation; - current = &(scan->currentItemData); - so = (HashScanOpaque) scan->opaque; - allbuckets = (scan->numberOfKeys < 1); - - metap = (HashMetaPage) BufferGetPage(metabuf); - _hash_checkpage((Page) metap, LH_META_PAGE); - - buf = *bufP; - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE); - opaque = (HashPageOpaque) PageGetSpecialPointer(page); - - /* - * If _hash_step is called from _hash_first, current will not be - * valid, so we can't dereference it. However, in that case, we - * presumably want to start at the beginning/end of the page... - */ - maxoff = PageGetMaxOffsetNumber(page); - if (ItemPointerIsValid(current)) { - offnum = ItemPointerGetOffsetNumber(current); - } else { - offnum = InvalidOffsetNumber; - } - - /* - * 'offnum' now points to the last tuple we have seen (if any). - * - * continue to step through tuples until: - * 1) we get to the end of the bucket chain or - * 2) we find a valid tuple. - */ - do { - bucket = opaque->hasho_bucket; - - switch (dir) { - case ForwardScanDirection: - if (offnum != InvalidOffsetNumber) { - offnum = OffsetNumberNext(offnum); /* move forward */ - } else { - offnum = FirstOffsetNumber; /* new page */ - } - while (offnum > maxoff) { - /* - * either this page is empty (maxoff == - * InvalidOffsetNumber) or we ran off the end. - */ - _hash_readnext(rel, &buf, &page, &opaque); - if (BufferIsInvalid(buf)) { /* end of chain */ - if (allbuckets && bucket < metap->hashm_maxbucket) { - ++bucket; - blkno = BUCKET_TO_BLKNO(bucket); - buf = _hash_getbuf(rel, blkno, HASH_READ); - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE); - opaque = (HashPageOpaque) PageGetSpecialPointer(page); - Assert(opaque->hasho_bucket == bucket); - while (PageIsEmpty(page) && - BlockNumberIsValid(opaque->hasho_nextblkno)) { - _hash_readnext(rel, &buf, &page, &opaque); + Relation rel; + ItemPointer current; + HashScanOpaque so; + int allbuckets; + HashMetaPage metap; + Buffer buf; + Page page; + HashPageOpaque opaque; + OffsetNumber maxoff; + OffsetNumber offnum; + Bucket bucket; + BlockNumber blkno; + HashItem hitem; + IndexTuple itup; + + rel = scan->relation; + current = &(scan->currentItemData); + so = (HashScanOpaque) scan->opaque; + allbuckets = (scan->numberOfKeys < 1); + + metap = (HashMetaPage) BufferGetPage(metabuf); + _hash_checkpage((Page) metap, LH_META_PAGE); + + buf = *bufP; + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + + /* + * If _hash_step is called from _hash_first, current will not be + * valid, so we can't dereference it. However, in that case, we + * presumably want to start at the beginning/end of the page... + */ + maxoff = PageGetMaxOffsetNumber(page); + if (ItemPointerIsValid(current)) + { + offnum = ItemPointerGetOffsetNumber(current); + } + else + { + offnum = InvalidOffsetNumber; + } + + /* + * 'offnum' now points to the last tuple we have seen (if any). + * + * continue to step through tuples until: 1) we get to the end of the + * bucket chain or 2) we find a valid tuple. + */ + do + { + bucket = opaque->hasho_bucket; + + switch (dir) + { + case ForwardScanDirection: + if (offnum != InvalidOffsetNumber) + { + offnum = OffsetNumberNext(offnum); /* move forward */ } - maxoff = PageGetMaxOffsetNumber(page); - offnum = FirstOffsetNumber; - } else { - maxoff = offnum = InvalidOffsetNumber; - break; /* while */ - } - } else { - /* _hash_readnext never returns an empty page */ - maxoff = PageGetMaxOffsetNumber(page); - offnum = FirstOffsetNumber; - } - } - break; - case BackwardScanDirection: - if (offnum != InvalidOffsetNumber) { - offnum = OffsetNumberPrev(offnum); /* move back */ - } else { - offnum = maxoff; /* new page */ - } - while (offnum < FirstOffsetNumber) { - /* - * either this page is empty (offnum == - * InvalidOffsetNumber) or we ran off the end. - */ - _hash_readprev(rel, &buf, &page, &opaque); - if (BufferIsInvalid(buf)) { /* end of chain */ - if (allbuckets && bucket > 0) { - --bucket; - blkno = BUCKET_TO_BLKNO(bucket); - buf = _hash_getbuf(rel, blkno, HASH_READ); - page = BufferGetPage(buf); - _hash_checkpage(page, LH_BUCKET_PAGE); - opaque = (HashPageOpaque) PageGetSpecialPointer(page); - Assert(opaque->hasho_bucket == bucket); - while (BlockNumberIsValid(opaque->hasho_nextblkno)) { - _hash_readnext(rel, &buf, &page, &opaque); + else + { + offnum = FirstOffsetNumber; /* new page */ + } + while (offnum > maxoff) + { + + /* + * either this page is empty (maxoff == + * InvalidOffsetNumber) or we ran off the end. + */ + _hash_readnext(rel, &buf, &page, &opaque); + if (BufferIsInvalid(buf)) + { /* end of chain */ + if (allbuckets && bucket < metap->hashm_maxbucket) + { + ++bucket; + blkno = BUCKET_TO_BLKNO(bucket); + buf = _hash_getbuf(rel, blkno, HASH_READ); + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE); + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + Assert(opaque->hasho_bucket == bucket); + while (PageIsEmpty(page) && + BlockNumberIsValid(opaque->hasho_nextblkno)) + { + _hash_readnext(rel, &buf, &page, &opaque); + } + maxoff = PageGetMaxOffsetNumber(page); + offnum = FirstOffsetNumber; + } + else + { + maxoff = offnum = InvalidOffsetNumber; + break; /* while */ + } + } + else + { + /* _hash_readnext never returns an empty page */ + maxoff = PageGetMaxOffsetNumber(page); + offnum = FirstOffsetNumber; + } + } + break; + case BackwardScanDirection: + if (offnum != InvalidOffsetNumber) + { + offnum = OffsetNumberPrev(offnum); /* move back */ + } + else + { + offnum = maxoff;/* new page */ } - maxoff = offnum = PageGetMaxOffsetNumber(page); - } else { - maxoff = offnum = InvalidOffsetNumber; - break; /* while */ - } - } else { - /* _hash_readprev never returns an empty page */ - maxoff = offnum = PageGetMaxOffsetNumber(page); + while (offnum < FirstOffsetNumber) + { + + /* + * either this page is empty (offnum == + * InvalidOffsetNumber) or we ran off the end. + */ + _hash_readprev(rel, &buf, &page, &opaque); + if (BufferIsInvalid(buf)) + { /* end of chain */ + if (allbuckets && bucket > 0) + { + --bucket; + blkno = BUCKET_TO_BLKNO(bucket); + buf = _hash_getbuf(rel, blkno, HASH_READ); + page = BufferGetPage(buf); + _hash_checkpage(page, LH_BUCKET_PAGE); + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + Assert(opaque->hasho_bucket == bucket); + while (BlockNumberIsValid(opaque->hasho_nextblkno)) + { + _hash_readnext(rel, &buf, &page, &opaque); + } + maxoff = offnum = PageGetMaxOffsetNumber(page); + } + else + { + maxoff = offnum = InvalidOffsetNumber; + break; /* while */ + } + } + else + { + /* _hash_readprev never returns an empty page */ + maxoff = offnum = PageGetMaxOffsetNumber(page); + } + } + break; + default: + /* NoMovementScanDirection */ + /* this should not be reached */ + break; } - } - break; - default: - /* NoMovementScanDirection */ - /* this should not be reached */ - break; - } - /* we ran off the end of the world without finding a match */ - if (offnum == InvalidOffsetNumber) { - _hash_relbuf(rel, metabuf, HASH_READ); - *bufP = so->hashso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(current); - return(false); - } - - /* get ready to check this tuple */ - hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &hitem->hash_itup; - } while (!_hash_checkqual(scan, itup)); - - /* if we made it to here, we've found a valid tuple */ - _hash_relbuf(rel, metabuf, HASH_READ); - blkno = BufferGetBlockNumber(buf); - *bufP = so->hashso_curbuf = buf; - ItemPointerSet(current, blkno, offnum); - return(true); + /* we ran off the end of the world without finding a match */ + if (offnum == InvalidOffsetNumber) + { + _hash_relbuf(rel, metabuf, HASH_READ); + *bufP = so->hashso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(current); + return (false); + } + + /* get ready to check this tuple */ + hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &hitem->hash_itup; + } while (!_hash_checkqual(scan, itup)); + + /* if we made it to here, we've found a valid tuple */ + _hash_relbuf(rel, metabuf, HASH_READ); + blkno = BufferGetBlockNumber(buf); + *bufP = so->hashso_curbuf = buf; + ItemPointerSet(current, blkno, offnum); + return (true); } diff --git a/src/backend/access/hash/hashstrat.c b/src/backend/access/hash/hashstrat.c index d2f1e513c38..f1bdbdb8a3a 100644 --- a/src/backend/access/hash/hashstrat.c +++ b/src/backend/access/hash/hashstrat.c @@ -1,80 +1,83 @@ /*------------------------------------------------------------------------- * * btstrat.c-- - * Srategy map entries for the btree indexed access method + * Srategy map entries for the btree indexed access method * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.9 1997/08/20 02:01:42 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.10 1997/09/07 04:38:03 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/hash.h> #include <access/istrat.h> -/* - * only one valid strategy for hash tables: equality. +/* + * only one valid strategy for hash tables: equality. */ #ifdef NOT_USED -static StrategyNumber HTNegate[1] = { - InvalidStrategy +static StrategyNumber HTNegate[1] = { + InvalidStrategy }; -static StrategyNumber HTCommute[1] = { - HTEqualStrategyNumber +static StrategyNumber HTCommute[1] = { + HTEqualStrategyNumber }; -static StrategyNumber HTNegateCommute[1] = { - InvalidStrategy +static StrategyNumber HTNegateCommute[1] = { + InvalidStrategy }; -static StrategyEvaluationData HTEvaluationData = { - /* XXX static for simplicity */ +static StrategyEvaluationData HTEvaluationData = { + /* XXX static for simplicity */ - HTMaxStrategyNumber, - (StrategyTransformMap)HTNegate, - (StrategyTransformMap)HTCommute, - (StrategyTransformMap)HTNegateCommute, - {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL} + HTMaxStrategyNumber, + (StrategyTransformMap) HTNegate, + (StrategyTransformMap) HTCommute, + (StrategyTransformMap) HTNegateCommute, + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL} }; + #endif /* ---------------------------------------------------------------- - * RelationGetHashStrategy + * RelationGetHashStrategy * ---------------------------------------------------------------- */ #ifdef NOT_USED -static StrategyNumber +static StrategyNumber _hash_getstrat(Relation rel, - AttrNumber attno, - RegProcedure proc) + AttrNumber attno, + RegProcedure proc) { - StrategyNumber strat; + StrategyNumber strat; - strat = RelationGetStrategy(rel, attno, &HTEvaluationData, proc); + strat = RelationGetStrategy(rel, attno, &HTEvaluationData, proc); - Assert(StrategyNumberIsValid(strat)); + Assert(StrategyNumberIsValid(strat)); - return (strat); + return (strat); } + #endif #ifdef NOT_USED -static bool +static bool _hash_invokestrat(Relation rel, - AttrNumber attno, - StrategyNumber strat, - Datum left, - Datum right) + AttrNumber attno, + StrategyNumber strat, + Datum left, + Datum right) { - return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat, - left, right)); + return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat, + left, right)); } + #endif diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c index dd0b4737454..f9fbe0e2d17 100644 --- a/src/backend/access/hash/hashutil.c +++ b/src/backend/access/hash/hashutil.c @@ -1,109 +1,110 @@ /*------------------------------------------------------------------------- * * btutils.c-- - * Utility code for Postgres btree implementation. + * Utility code for Postgres btree implementation. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.9 1997/08/14 05:01:32 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.10 1997/09/07 04:38:04 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <access/hash.h> #include <fmgr.h> #include <utils/memutils.h> #include <access/iqual.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif ScanKey _hash_mkscankey(Relation rel, IndexTuple itup, HashMetaPage metap) { - ScanKey skey; - TupleDesc itupdesc; - int natts; - AttrNumber i; - Datum arg; - RegProcedure proc; - bool null; - - natts = rel->rd_rel->relnatts; - itupdesc = RelationGetTupleDescriptor(rel); - - skey = (ScanKey) palloc(natts * sizeof(ScanKeyData)); - - for (i = 0; i < natts; i++) { - arg = index_getattr(itup, i + 1, itupdesc, &null); - proc = metap->hashm_procid; - ScanKeyEntryInitialize(&skey[i], - 0x0, (AttrNumber) (i + 1), proc, arg); - } - - return (skey); -} + ScanKey skey; + TupleDesc itupdesc; + int natts; + AttrNumber i; + Datum arg; + RegProcedure proc; + bool null; + + natts = rel->rd_rel->relnatts; + itupdesc = RelationGetTupleDescriptor(rel); + + skey = (ScanKey) palloc(natts * sizeof(ScanKeyData)); + + for (i = 0; i < natts; i++) + { + arg = index_getattr(itup, i + 1, itupdesc, &null); + proc = metap->hashm_procid; + ScanKeyEntryInitialize(&skey[i], + 0x0, (AttrNumber) (i + 1), proc, arg); + } + + return (skey); +} void _hash_freeskey(ScanKey skey) { - pfree(skey); + pfree(skey); } bool _hash_checkqual(IndexScanDesc scan, IndexTuple itup) { - if (scan->numberOfKeys > 0) - return (index_keytest(itup, - RelationGetTupleDescriptor(scan->relation), - scan->numberOfKeys, scan->keyData)); - else - return (true); + if (scan->numberOfKeys > 0) + return (index_keytest(itup, + RelationGetTupleDescriptor(scan->relation), + scan->numberOfKeys, scan->keyData)); + else + return (true); } HashItem _hash_formitem(IndexTuple itup) { - int nbytes_hitem; - HashItem hitem; - Size tuplen; - - /* disallow nulls in hash keys */ - if (itup->t_info & INDEX_NULL_MASK) - elog(WARN, "hash indices cannot include null keys"); - - /* make a copy of the index tuple with room for the sequence number */ - tuplen = IndexTupleSize(itup); - nbytes_hitem = tuplen + - (sizeof(HashItemData) - sizeof(IndexTupleData)); - - hitem = (HashItem) palloc(nbytes_hitem); - memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen); - - return (hitem); + int nbytes_hitem; + HashItem hitem; + Size tuplen; + + /* disallow nulls in hash keys */ + if (itup->t_info & INDEX_NULL_MASK) + elog(WARN, "hash indices cannot include null keys"); + + /* make a copy of the index tuple with room for the sequence number */ + tuplen = IndexTupleSize(itup); + nbytes_hitem = tuplen + + (sizeof(HashItemData) - sizeof(IndexTupleData)); + + hitem = (HashItem) palloc(nbytes_hitem); + memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen); + + return (hitem); } Bucket _hash_call(Relation rel, HashMetaPage metap, Datum key) { - uint32 n; - Bucket bucket; - RegProcedure proc; - - proc = metap->hashm_procid; - n = (uint32) fmgr(proc, key); - bucket = n & metap->hashm_highmask; - if (bucket > metap->hashm_maxbucket) - bucket = bucket & metap->hashm_lowmask; - return (bucket); + uint32 n; + Bucket bucket; + RegProcedure proc; + + proc = metap->hashm_procid; + n = (uint32) fmgr(proc, key); + bucket = n & metap->hashm_highmask; + if (bucket > metap->hashm_maxbucket) + bucket = bucket & metap->hashm_lowmask; + return (bucket); } /* @@ -112,12 +113,13 @@ _hash_call(Relation rel, HashMetaPage metap, Datum key) uint32 _hash_log2(uint32 num) { - uint32 i, limit; - - limit = 1; - for (i = 0; limit < num; limit = limit << 1, i++) - ; - return (i); + uint32 i, + limit; + + limit = 1; + for (i = 0; limit < num; limit = limit << 1, i++) + ; + return (i); } /* @@ -126,19 +128,20 @@ _hash_log2(uint32 num) void _hash_checkpage(Page page, int flags) { - HashPageOpaque opaque; + HashPageOpaque opaque; - Assert(page); - Assert(((PageHeader)(page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData))); + Assert(page); + Assert(((PageHeader) (page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData))); #if 1 - Assert(((PageHeader)(page))->pd_upper <= - (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData)))); - Assert(((PageHeader)(page))->pd_special == - (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData)))); - Assert(((PageHeader)(page))->pd_opaque.od_pagesize == BLCKSZ); + Assert(((PageHeader) (page))->pd_upper <= + (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData)))); + Assert(((PageHeader) (page))->pd_special == + (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData)))); + Assert(((PageHeader) (page))->pd_opaque.od_pagesize == BLCKSZ); #endif - if (flags) { - opaque = (HashPageOpaque) PageGetSpecialPointer(page); - Assert(opaque->hasho_flag & flags); - } + if (flags) + { + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + Assert(opaque->hasho_flag & flags); + } } diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index f199803a711..b7ab8625140 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -1,74 +1,74 @@ /*------------------------------------------------------------------------- * * heapam.c-- - * heap access method code + * heap access method code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.15 1997/08/27 09:00:20 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.16 1997/09/07 04:38:09 momjian Exp $ * * * INTERFACE ROUTINES - * heapgettup - fetch next heap tuple from a scan - * heap_open - open a heap relation by relationId - * heap_openr - open a heap relation by name - * heap_close - close a heap relation - * heap_beginscan - begin relation scan - * heap_rescan - restart a relation scan - * heap_endscan - end relation scan - * heap_getnext - retrieve next tuple in scan - * heap_fetch - retrive tuple with tid - * heap_insert - insert tuple into a relation - * heap_delete - delete a tuple from a relation - * heap_replace - replace a tuple in a relation with another tuple - * heap_markpos - mark scan position - * heap_restrpos - restore position to marked location - * + * heapgettup - fetch next heap tuple from a scan + * heap_open - open a heap relation by relationId + * heap_openr - open a heap relation by name + * heap_close - close a heap relation + * heap_beginscan - begin relation scan + * heap_rescan - restart a relation scan + * heap_endscan - end relation scan + * heap_getnext - retrieve next tuple in scan + * heap_fetch - retrive tuple with tid + * heap_insert - insert tuple into a relation + * heap_delete - delete a tuple from a relation + * heap_replace - replace a tuple in a relation with another tuple + * heap_markpos - mark scan position + * heap_restrpos - restore position to marked location + * * NOTES - * This file contains the heap_ routines which implement - * the POSTGRES heap access method used for all POSTGRES - * relations. + * This file contains the heap_ routines which implement + * the POSTGRES heap access method used for all POSTGRES + * relations. * * OLD COMMENTS - * struct relscan hints: (struct should be made AM independent?) + * struct relscan hints: (struct should be made AM independent?) * - * rs_ctid is the tid of the last tuple returned by getnext. - * rs_ptid and rs_ntid are the tids of the previous and next tuples - * returned by getnext, respectively. NULL indicates an end of - * scan (either direction); NON indicates an unknow value. + * rs_ctid is the tid of the last tuple returned by getnext. + * rs_ptid and rs_ntid are the tids of the previous and next tuples + * returned by getnext, respectively. NULL indicates an end of + * scan (either direction); NON indicates an unknow value. * - * possible combinations: - * rs_p rs_c rs_n interpretation - * NULL NULL NULL empty scan - * NULL NULL NON at begining of scan - * NULL NULL t1 at begining of scan (with cached tid) - * NON NULL NULL at end of scan - * t1 NULL NULL at end of scan (with cached tid) - * NULL t1 NULL just returned only tuple - * NULL t1 NON just returned first tuple - * NULL t1 t2 returned first tuple (with cached tid) - * NON t1 NULL just returned last tuple - * t2 t1 NULL returned last tuple (with cached tid) - * t1 t2 NON in the middle of a forward scan - * NON t2 t1 in the middle of a reverse scan - * ti tj tk in the middle of a scan (w cached tid) + * possible combinations: + * rs_p rs_c rs_n interpretation + * NULL NULL NULL empty scan + * NULL NULL NON at begining of scan + * NULL NULL t1 at begining of scan (with cached tid) + * NON NULL NULL at end of scan + * t1 NULL NULL at end of scan (with cached tid) + * NULL t1 NULL just returned only tuple + * NULL t1 NON just returned first tuple + * NULL t1 t2 returned first tuple (with cached tid) + * NON t1 NULL just returned last tuple + * t2 t1 NULL returned last tuple (with cached tid) + * t1 t2 NON in the middle of a forward scan + * NON t2 t1 in the middle of a reverse scan + * ti tj tk in the middle of a scan (w cached tid) * - * Here NULL is ...tup == NULL && ...buf == InvalidBuffer, - * and NON is ...tup == NULL && ...buf == UnknownBuffer. + * Here NULL is ...tup == NULL && ...buf == InvalidBuffer, + * and NON is ...tup == NULL && ...buf == UnknownBuffer. * - * Currently, the NONTID values are not cached with their actual - * values by getnext. Values may be cached by markpos since it stores - * all three tids. + * Currently, the NONTID values are not cached with their actual + * values by getnext. Values may be cached by markpos since it stores + * all three tids. * - * NOTE: the calls to elog() must stop. Should decide on an interface - * between the general and specific AM calls. + * NOTE: the calls to elog() must stop. Should decide on an interface + * between the general and specific AM calls. * - * XXX probably do not need a free tuple routine for heaps. - * Huh? Free tuple is not necessary for tuples returned by scans, but - * is necessary for tuples which are returned by - * RelationGetTupleByItemPointer. -hirohama + * XXX probably do not need a free tuple routine for heaps. + * Huh? Free tuple is not necessary for tuples returned by scans, but + * is necessary for tuples which are returned by + * RelationGetTupleByItemPointer. -hirohama * *------------------------------------------------------------------------- */ @@ -91,644 +91,706 @@ #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static bool ImmediateInvalidation; +static bool ImmediateInvalidation; /* ---------------------------------------------------------------- - * heap support routines + * heap support routines * ---------------------------------------------------------------- */ /* ---------------- - * initsdesc - sdesc code common to heap_beginscan and heap_rescan + * initsdesc - sdesc code common to heap_beginscan and heap_rescan * ---------------- */ static void initsdesc(HeapScanDesc sdesc, - Relation relation, - int atend, - unsigned nkeys, - ScanKey key) + Relation relation, + int atend, + unsigned nkeys, + ScanKey key) { - if (!RelationGetNumberOfBlocks(relation)) { - /* ---------------- - * relation is empty - * ---------------- - */ - sdesc->rs_ntup = sdesc->rs_ctup = sdesc->rs_ptup = NULL; - sdesc->rs_nbuf = sdesc->rs_cbuf = sdesc->rs_pbuf = InvalidBuffer; - } else if (atend) { - /* ---------------- - * reverse scan - * ---------------- - */ - sdesc->rs_ntup = sdesc->rs_ctup = NULL; - sdesc->rs_nbuf = sdesc->rs_cbuf = InvalidBuffer; - sdesc->rs_ptup = NULL; - sdesc->rs_pbuf = UnknownBuffer; - } else { + if (!RelationGetNumberOfBlocks(relation)) + { + /* ---------------- + * relation is empty + * ---------------- + */ + sdesc->rs_ntup = sdesc->rs_ctup = sdesc->rs_ptup = NULL; + sdesc->rs_nbuf = sdesc->rs_cbuf = sdesc->rs_pbuf = InvalidBuffer; + } + else if (atend) + { + /* ---------------- + * reverse scan + * ---------------- + */ + sdesc->rs_ntup = sdesc->rs_ctup = NULL; + sdesc->rs_nbuf = sdesc->rs_cbuf = InvalidBuffer; + sdesc->rs_ptup = NULL; + sdesc->rs_pbuf = UnknownBuffer; + } + else + { + /* ---------------- + * forward scan + * ---------------- + */ + sdesc->rs_ctup = sdesc->rs_ptup = NULL; + sdesc->rs_cbuf = sdesc->rs_pbuf = InvalidBuffer; + sdesc->rs_ntup = NULL; + sdesc->rs_nbuf = UnknownBuffer; + } /* invalid too */ + + /* we don't have a marked position... */ + ItemPointerSetInvalid(&(sdesc->rs_mptid)); + ItemPointerSetInvalid(&(sdesc->rs_mctid)); + ItemPointerSetInvalid(&(sdesc->rs_mntid)); + ItemPointerSetInvalid(&(sdesc->rs_mcd)); + /* ---------------- - * forward scan + * copy the scan key, if appropriate * ---------------- */ - sdesc->rs_ctup = sdesc->rs_ptup = NULL; - sdesc->rs_cbuf = sdesc->rs_pbuf = InvalidBuffer; - sdesc->rs_ntup = NULL; - sdesc->rs_nbuf = UnknownBuffer; - } /* invalid too */ - - /* we don't have a marked position... */ - ItemPointerSetInvalid(&(sdesc->rs_mptid)); - ItemPointerSetInvalid(&(sdesc->rs_mctid)); - ItemPointerSetInvalid(&(sdesc->rs_mntid)); - ItemPointerSetInvalid(&(sdesc->rs_mcd)); - - /* ---------------- - * copy the scan key, if appropriate - * ---------------- - */ - if (key != NULL) - memmove(sdesc->rs_key, key, nkeys * sizeof(ScanKeyData)); + if (key != NULL) + memmove(sdesc->rs_key, key, nkeys * sizeof(ScanKeyData)); } /* ---------------- - * unpinsdesc - code common to heap_rescan and heap_endscan + * unpinsdesc - code common to heap_rescan and heap_endscan * ---------------- */ static void unpinsdesc(HeapScanDesc sdesc) { - if (BufferIsValid(sdesc->rs_pbuf)) { - ReleaseBuffer(sdesc->rs_pbuf); - } - - /* ------------------------------------ - * Scan will pin buffer one for each non-NULL tuple pointer - * (ptup, ctup, ntup), so they have to be unpinned multiple - * times. - * ------------------------------------ - */ - if (BufferIsValid(sdesc->rs_cbuf)) { - ReleaseBuffer(sdesc->rs_cbuf); - } - - if (BufferIsValid(sdesc->rs_nbuf)) { - ReleaseBuffer(sdesc->rs_nbuf); - } + if (BufferIsValid(sdesc->rs_pbuf)) + { + ReleaseBuffer(sdesc->rs_pbuf); + } + + /* ------------------------------------ + * Scan will pin buffer one for each non-NULL tuple pointer + * (ptup, ctup, ntup), so they have to be unpinned multiple + * times. + * ------------------------------------ + */ + if (BufferIsValid(sdesc->rs_cbuf)) + { + ReleaseBuffer(sdesc->rs_cbuf); + } + + if (BufferIsValid(sdesc->rs_nbuf)) + { + ReleaseBuffer(sdesc->rs_nbuf); + } } /* ------------------------------------------ - * nextpage + * nextpage * - * figure out the next page to scan after the current page - * taking into account of possible adjustment of degrees of - * parallelism + * figure out the next page to scan after the current page + * taking into account of possible adjustment of degrees of + * parallelism * ------------------------------------------ */ static int nextpage(int page, int dir) { - return((dir<0)?page-1:page+1); + return ((dir < 0) ? page - 1 : page + 1); } /* ---------------- - * heapgettup - fetch next heap tuple + * heapgettup - fetch next heap tuple * - * routine used by heap_getnext() which does most of the - * real work in scanning tuples. + * routine used by heap_getnext() which does most of the + * real work in scanning tuples. * ---------------- */ -static HeapTuple +static HeapTuple heapgettup(Relation relation, - ItemPointer tid, - int dir, - Buffer *b, - TimeQual timeQual, - int nkeys, - ScanKey key) + ItemPointer tid, + int dir, + Buffer * b, + TimeQual timeQual, + int nkeys, + ScanKey key) { - ItemId lpp; - Page dp; - int page; - int pages; - int lines; - HeapTuple rtup; - OffsetNumber lineoff; - int linesleft; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_heapgettup); - IncrHeapAccessStat(global_heapgettup); - - /* ---------------- - * debugging stuff - * - * check validity of arguments, here and for other functions too - * Note: no locking manipulations needed--this is a local function - * ---------------- - */ -#ifdef HEAPDEBUGALL - if (ItemPointerIsValid(tid)) { - elog(DEBUG, "heapgettup(%.16s, tid=0x%x[%d,%d], dir=%d, ...)", - RelationGetRelationName(relation), tid, tid->ip_blkid, - tid->ip_posid, dir); - } else { - elog(DEBUG, "heapgettup(%.16s, tid=0x%x, dir=%d, ...)", - RelationGetRelationName(relation), tid, dir); - } - elog(DEBUG, "heapgettup(..., b=0x%x, timeQ=0x%x, nkeys=%d, key=0x%x", - b, timeQual, nkeys, key); - if (timeQual == SelfTimeQual) { - elog(DEBUG, "heapgettup: relation(%c)=`%.16s', SelfTimeQual", - relation->rd_rel->relkind, &relation->rd_rel->relname); - } else { - elog(DEBUG, "heapgettup: relation(%c)=`%.16s', timeQual=%d", - relation->rd_rel->relkind, &relation->rd_rel->relname, - timeQual); - } -#endif /* !defined(HEAPDEBUGALL) */ - - if (!ItemPointerIsValid(tid)) { - Assert(!PointerIsValid(tid)); - } - - /* ---------------- - * return null immediately if relation is empty - * ---------------- - */ - if (!(pages = relation->rd_nblocks)) - return (NULL); - - /* ---------------- - * calculate next starting lineoff, given scan direction - * ---------------- - */ - if (!dir) { + ItemId lpp; + Page dp; + int page; + int pages; + int lines; + HeapTuple rtup; + OffsetNumber lineoff; + int linesleft; + /* ---------------- - * ``no movement'' scan direction + * increment access statistics * ---------------- */ - /* assume it is a valid TID XXX */ - if (ItemPointerIsValid(tid) == false) { - *b = InvalidBuffer; - return (NULL); - } - *b = RelationGetBufferWithBuffer(relation, - ItemPointerGetBlockNumber(tid), - *b); - -#ifndef NO_BUFFERISVALID - if (!BufferIsValid(*b)) { - elog(WARN, "heapgettup: failed ReadBuffer"); - } -#endif - - dp = (Page) BufferGetPage(*b); - lineoff = ItemPointerGetOffsetNumber(tid); - lpp = PageGetItemId(dp, lineoff); - - rtup = (HeapTuple)PageGetItem((Page) dp, lpp); - return (rtup); - - } else if (dir < 0) { + IncrHeapAccessStat(local_heapgettup); + IncrHeapAccessStat(global_heapgettup); + /* ---------------- - * reverse scan direction + * debugging stuff + * + * check validity of arguments, here and for other functions too + * Note: no locking manipulations needed--this is a local function * ---------------- */ - if (ItemPointerIsValid(tid) == false) { - tid = NULL; +#ifdef HEAPDEBUGALL + if (ItemPointerIsValid(tid)) + { + elog(DEBUG, "heapgettup(%.16s, tid=0x%x[%d,%d], dir=%d, ...)", + RelationGetRelationName(relation), tid, tid->ip_blkid, + tid->ip_posid, dir); } - if (tid == NULL) { - page = pages - 1; /* final page */ - } else { - page = ItemPointerGetBlockNumber(tid); /* current page */ + else + { + elog(DEBUG, "heapgettup(%.16s, tid=0x%x, dir=%d, ...)", + RelationGetRelationName(relation), tid, dir); } - if (page < 0) { - *b = InvalidBuffer; - return (NULL); + elog(DEBUG, "heapgettup(..., b=0x%x, timeQ=0x%x, nkeys=%d, key=0x%x", + b, timeQual, nkeys, key); + if (timeQual == SelfTimeQual) + { + elog(DEBUG, "heapgettup: relation(%c)=`%.16s', SelfTimeQual", + relation->rd_rel->relkind, &relation->rd_rel->relname); } - - *b = RelationGetBufferWithBuffer(relation, page, *b); -#ifndef NO_BUFFERISVALID - if (!BufferIsValid(*b)) { - elog(WARN, "heapgettup: failed ReadBuffer"); + else + { + elog(DEBUG, "heapgettup: relation(%c)=`%.16s', timeQual=%d", + relation->rd_rel->relkind, &relation->rd_rel->relname, + timeQual); } -#endif - - dp = (Page) BufferGetPage(*b); - lines = PageGetMaxOffsetNumber(dp); - if (tid == NULL) { - lineoff = lines; /* final offnum */ - } else { - lineoff = /* previous offnum */ - OffsetNumberPrev(ItemPointerGetOffsetNumber(tid)); +#endif /* !defined(HEAPDEBUGALL) */ + + if (!ItemPointerIsValid(tid)) + { + Assert(!PointerIsValid(tid)); } - /* page and lineoff now reference the physically previous tid */ - } else { /* ---------------- - * forward scan direction + * return null immediately if relation is empty * ---------------- */ - if (ItemPointerIsValid(tid) == false) { - page = 0; /* first page */ - lineoff = FirstOffsetNumber; /* first offnum */ - } else { - page = ItemPointerGetBlockNumber(tid); /* current page */ - lineoff = /* next offnum */ - OffsetNumberNext(ItemPointerGetOffsetNumber(tid)); - } - - if (page >= pages) { - *b = InvalidBuffer; - return (NULL); - } - /* page and lineoff now reference the physically next tid */ + if (!(pages = relation->rd_nblocks)) + return (NULL); + + /* ---------------- + * calculate next starting lineoff, given scan direction + * ---------------- + */ + if (!dir) + { + /* ---------------- + * ``no movement'' scan direction + * ---------------- + */ + /* assume it is a valid TID XXX */ + if (ItemPointerIsValid(tid) == false) + { + *b = InvalidBuffer; + return (NULL); + } + *b = RelationGetBufferWithBuffer(relation, + ItemPointerGetBlockNumber(tid), + *b); - *b = RelationGetBufferWithBuffer(relation, page, *b); #ifndef NO_BUFFERISVALID - if (!BufferIsValid(*b)) { - elog(WARN, "heapgettup: failed ReadBuffer"); + if (!BufferIsValid(*b)) + { + elog(WARN, "heapgettup: failed ReadBuffer"); + } +#endif + + dp = (Page) BufferGetPage(*b); + lineoff = ItemPointerGetOffsetNumber(tid); + lpp = PageGetItemId(dp, lineoff); + + rtup = (HeapTuple) PageGetItem((Page) dp, lpp); + return (rtup); + } + else if (dir < 0) + { + /* ---------------- + * reverse scan direction + * ---------------- + */ + if (ItemPointerIsValid(tid) == false) + { + tid = NULL; + } + if (tid == NULL) + { + page = pages - 1; /* final page */ + } + else + { + page = ItemPointerGetBlockNumber(tid); /* current page */ + } + if (page < 0) + { + *b = InvalidBuffer; + return (NULL); + } + + *b = RelationGetBufferWithBuffer(relation, page, *b); +#ifndef NO_BUFFERISVALID + if (!BufferIsValid(*b)) + { + elog(WARN, "heapgettup: failed ReadBuffer"); + } #endif - - dp = (Page) BufferGetPage(*b); - lines = PageGetMaxOffsetNumber(dp); - } - - /* 'dir' is now non-zero */ - - /* ---------------- - * calculate line pointer and number of remaining items - * to check on this page. - * ---------------- - */ - lpp = PageGetItemId(dp, lineoff); - if (dir < 0) { - linesleft = lineoff - 1; - } else { - linesleft = lines - lineoff; - } - - /* ---------------- - * advance the scan until we find a qualifying tuple or - * run out of stuff to scan - * ---------------- - */ - for (;;) { - while (linesleft >= 0) { - /* ---------------- - * if current tuple qualifies, return it. - * ---------------- - */ - if ((rtup = heap_tuple_satisfies(lpp, relation, *b, (PageHeader) dp, - timeQual, nkeys, key)) != NULL) { - ItemPointer iptr = &(rtup->t_ctid); - if (ItemPointerGetBlockNumber(iptr) != page) { - /* - * set block id to the correct page number - * --- this is a hack to support the virtual fragment - * concept - */ - ItemPointerSetBlockNumber(iptr, page); + + dp = (Page) BufferGetPage(*b); + lines = PageGetMaxOffsetNumber(dp); + if (tid == NULL) + { + lineoff = lines; /* final offnum */ } - return (rtup); - } - - /* ---------------- - * otherwise move to the next item on the page - * ---------------- - */ - --linesleft; - if (dir < 0) { - --lpp; /* move back in this page's ItemId array */ - } else { - ++lpp; /* move forward in this page's ItemId array */ - } + else + { + lineoff = /* previous offnum */ + OffsetNumberPrev(ItemPointerGetOffsetNumber(tid)); + } + /* page and lineoff now reference the physically previous tid */ + + } + else + { + /* ---------------- + * forward scan direction + * ---------------- + */ + if (ItemPointerIsValid(tid) == false) + { + page = 0; /* first page */ + lineoff = FirstOffsetNumber; /* first offnum */ + } + else + { + page = ItemPointerGetBlockNumber(tid); /* current page */ + lineoff = /* next offnum */ + OffsetNumberNext(ItemPointerGetOffsetNumber(tid)); + } + + if (page >= pages) + { + *b = InvalidBuffer; + return (NULL); + } + /* page and lineoff now reference the physically next tid */ + + *b = RelationGetBufferWithBuffer(relation, page, *b); +#ifndef NO_BUFFERISVALID + if (!BufferIsValid(*b)) + { + elog(WARN, "heapgettup: failed ReadBuffer"); + } +#endif + + dp = (Page) BufferGetPage(*b); + lines = PageGetMaxOffsetNumber(dp); } - + + /* 'dir' is now non-zero */ + /* ---------------- - * if we get here, it means we've exhausted the items on - * this page and it's time to move to the next.. + * calculate line pointer and number of remaining items + * to check on this page. * ---------------- */ - page = nextpage(page, dir); - + lpp = PageGetItemId(dp, lineoff); + if (dir < 0) + { + linesleft = lineoff - 1; + } + else + { + linesleft = lines - lineoff; + } + /* ---------------- - * return NULL if we've exhausted all the pages.. + * advance the scan until we find a qualifying tuple or + * run out of stuff to scan * ---------------- */ - if (page < 0 || page >= pages) { - if (BufferIsValid(*b)) - ReleaseBuffer(*b); - *b = InvalidBuffer; - return (NULL); - } - - *b = ReleaseAndReadBuffer(*b, relation, page); - + for (;;) + { + while (linesleft >= 0) + { + /* ---------------- + * if current tuple qualifies, return it. + * ---------------- + */ + if ((rtup = heap_tuple_satisfies(lpp, relation, *b, (PageHeader) dp, + timeQual, nkeys, key)) != NULL) + { + ItemPointer iptr = &(rtup->t_ctid); + + if (ItemPointerGetBlockNumber(iptr) != page) + { + + /* + * set block id to the correct page number --- this is + * a hack to support the virtual fragment concept + */ + ItemPointerSetBlockNumber(iptr, page); + } + return (rtup); + } + + /* ---------------- + * otherwise move to the next item on the page + * ---------------- + */ + --linesleft; + if (dir < 0) + { + --lpp; /* move back in this page's ItemId array */ + } + else + { + ++lpp; /* move forward in this page's ItemId + * array */ + } + } + + /* ---------------- + * if we get here, it means we've exhausted the items on + * this page and it's time to move to the next.. + * ---------------- + */ + page = nextpage(page, dir); + + /* ---------------- + * return NULL if we've exhausted all the pages.. + * ---------------- + */ + if (page < 0 || page >= pages) + { + if (BufferIsValid(*b)) + ReleaseBuffer(*b); + *b = InvalidBuffer; + return (NULL); + } + + *b = ReleaseAndReadBuffer(*b, relation, page); + #ifndef NO_BUFFERISVALID - if (!BufferIsValid(*b)) { - elog(WARN, "heapgettup: failed ReadBuffer"); - } + if (!BufferIsValid(*b)) + { + elog(WARN, "heapgettup: failed ReadBuffer"); + } #endif - dp = (Page) BufferGetPage(*b); - lines = lineoff = PageGetMaxOffsetNumber((Page) dp); - linesleft = lines - 1; - if (dir < 0) { - lpp = PageGetItemId(dp, lineoff); - } else { - lpp = PageGetItemId(dp, FirstOffsetNumber); + dp = (Page) BufferGetPage(*b); + lines = lineoff = PageGetMaxOffsetNumber((Page) dp); + linesleft = lines - 1; + if (dir < 0) + { + lpp = PageGetItemId(dp, lineoff); + } + else + { + lpp = PageGetItemId(dp, FirstOffsetNumber); + } } - } } void doinsert(Relation relation, HeapTuple tup) { - RelationPutHeapTupleAtEnd(relation, tup); - return; + RelationPutHeapTupleAtEnd(relation, tup); + return; } -/* - * HeapScanIsValid is now a macro in relscan.h -cim 4/27/91 +/* + * HeapScanIsValid is now a macro in relscan.h -cim 4/27/91 */ #ifdef NOT_USED /* ---------------- - * SetHeapAccessMethodImmediateInvalidation + * SetHeapAccessMethodImmediateInvalidation * ---------------- */ void SetHeapAccessMethodImmediateInvalidation(bool on) { - ImmediateInvalidation = on; + ImmediateInvalidation = on; } + #endif /* ---------------------------------------------------------------- - * heap access method interface + * heap access method interface * ---------------------------------------------------------------- */ /* ---------------- - * heap_open - open a heap relation by relationId + * heap_open - open a heap relation by relationId * - * presently the relcache routines do all the work we need - * to open/close heap relations. + * presently the relcache routines do all the work we need + * to open/close heap relations. * ---------------- */ Relation heap_open(Oid relationId) { - Relation r; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_open); - IncrHeapAccessStat(global_open); - - r = (Relation) RelationIdGetRelation(relationId); - - if (RelationIsValid(r) && r->rd_rel->relkind == RELKIND_INDEX) { - elog(WARN, "%s is an index relation", r->rd_rel->relname.data); - } - - return (r); + Relation r; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_open); + IncrHeapAccessStat(global_open); + + r = (Relation) RelationIdGetRelation(relationId); + + if (RelationIsValid(r) && r->rd_rel->relkind == RELKIND_INDEX) + { + elog(WARN, "%s is an index relation", r->rd_rel->relname.data); + } + + return (r); } /* ---------------- - * heap_openr - open a heap relation by name + * heap_openr - open a heap relation by name * - * presently the relcache routines do all the work we need - * to open/close heap relations. + * presently the relcache routines do all the work we need + * to open/close heap relations. * ---------------- */ Relation heap_openr(char *relationName) { - Relation r; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_openr); - IncrHeapAccessStat(global_openr); - - r = RelationNameGetRelation(relationName); - - if (RelationIsValid(r) && r->rd_rel->relkind == RELKIND_INDEX) { - elog(WARN, "%s is an index relation", r->rd_rel->relname.data); - } - - return (r); + Relation r; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_openr); + IncrHeapAccessStat(global_openr); + + r = RelationNameGetRelation(relationName); + + if (RelationIsValid(r) && r->rd_rel->relkind == RELKIND_INDEX) + { + elog(WARN, "%s is an index relation", r->rd_rel->relname.data); + } + + return (r); } /* ---------------- - * heap_close - close a heap relation + * heap_close - close a heap relation * - * presently the relcache routines do all the work we need - * to open/close heap relations. + * presently the relcache routines do all the work we need + * to open/close heap relations. * ---------------- */ void heap_close(Relation relation) { - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_close); - IncrHeapAccessStat(global_close); - - RelationClose(relation); + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_close); + IncrHeapAccessStat(global_close); + + RelationClose(relation); } /* ---------------- - * heap_beginscan - begin relation scan + * heap_beginscan - begin relation scan * ---------------- */ HeapScanDesc heap_beginscan(Relation relation, - int atend, - TimeQual timeQual, - unsigned nkeys, - ScanKey key) + int atend, + TimeQual timeQual, + unsigned nkeys, + ScanKey key) { - HeapScanDesc sdesc; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_beginscan); - IncrHeapAccessStat(global_beginscan); - - /* ---------------- - * sanity checks - * ---------------- - */ - if (RelationIsValid(relation) == false) - elog(WARN, "heap_beginscan: !RelationIsValid(relation)"); - - /* ---------------- - * set relation level read lock - * ---------------- - */ - RelationSetLockForRead(relation); - - /* XXX someday assert SelfTimeQual if relkind == RELKIND_UNCATALOGED */ - if (relation->rd_rel->relkind == RELKIND_UNCATALOGED) { - timeQual = SelfTimeQual; - } - - /* ---------------- - * increment relation ref count while scanning relation - * ---------------- - */ - RelationIncrementReferenceCount(relation); - - /* ---------------- - * allocate and initialize scan descriptor - * ---------------- - */ - sdesc = (HeapScanDesc) palloc(sizeof(HeapScanDescData)); - - relation->rd_nblocks = smgrnblocks(relation->rd_rel->relsmgr, relation); - sdesc->rs_rd = relation; - - if (nkeys) { - /* - * we do this here instead of in initsdesc() because heap_rescan also - * calls initsdesc() and we don't want to allocate memory again + HeapScanDesc sdesc; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_beginscan); + IncrHeapAccessStat(global_beginscan); + + /* ---------------- + * sanity checks + * ---------------- */ - sdesc->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys); - } else { - sdesc->rs_key = NULL; - } - - initsdesc(sdesc, relation, atend, nkeys, key); - - sdesc->rs_atend = atend; - sdesc->rs_tr = timeQual; - sdesc->rs_nkeys = (short)nkeys; - - return (sdesc); + if (RelationIsValid(relation) == false) + elog(WARN, "heap_beginscan: !RelationIsValid(relation)"); + + /* ---------------- + * set relation level read lock + * ---------------- + */ + RelationSetLockForRead(relation); + + /* XXX someday assert SelfTimeQual if relkind == RELKIND_UNCATALOGED */ + if (relation->rd_rel->relkind == RELKIND_UNCATALOGED) + { + timeQual = SelfTimeQual; + } + + /* ---------------- + * increment relation ref count while scanning relation + * ---------------- + */ + RelationIncrementReferenceCount(relation); + + /* ---------------- + * allocate and initialize scan descriptor + * ---------------- + */ + sdesc = (HeapScanDesc) palloc(sizeof(HeapScanDescData)); + + relation->rd_nblocks = smgrnblocks(relation->rd_rel->relsmgr, relation); + sdesc->rs_rd = relation; + + if (nkeys) + { + + /* + * we do this here instead of in initsdesc() because heap_rescan + * also calls initsdesc() and we don't want to allocate memory + * again + */ + sdesc->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys); + } + else + { + sdesc->rs_key = NULL; + } + + initsdesc(sdesc, relation, atend, nkeys, key); + + sdesc->rs_atend = atend; + sdesc->rs_tr = timeQual; + sdesc->rs_nkeys = (short) nkeys; + + return (sdesc); } /* ---------------- - * heap_rescan - restart a relation scan + * heap_rescan - restart a relation scan * ---------------- */ void heap_rescan(HeapScanDesc sdesc, - bool scanFromEnd, - ScanKey key) + bool scanFromEnd, + ScanKey key) { - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_rescan); - IncrHeapAccessStat(global_rescan); - - /* Note: set relation level read lock is still set */ - - /* ---------------- - * unpin scan buffers - * ---------------- - */ - unpinsdesc(sdesc); - - /* ---------------- - * reinitialize scan descriptor - * ---------------- - */ - initsdesc(sdesc, sdesc->rs_rd, scanFromEnd, sdesc->rs_nkeys, key); - sdesc->rs_atend = (bool) scanFromEnd; + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_rescan); + IncrHeapAccessStat(global_rescan); + + /* Note: set relation level read lock is still set */ + + /* ---------------- + * unpin scan buffers + * ---------------- + */ + unpinsdesc(sdesc); + + /* ---------------- + * reinitialize scan descriptor + * ---------------- + */ + initsdesc(sdesc, sdesc->rs_rd, scanFromEnd, sdesc->rs_nkeys, key); + sdesc->rs_atend = (bool) scanFromEnd; } /* ---------------- - * heap_endscan - end relation scan + * heap_endscan - end relation scan * - * See how to integrate with index scans. - * Check handling if reldesc caching. + * See how to integrate with index scans. + * Check handling if reldesc caching. * ---------------- */ void heap_endscan(HeapScanDesc sdesc) { - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_endscan); - IncrHeapAccessStat(global_endscan); - - /* Note: no locking manipulations needed */ - - /* ---------------- - * unpin scan buffers - * ---------------- - */ - unpinsdesc(sdesc); - - /* ---------------- - * decrement relation reference count and free scan descriptor storage - * ---------------- - */ - RelationDecrementReferenceCount(sdesc->rs_rd); - - /* ---------------- - * Non 2-phase read locks on catalog relations - * ---------------- - */ - if ( IsSystemRelationName(RelationGetRelationName(sdesc->rs_rd)->data) ) - - RelationUnsetLockForRead(sdesc->rs_rd); - - pfree(sdesc); /* XXX */ + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_endscan); + IncrHeapAccessStat(global_endscan); + + /* Note: no locking manipulations needed */ + + /* ---------------- + * unpin scan buffers + * ---------------- + */ + unpinsdesc(sdesc); + + /* ---------------- + * decrement relation reference count and free scan descriptor storage + * ---------------- + */ + RelationDecrementReferenceCount(sdesc->rs_rd); + + /* ---------------- + * Non 2-phase read locks on catalog relations + * ---------------- + */ + if (IsSystemRelationName(RelationGetRelationName(sdesc->rs_rd)->data)) + + RelationUnsetLockForRead(sdesc->rs_rd); + + pfree(sdesc); /* XXX */ } /* ---------------- - * heap_getnext - retrieve next tuple in scan + * heap_getnext - retrieve next tuple in scan * - * Fix to work with index relations. + * Fix to work with index relations. * ---------------- */ #ifdef HEAPDEBUGALL #define HEAPDEBUG_1 \ elog(DEBUG, "heap_getnext([%s,nkeys=%d],backw=%d,0x%x) called", \ - sdesc->rs_rd->rd_rel->relname.data, sdesc->rs_nkeys, backw, b) - + sdesc->rs_rd->rd_rel->relname.data, sdesc->rs_nkeys, backw, b) + #define HEAPDEBUG_2 \ - elog(DEBUG, "heap_getnext called with backw (no tracing yet)") - + elog(DEBUG, "heap_getnext called with backw (no tracing yet)") + #define HEAPDEBUG_3 \ - elog(DEBUG, "heap_getnext returns NULL at end") - + elog(DEBUG, "heap_getnext returns NULL at end") + #define HEAPDEBUG_4 \ - elog(DEBUG, "heap_getnext valid buffer UNPIN'd") - + elog(DEBUG, "heap_getnext valid buffer UNPIN'd") + #define HEAPDEBUG_5 \ - elog(DEBUG, "heap_getnext next tuple was cached") - + elog(DEBUG, "heap_getnext next tuple was cached") + #define HEAPDEBUG_6 \ - elog(DEBUG, "heap_getnext returning EOS") - + elog(DEBUG, "heap_getnext returning EOS") + #define HEAPDEBUG_7 \ - elog(DEBUG, "heap_getnext returning tuple"); + elog(DEBUG, "heap_getnext returning tuple"); #else #define HEAPDEBUG_1 #define HEAPDEBUG_2 @@ -737,715 +799,759 @@ elog(DEBUG, "heap_getnext([%s,nkeys=%d],backw=%d,0x%x) called", \ #define HEAPDEBUG_5 #define HEAPDEBUG_6 #define HEAPDEBUG_7 -#endif /* !defined(HEAPDEBUGALL) */ - - +#endif /* !defined(HEAPDEBUGALL) */ + + HeapTuple heap_getnext(HeapScanDesc scandesc, - int backw, - Buffer *b) + int backw, + Buffer * b) { - register HeapScanDesc sdesc = scandesc; - Buffer localb; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_getnext); - IncrHeapAccessStat(global_getnext); - - /* Note: no locking manipulations needed */ - - /* ---------------- - * argument checks - * ---------------- - */ - if (sdesc == NULL) - elog(WARN, "heap_getnext: NULL relscan"); - - /* ---------------- - * initialize return buffer to InvalidBuffer - * ---------------- - */ - if (! PointerIsValid(b)) b = &localb; - (*b) = InvalidBuffer; - - HEAPDEBUG_1; /* heap_getnext( info ) */ - - if (backw) { + register HeapScanDesc sdesc = scandesc; + Buffer localb; + /* ---------------- - * handle reverse scan + * increment access statistics * ---------------- */ - HEAPDEBUG_2; /* heap_getnext called with backw */ - - if (sdesc->rs_ptup == sdesc->rs_ctup && - BufferIsInvalid(sdesc->rs_pbuf)) - { - if (BufferIsValid(sdesc->rs_nbuf)) - ReleaseBuffer(sdesc->rs_nbuf); - return (NULL); - } - - /* - * Copy the "current" tuple/buffer - * to "next". Pin/unpin the buffers - * accordingly + IncrHeapAccessStat(local_getnext); + IncrHeapAccessStat(global_getnext); + + /* Note: no locking manipulations needed */ + + /* ---------------- + * argument checks + * ---------------- */ - if (sdesc->rs_nbuf != sdesc->rs_cbuf) { - if (BufferIsValid(sdesc->rs_nbuf)) - ReleaseBuffer(sdesc->rs_nbuf); - if (BufferIsValid(sdesc->rs_cbuf)) - IncrBufferRefCount(sdesc->rs_cbuf); - } - sdesc->rs_ntup = sdesc->rs_ctup; - sdesc->rs_nbuf = sdesc->rs_cbuf; - - if (sdesc->rs_ptup != NULL) { - if (sdesc->rs_cbuf != sdesc->rs_pbuf) { - if (BufferIsValid(sdesc->rs_cbuf)) - ReleaseBuffer(sdesc->rs_cbuf); - if (BufferIsValid(sdesc->rs_pbuf)) - IncrBufferRefCount(sdesc->rs_pbuf); - } - sdesc->rs_ctup = sdesc->rs_ptup; - sdesc->rs_cbuf = sdesc->rs_pbuf; - } else { /* NONTUP */ - ItemPointer iptr; - - iptr = (sdesc->rs_ctup != NULL) ? - &(sdesc->rs_ctup->t_ctid) : (ItemPointer) NULL; - - /* Don't release sdesc->rs_cbuf at this point, because - heapgettup doesn't increase PrivateRefCount if it - is already set. On a backward scan, both rs_ctup and rs_ntup - usually point to the same buffer page, so - PrivateRefCount[rs_cbuf] should be 2 (or more, if for instance - ctup is stored in a TupleTableSlot). - 01/09/94 */ - - sdesc->rs_ctup = (HeapTuple) - heapgettup(sdesc->rs_rd, - iptr, - -1, - &(sdesc->rs_cbuf), - sdesc->rs_tr, - sdesc->rs_nkeys, - sdesc->rs_key); - } - - if (sdesc->rs_ctup == NULL && !BufferIsValid(sdesc->rs_cbuf)) - { + if (sdesc == NULL) + elog(WARN, "heap_getnext: NULL relscan"); + + /* ---------------- + * initialize return buffer to InvalidBuffer + * ---------------- + */ + if (!PointerIsValid(b)) + b = &localb; + (*b) = InvalidBuffer; + + HEAPDEBUG_1; /* heap_getnext( info ) */ + + if (backw) + { + /* ---------------- + * handle reverse scan + * ---------------- + */ + HEAPDEBUG_2; /* heap_getnext called with backw */ + + if (sdesc->rs_ptup == sdesc->rs_ctup && + BufferIsInvalid(sdesc->rs_pbuf)) + { + if (BufferIsValid(sdesc->rs_nbuf)) + ReleaseBuffer(sdesc->rs_nbuf); + return (NULL); + } + + /* + * Copy the "current" tuple/buffer to "next". Pin/unpin the + * buffers accordingly + */ + if (sdesc->rs_nbuf != sdesc->rs_cbuf) + { + if (BufferIsValid(sdesc->rs_nbuf)) + ReleaseBuffer(sdesc->rs_nbuf); + if (BufferIsValid(sdesc->rs_cbuf)) + IncrBufferRefCount(sdesc->rs_cbuf); + } + sdesc->rs_ntup = sdesc->rs_ctup; + sdesc->rs_nbuf = sdesc->rs_cbuf; + + if (sdesc->rs_ptup != NULL) + { + if (sdesc->rs_cbuf != sdesc->rs_pbuf) + { + if (BufferIsValid(sdesc->rs_cbuf)) + ReleaseBuffer(sdesc->rs_cbuf); + if (BufferIsValid(sdesc->rs_pbuf)) + IncrBufferRefCount(sdesc->rs_pbuf); + } + sdesc->rs_ctup = sdesc->rs_ptup; + sdesc->rs_cbuf = sdesc->rs_pbuf; + } + else + { /* NONTUP */ + ItemPointer iptr; + + iptr = (sdesc->rs_ctup != NULL) ? + &(sdesc->rs_ctup->t_ctid) : (ItemPointer) NULL; + + /* + * Don't release sdesc->rs_cbuf at this point, because + * heapgettup doesn't increase PrivateRefCount if it is + * already set. On a backward scan, both rs_ctup and rs_ntup + * usually point to the same buffer page, so + * PrivateRefCount[rs_cbuf] should be 2 (or more, if for + * instance ctup is stored in a TupleTableSlot). - 01/09/94 + */ + + sdesc->rs_ctup = (HeapTuple) + heapgettup(sdesc->rs_rd, + iptr, + -1, + &(sdesc->rs_cbuf), + sdesc->rs_tr, + sdesc->rs_nkeys, + sdesc->rs_key); + } + + if (sdesc->rs_ctup == NULL && !BufferIsValid(sdesc->rs_cbuf)) + { + if (BufferIsValid(sdesc->rs_pbuf)) + ReleaseBuffer(sdesc->rs_pbuf); + sdesc->rs_ptup = NULL; + sdesc->rs_pbuf = InvalidBuffer; + if (BufferIsValid(sdesc->rs_nbuf)) + ReleaseBuffer(sdesc->rs_nbuf); + sdesc->rs_ntup = NULL; + sdesc->rs_nbuf = InvalidBuffer; + return (NULL); + } + if (BufferIsValid(sdesc->rs_pbuf)) - ReleaseBuffer(sdesc->rs_pbuf); + ReleaseBuffer(sdesc->rs_pbuf); sdesc->rs_ptup = NULL; - sdesc->rs_pbuf = InvalidBuffer; + sdesc->rs_pbuf = UnknownBuffer; + + } + else + { + /* ---------------- + * handle forward scan + * ---------------- + */ + if (sdesc->rs_ctup == sdesc->rs_ntup && + BufferIsInvalid(sdesc->rs_nbuf)) + { + if (BufferIsValid(sdesc->rs_pbuf)) + ReleaseBuffer(sdesc->rs_pbuf); + HEAPDEBUG_3; /* heap_getnext returns NULL at end */ + return (NULL); + } + + /* + * Copy the "current" tuple/buffer to "previous". Pin/unpin the + * buffers accordingly + */ + if (sdesc->rs_pbuf != sdesc->rs_cbuf) + { + if (BufferIsValid(sdesc->rs_pbuf)) + ReleaseBuffer(sdesc->rs_pbuf); + if (BufferIsValid(sdesc->rs_cbuf)) + IncrBufferRefCount(sdesc->rs_cbuf); + } + sdesc->rs_ptup = sdesc->rs_ctup; + sdesc->rs_pbuf = sdesc->rs_cbuf; + + if (sdesc->rs_ntup != NULL) + { + if (sdesc->rs_cbuf != sdesc->rs_nbuf) + { + if (BufferIsValid(sdesc->rs_cbuf)) + ReleaseBuffer(sdesc->rs_cbuf); + if (BufferIsValid(sdesc->rs_nbuf)) + IncrBufferRefCount(sdesc->rs_nbuf); + } + sdesc->rs_ctup = sdesc->rs_ntup; + sdesc->rs_cbuf = sdesc->rs_nbuf; + HEAPDEBUG_5; /* heap_getnext next tuple was cached */ + } + else + { /* NONTUP */ + ItemPointer iptr; + + iptr = (sdesc->rs_ctup != NULL) ? + &sdesc->rs_ctup->t_ctid : (ItemPointer) NULL; + + /* + * Don't release sdesc->rs_cbuf at this point, because + * heapgettup doesn't increase PrivateRefCount if it is + * already set. On a forward scan, both rs_ctup and rs_ptup + * usually point to the same buffer page, so + * PrivateRefCount[rs_cbuf] should be 2 (or more, if for + * instance ctup is stored in a TupleTableSlot). - 01/09/93 + */ + + sdesc->rs_ctup = (HeapTuple) + heapgettup(sdesc->rs_rd, + iptr, + 1, + &sdesc->rs_cbuf, + sdesc->rs_tr, + sdesc->rs_nkeys, + sdesc->rs_key); + } + + if (sdesc->rs_ctup == NULL && !BufferIsValid(sdesc->rs_cbuf)) + { + if (BufferIsValid(sdesc->rs_nbuf)) + ReleaseBuffer(sdesc->rs_nbuf); + sdesc->rs_ntup = NULL; + sdesc->rs_nbuf = InvalidBuffer; + if (BufferIsValid(sdesc->rs_pbuf)) + ReleaseBuffer(sdesc->rs_pbuf); + sdesc->rs_ptup = NULL; + sdesc->rs_pbuf = InvalidBuffer; + HEAPDEBUG_6; /* heap_getnext returning EOS */ + return (NULL); + } + if (BufferIsValid(sdesc->rs_nbuf)) - ReleaseBuffer(sdesc->rs_nbuf); + ReleaseBuffer(sdesc->rs_nbuf); sdesc->rs_ntup = NULL; - sdesc->rs_nbuf = InvalidBuffer; - return (NULL); - } - - if (BufferIsValid(sdesc->rs_pbuf)) - ReleaseBuffer(sdesc->rs_pbuf); - sdesc->rs_ptup = NULL; - sdesc->rs_pbuf = UnknownBuffer; - - } else { + sdesc->rs_nbuf = UnknownBuffer; + } + /* ---------------- - * handle forward scan + * if we get here it means we have a new current scan tuple, so + * point to the proper return buffer and return the tuple. * ---------------- */ - if (sdesc->rs_ctup == sdesc->rs_ntup && - BufferIsInvalid(sdesc->rs_nbuf)) { - if (BufferIsValid(sdesc->rs_pbuf)) - ReleaseBuffer(sdesc->rs_pbuf); - HEAPDEBUG_3; /* heap_getnext returns NULL at end */ - return (NULL); - } - - /* - * Copy the "current" tuple/buffer - * to "previous". Pin/unpin the buffers - * accordingly - */ - if (sdesc->rs_pbuf != sdesc->rs_cbuf) { - if (BufferIsValid(sdesc->rs_pbuf)) - ReleaseBuffer(sdesc->rs_pbuf); - if (BufferIsValid(sdesc->rs_cbuf)) - IncrBufferRefCount(sdesc->rs_cbuf); - } - sdesc->rs_ptup = sdesc->rs_ctup; - sdesc->rs_pbuf = sdesc->rs_cbuf; - - if (sdesc->rs_ntup != NULL) { - if (sdesc->rs_cbuf != sdesc->rs_nbuf) { - if (BufferIsValid(sdesc->rs_cbuf)) - ReleaseBuffer(sdesc->rs_cbuf); - if (BufferIsValid(sdesc->rs_nbuf)) - IncrBufferRefCount(sdesc->rs_nbuf); - } - sdesc->rs_ctup = sdesc->rs_ntup; - sdesc->rs_cbuf = sdesc->rs_nbuf; - HEAPDEBUG_5; /* heap_getnext next tuple was cached */ - } else { /* NONTUP */ - ItemPointer iptr; - - iptr = (sdesc->rs_ctup != NULL) ? - &sdesc->rs_ctup->t_ctid : (ItemPointer) NULL; - - /* Don't release sdesc->rs_cbuf at this point, because - heapgettup doesn't increase PrivateRefCount if it - is already set. On a forward scan, both rs_ctup and rs_ptup - usually point to the same buffer page, so - PrivateRefCount[rs_cbuf] should be 2 (or more, if for instance - ctup is stored in a TupleTableSlot). - 01/09/93 */ - - sdesc->rs_ctup = (HeapTuple) - heapgettup(sdesc->rs_rd, - iptr, - 1, - &sdesc->rs_cbuf, - sdesc->rs_tr, - sdesc->rs_nkeys, - sdesc->rs_key); - } - - if (sdesc->rs_ctup == NULL && !BufferIsValid(sdesc->rs_cbuf)) { - if (BufferIsValid(sdesc->rs_nbuf)) - ReleaseBuffer(sdesc->rs_nbuf); - sdesc->rs_ntup = NULL; - sdesc->rs_nbuf = InvalidBuffer; - if (BufferIsValid(sdesc->rs_pbuf)) - ReleaseBuffer(sdesc->rs_pbuf); - sdesc->rs_ptup = NULL; - sdesc->rs_pbuf = InvalidBuffer; - HEAPDEBUG_6; /* heap_getnext returning EOS */ - return (NULL); - } - - if (BufferIsValid(sdesc->rs_nbuf)) - ReleaseBuffer(sdesc->rs_nbuf); - sdesc->rs_ntup = NULL; - sdesc->rs_nbuf = UnknownBuffer; - } - - /* ---------------- - * if we get here it means we have a new current scan tuple, so - * point to the proper return buffer and return the tuple. - * ---------------- - */ - (*b) = sdesc->rs_cbuf; - - HEAPDEBUG_7; /* heap_getnext returning tuple */ - - return (sdesc->rs_ctup); + (*b) = sdesc->rs_cbuf; + + HEAPDEBUG_7; /* heap_getnext returning tuple */ + + return (sdesc->rs_ctup); } /* ---------------- - * heap_fetch - retrive tuple with tid + * heap_fetch - retrive tuple with tid * - * Currently ignores LP_IVALID during processing! + * Currently ignores LP_IVALID during processing! * ---------------- */ HeapTuple heap_fetch(Relation relation, - TimeQual timeQual, - ItemPointer tid, - Buffer *b) + TimeQual timeQual, + ItemPointer tid, + Buffer * b) { - ItemId lp; - Buffer buffer; - PageHeader dp; - HeapTuple tuple; - OffsetNumber offnum; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_fetch); - IncrHeapAccessStat(global_fetch); - - /* - * Note: This is collosally expensive - does two system calls per - * indexscan tuple fetch. Not good, and since we should be doing - * page level locking by the scanner anyway, it is commented out. - */ - - /* RelationSetLockForTupleRead(relation, tid); */ - - /* ---------------- - * get the buffer from the relation descriptor - * Note that this does a buffer pin. - * ---------------- - */ - - buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); - + ItemId lp; + Buffer buffer; + PageHeader dp; + HeapTuple tuple; + OffsetNumber offnum; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_fetch); + IncrHeapAccessStat(global_fetch); + + /* + * Note: This is collosally expensive - does two system calls per + * indexscan tuple fetch. Not good, and since we should be doing page + * level locking by the scanner anyway, it is commented out. + */ + + /* RelationSetLockForTupleRead(relation, tid); */ + + /* ---------------- + * get the buffer from the relation descriptor + * Note that this does a buffer pin. + * ---------------- + */ + + buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); + #ifndef NO_BUFFERISVALID - if (!BufferIsValid(buffer)) { - elog(WARN, "heap_fetch: %s relation: ReadBuffer(%lx) failed", - &relation->rd_rel->relname, (long)tid); - } + if (!BufferIsValid(buffer)) + { + elog(WARN, "heap_fetch: %s relation: ReadBuffer(%lx) failed", + &relation->rd_rel->relname, (long) tid); + } #endif - - /* ---------------- - * get the item line pointer corresponding to the requested tid - * ---------------- - */ - dp = (PageHeader) BufferGetPage(buffer); - offnum = ItemPointerGetOffsetNumber(tid); - lp = PageGetItemId(dp, offnum); - - /* ---------------- - * more sanity checks - * ---------------- - */ - - Assert(ItemIdIsUsed(lp)); - - /* ---------------- - * check time qualification of tid - * ---------------- - */ - - tuple = heap_tuple_satisfies(lp, relation, buffer, dp, - timeQual, 0,(ScanKey)NULL); - - if (tuple == NULL) + + /* ---------------- + * get the item line pointer corresponding to the requested tid + * ---------------- + */ + dp = (PageHeader) BufferGetPage(buffer); + offnum = ItemPointerGetOffsetNumber(tid); + lp = PageGetItemId(dp, offnum); + + /* ---------------- + * more sanity checks + * ---------------- + */ + + Assert(ItemIdIsUsed(lp)); + + /* ---------------- + * check time qualification of tid + * ---------------- + */ + + tuple = heap_tuple_satisfies(lp, relation, buffer, dp, + timeQual, 0, (ScanKey) NULL); + + if (tuple == NULL) { - ReleaseBuffer(buffer); - return (NULL); + ReleaseBuffer(buffer); + return (NULL); } - - /* ---------------- - * all checks passed, now either return a copy of the tuple - * or pin the buffer page and return a pointer, depending on - * whether caller gave us a valid b. - * ---------------- - */ - - if (PointerIsValid(b)) { - *b = buffer; - } else { - tuple = heap_copytuple(tuple); - ReleaseBuffer(buffer); - } - return (tuple); + + /* ---------------- + * all checks passed, now either return a copy of the tuple + * or pin the buffer page and return a pointer, depending on + * whether caller gave us a valid b. + * ---------------- + */ + + if (PointerIsValid(b)) + { + *b = buffer; + } + else + { + tuple = heap_copytuple(tuple); + ReleaseBuffer(buffer); + } + return (tuple); } /* ---------------- - * heap_insert - insert tuple + * heap_insert - insert tuple * - * The assignment of t_min (and thus the others) should be - * removed eventually. + * The assignment of t_min (and thus the others) should be + * removed eventually. * - * Currently places the tuple onto the last page. If there is no room, - * it is placed on new pages. (Heap relations) - * Note that concurrent inserts during a scan will probably have - * unexpected results, though this will be fixed eventually. + * Currently places the tuple onto the last page. If there is no room, + * it is placed on new pages. (Heap relations) + * Note that concurrent inserts during a scan will probably have + * unexpected results, though this will be fixed eventually. * - * Fix to work with indexes. + * Fix to work with indexes. * ---------------- */ Oid heap_insert(Relation relation, HeapTuple tup) { - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_insert); - IncrHeapAccessStat(global_insert); - - /* ---------------- - * set relation level write lock. If this is a "local" relation (not - * visible to others), we don't need to set a write lock. - * ---------------- - */ - if (!relation->rd_islocal) - RelationSetLockForWrite(relation); + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_insert); + IncrHeapAccessStat(global_insert); - /* ---------------- - * If the object id of this tuple has already been assigned, trust - * the caller. There are a couple of ways this can happen. At initial - * db creation, the backend program sets oids for tuples. When we - * define an index, we set the oid. Finally, in the future, we may - * allow users to set their own object ids in order to support a - * persistent object store (objects need to contain pointers to one - * another). - * ---------------- - */ - if (!OidIsValid(tup->t_oid)) { - tup->t_oid = newoid(); - LastOidProcessed = tup->t_oid; - } - else - CheckMaxObjectId(tup->t_oid); - - TransactionIdStore(GetCurrentTransactionId(), &(tup->t_xmin)); - tup->t_cmin = GetCurrentCommandId(); - StoreInvalidTransactionId(&(tup->t_xmax)); - tup->t_tmin = INVALID_ABSTIME; - tup->t_tmax = CURRENT_ABSTIME; - - doinsert(relation, tup); - - if ( IsSystemRelationName(RelationGetRelationName(relation)->data)) { - RelationUnsetLockForWrite(relation); - /* ---------------- - * invalidate caches (only works for system relations) + * set relation level write lock. If this is a "local" relation (not + * visible to others), we don't need to set a write lock. * ---------------- */ - SetRefreshWhenInvalidate(ImmediateInvalidation); - RelationInvalidateHeapTuple(relation, tup); - SetRefreshWhenInvalidate((bool)!ImmediateInvalidation); - } - - return(tup->t_oid); + if (!relation->rd_islocal) + RelationSetLockForWrite(relation); + + /* ---------------- + * If the object id of this tuple has already been assigned, trust + * the caller. There are a couple of ways this can happen. At initial + * db creation, the backend program sets oids for tuples. When we + * define an index, we set the oid. Finally, in the future, we may + * allow users to set their own object ids in order to support a + * persistent object store (objects need to contain pointers to one + * another). + * ---------------- + */ + if (!OidIsValid(tup->t_oid)) + { + tup->t_oid = newoid(); + LastOidProcessed = tup->t_oid; + } + else + CheckMaxObjectId(tup->t_oid); + + TransactionIdStore(GetCurrentTransactionId(), &(tup->t_xmin)); + tup->t_cmin = GetCurrentCommandId(); + StoreInvalidTransactionId(&(tup->t_xmax)); + tup->t_tmin = INVALID_ABSTIME; + tup->t_tmax = CURRENT_ABSTIME; + + doinsert(relation, tup); + + if (IsSystemRelationName(RelationGetRelationName(relation)->data)) + { + RelationUnsetLockForWrite(relation); + + /* ---------------- + * invalidate caches (only works for system relations) + * ---------------- + */ + SetRefreshWhenInvalidate(ImmediateInvalidation); + RelationInvalidateHeapTuple(relation, tup); + SetRefreshWhenInvalidate((bool) ! ImmediateInvalidation); + } + + return (tup->t_oid); } /* ---------------- - * heap_delete - delete a tuple + * heap_delete - delete a tuple * - * Must decide how to handle errors. + * Must decide how to handle errors. * ---------------- */ int heap_delete(Relation relation, ItemPointer tid) { - ItemId lp; - HeapTuple tp; - PageHeader dp; - Buffer b; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_delete); - IncrHeapAccessStat(global_delete); - - /* ---------------- - * sanity check - * ---------------- - */ - Assert(ItemPointerIsValid(tid)); - - /* ---------------- - * set relation level write lock - * ---------------- - */ - RelationSetLockForWrite(relation); - - b = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); - + ItemId lp; + HeapTuple tp; + PageHeader dp; + Buffer b; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_delete); + IncrHeapAccessStat(global_delete); + + /* ---------------- + * sanity check + * ---------------- + */ + Assert(ItemPointerIsValid(tid)); + + /* ---------------- + * set relation level write lock + * ---------------- + */ + RelationSetLockForWrite(relation); + + b = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); + #ifndef NO_BUFFERISVALID - if (!BufferIsValid(b)) { /* XXX L_SH better ??? */ - elog(WARN, "heap_delete: failed ReadBuffer"); - } -#endif /* NO_BUFFERISVALID */ - - dp = (PageHeader) BufferGetPage(b); - lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); - - /* - * Just like test against non-functional updates we try to catch - * non-functional delete attempts. - vadim 05/05/97 - */ - tp = (HeapTuple) PageGetItem((Page)dp, lp); - Assert(HeapTupleIsValid(tp)); - if (TupleUpdatedByCurXactAndCmd(tp)) { - elog(NOTICE, "Non-functional delete, tuple already deleted"); - if ( IsSystemRelationName(RelationGetRelationName(relation)->data) ) - RelationUnsetLockForWrite(relation); - ReleaseBuffer(b); - return (1); - } - /* ---------------- - * check that we're deleteing a valid item - * ---------------- - */ - if (!(tp = heap_tuple_satisfies(lp, relation, b, dp, - NowTimeQual, 0, (ScanKey) NULL))) { - - /* XXX call something else */ - ReleaseBuffer(b); - - elog(WARN, "heap_delete: (am)invalid tid"); - } - - /* ---------------- - * get the tuple and lock tell the buffer manager we want - * exclusive access to the page - * ---------------- - */ - - /* ---------------- - * store transaction information of xact deleting the tuple - * ---------------- - */ - TransactionIdStore(GetCurrentTransactionId(), &(tp->t_xmax)); - tp->t_cmax = GetCurrentCommandId(); - ItemPointerSetInvalid(&tp->t_chain); - - /* ---------------- - * invalidate caches - * ---------------- - */ - SetRefreshWhenInvalidate(ImmediateInvalidation); - RelationInvalidateHeapTuple(relation, tp); - SetRefreshWhenInvalidate((bool)!ImmediateInvalidation); - - WriteBuffer(b); - if ( IsSystemRelationName(RelationGetRelationName(relation)->data) ) - RelationUnsetLockForWrite(relation); - - return(0); + if (!BufferIsValid(b)) + { /* XXX L_SH better ??? */ + elog(WARN, "heap_delete: failed ReadBuffer"); + } +#endif /* NO_BUFFERISVALID */ + + dp = (PageHeader) BufferGetPage(b); + lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); + + /* + * Just like test against non-functional updates we try to catch + * non-functional delete attempts. - vadim 05/05/97 + */ + tp = (HeapTuple) PageGetItem((Page) dp, lp); + Assert(HeapTupleIsValid(tp)); + if (TupleUpdatedByCurXactAndCmd(tp)) + { + elog(NOTICE, "Non-functional delete, tuple already deleted"); + if (IsSystemRelationName(RelationGetRelationName(relation)->data)) + RelationUnsetLockForWrite(relation); + ReleaseBuffer(b); + return (1); + } + /* ---------------- + * check that we're deleteing a valid item + * ---------------- + */ + if (!(tp = heap_tuple_satisfies(lp, relation, b, dp, + NowTimeQual, 0, (ScanKey) NULL))) + { + + /* XXX call something else */ + ReleaseBuffer(b); + + elog(WARN, "heap_delete: (am)invalid tid"); + } + + /* ---------------- + * get the tuple and lock tell the buffer manager we want + * exclusive access to the page + * ---------------- + */ + + /* ---------------- + * store transaction information of xact deleting the tuple + * ---------------- + */ + TransactionIdStore(GetCurrentTransactionId(), &(tp->t_xmax)); + tp->t_cmax = GetCurrentCommandId(); + ItemPointerSetInvalid(&tp->t_chain); + + /* ---------------- + * invalidate caches + * ---------------- + */ + SetRefreshWhenInvalidate(ImmediateInvalidation); + RelationInvalidateHeapTuple(relation, tp); + SetRefreshWhenInvalidate((bool) ! ImmediateInvalidation); + + WriteBuffer(b); + if (IsSystemRelationName(RelationGetRelationName(relation)->data)) + RelationUnsetLockForWrite(relation); + + return (0); } /* ---------------- - * heap_replace - replace a tuple + * heap_replace - replace a tuple + * + * Must decide how to handle errors. * - * Must decide how to handle errors. + * Fix arguments, work with indexes. * - * Fix arguments, work with indexes. - * - * 12/30/93 - modified the return value to be 1 when - * a non-functional update is detected. This - * prevents the calling routine from updating - * indices unnecessarily. -kw + * 12/30/93 - modified the return value to be 1 when + * a non-functional update is detected. This + * prevents the calling routine from updating + * indices unnecessarily. -kw * * ---------------- */ int heap_replace(Relation relation, ItemPointer otid, HeapTuple tup) { - ItemId lp; - HeapTuple tp; - Page dp; - Buffer buffer; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_replace); - IncrHeapAccessStat(global_replace); - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(ItemPointerIsValid(otid)); - - /* ---------------- - * set relation level write lock - * ---------------- - */ - if (!relation->rd_islocal) - RelationSetLockForWrite(relation); - - buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(otid)); + ItemId lp; + HeapTuple tp; + Page dp; + Buffer buffer; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_replace); + IncrHeapAccessStat(global_replace); + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(ItemPointerIsValid(otid)); + + /* ---------------- + * set relation level write lock + * ---------------- + */ + if (!relation->rd_islocal) + RelationSetLockForWrite(relation); + + buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(otid)); #ifndef NO_BUFFERISVALID - if (!BufferIsValid(buffer)) { - /* XXX L_SH better ??? */ - elog(WARN, "amreplace: failed ReadBuffer"); - } -#endif /* NO_BUFFERISVALID */ - - dp = (Page) BufferGetPage(buffer); - lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(otid)); - - /* ---------------- - * logically delete old item - * ---------------- - */ - - tp = (HeapTuple) PageGetItem(dp, lp); - Assert(HeapTupleIsValid(tp)); - - /* ----------------- - * the following test should be able to catch all non-functional - * update attempts and shut out all ghost tuples. - * XXX In the future, Spyros may need to update the rule lock on a tuple - * more than once within the same command and same transaction. - * He will have to introduce a new flag to override the following check. - * -- Wei - * - * ----------------- - */ - - if (TupleUpdatedByCurXactAndCmd(tp)) { - elog(NOTICE, "Non-functional update, only first update is performed"); - if ( IsSystemRelationName(RelationGetRelationName(relation)->data) ) - RelationUnsetLockForWrite(relation); - ReleaseBuffer(buffer); - return(1); - } - - /* ---------------- - * check that we're replacing a valid item - - * - * NOTE that this check must follow the non-functional update test - * above as it can happen that we try to 'replace' the same tuple - * twice in a single transaction. The second time around the - * tuple will fail the NowTimeQual. We don't want to abort the - * xact, we only want to flag the 'non-functional' NOTICE. -mer - * ---------------- - */ - if (!heap_tuple_satisfies(lp, - relation, - buffer, - (PageHeader)dp, - NowTimeQual, - 0, - (ScanKey)NULL)) - { - ReleaseBuffer(buffer); - elog(WARN, "heap_replace: (am)invalid otid"); - } - - /* XXX order problems if not atomic assignment ??? */ - tup->t_oid = tp->t_oid; - TransactionIdStore(GetCurrentTransactionId(), &(tup->t_xmin)); - tup->t_cmin = GetCurrentCommandId(); - StoreInvalidTransactionId(&(tup->t_xmax)); - tup->t_tmin = INVALID_ABSTIME; - tup->t_tmax = CURRENT_ABSTIME; - ItemPointerSetInvalid(&tup->t_chain); - - /* ---------------- - * insert new item - * ---------------- - */ - if ((unsigned)DOUBLEALIGN(tup->t_len) <= PageGetFreeSpace((Page) dp)) { - RelationPutHeapTuple(relation, BufferGetBlockNumber(buffer), tup); - } else { + if (!BufferIsValid(buffer)) + { + /* XXX L_SH better ??? */ + elog(WARN, "amreplace: failed ReadBuffer"); + } +#endif /* NO_BUFFERISVALID */ + + dp = (Page) BufferGetPage(buffer); + lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(otid)); + /* ---------------- - * new item won't fit on same page as old item, have to look - * for a new place to put it. + * logically delete old item * ---------------- */ - doinsert(relation, tup); - } - - /* ---------------- - * new item in place, now record transaction information - * ---------------- - */ - TransactionIdStore(GetCurrentTransactionId(), &(tp->t_xmax)); - tp->t_cmax = GetCurrentCommandId(); - tp->t_chain = tup->t_ctid; - - /* ---------------- - * invalidate caches - * ---------------- - */ - SetRefreshWhenInvalidate(ImmediateInvalidation); - RelationInvalidateHeapTuple(relation, tp); - SetRefreshWhenInvalidate((bool)!ImmediateInvalidation); - - WriteBuffer(buffer); - - if ( IsSystemRelationName(RelationGetRelationName(relation)->data) ) - RelationUnsetLockForWrite(relation); - - return(0); + + tp = (HeapTuple) PageGetItem(dp, lp); + Assert(HeapTupleIsValid(tp)); + + /* ----------------- + * the following test should be able to catch all non-functional + * update attempts and shut out all ghost tuples. + * XXX In the future, Spyros may need to update the rule lock on a tuple + * more than once within the same command and same transaction. + * He will have to introduce a new flag to override the following check. + * -- Wei + * + * ----------------- + */ + + if (TupleUpdatedByCurXactAndCmd(tp)) + { + elog(NOTICE, "Non-functional update, only first update is performed"); + if (IsSystemRelationName(RelationGetRelationName(relation)->data)) + RelationUnsetLockForWrite(relation); + ReleaseBuffer(buffer); + return (1); + } + + /* ---------------- + * check that we're replacing a valid item - + * + * NOTE that this check must follow the non-functional update test + * above as it can happen that we try to 'replace' the same tuple + * twice in a single transaction. The second time around the + * tuple will fail the NowTimeQual. We don't want to abort the + * xact, we only want to flag the 'non-functional' NOTICE. -mer + * ---------------- + */ + if (!heap_tuple_satisfies(lp, + relation, + buffer, + (PageHeader) dp, + NowTimeQual, + 0, + (ScanKey) NULL)) + { + ReleaseBuffer(buffer); + elog(WARN, "heap_replace: (am)invalid otid"); + } + + /* XXX order problems if not atomic assignment ??? */ + tup->t_oid = tp->t_oid; + TransactionIdStore(GetCurrentTransactionId(), &(tup->t_xmin)); + tup->t_cmin = GetCurrentCommandId(); + StoreInvalidTransactionId(&(tup->t_xmax)); + tup->t_tmin = INVALID_ABSTIME; + tup->t_tmax = CURRENT_ABSTIME; + ItemPointerSetInvalid(&tup->t_chain); + + /* ---------------- + * insert new item + * ---------------- + */ + if ((unsigned) DOUBLEALIGN(tup->t_len) <= PageGetFreeSpace((Page) dp)) + { + RelationPutHeapTuple(relation, BufferGetBlockNumber(buffer), tup); + } + else + { + /* ---------------- + * new item won't fit on same page as old item, have to look + * for a new place to put it. + * ---------------- + */ + doinsert(relation, tup); + } + + /* ---------------- + * new item in place, now record transaction information + * ---------------- + */ + TransactionIdStore(GetCurrentTransactionId(), &(tp->t_xmax)); + tp->t_cmax = GetCurrentCommandId(); + tp->t_chain = tup->t_ctid; + + /* ---------------- + * invalidate caches + * ---------------- + */ + SetRefreshWhenInvalidate(ImmediateInvalidation); + RelationInvalidateHeapTuple(relation, tp); + SetRefreshWhenInvalidate((bool) ! ImmediateInvalidation); + + WriteBuffer(buffer); + + if (IsSystemRelationName(RelationGetRelationName(relation)->data)) + RelationUnsetLockForWrite(relation); + + return (0); } /* ---------------- - * heap_markpos - mark scan position + * heap_markpos - mark scan position * - * Note: - * Should only one mark be maintained per scan at one time. - * Check if this can be done generally--say calls to get the - * next/previous tuple and NEVER pass struct scandesc to the - * user AM's. Now, the mark is sent to the executor for safekeeping. - * Probably can store this info into a GENERAL scan structure. + * Note: + * Should only one mark be maintained per scan at one time. + * Check if this can be done generally--say calls to get the + * next/previous tuple and NEVER pass struct scandesc to the + * user AM's. Now, the mark is sent to the executor for safekeeping. + * Probably can store this info into a GENERAL scan structure. * - * May be best to change this call to store the marked position - * (up to 2?) in the scan structure itself. - * Fix to use the proper caching structure. + * May be best to change this call to store the marked position + * (up to 2?) in the scan structure itself. + * Fix to use the proper caching structure. * ---------------- */ void heap_markpos(HeapScanDesc sdesc) { - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_markpos); - IncrHeapAccessStat(global_markpos); - - /* Note: no locking manipulations needed */ - - if (sdesc->rs_ptup == NULL && - BufferIsUnknown(sdesc->rs_pbuf)) { /* == NONTUP */ - sdesc->rs_ptup = (HeapTuple) - heapgettup(sdesc->rs_rd, - (sdesc->rs_ctup == NULL) ? - (ItemPointer)NULL : &sdesc->rs_ctup->t_ctid, - -1, - &sdesc->rs_pbuf, - sdesc->rs_tr, - sdesc->rs_nkeys, - sdesc->rs_key); - - } else if (sdesc->rs_ntup == NULL && - BufferIsUnknown(sdesc->rs_nbuf)) { /* == NONTUP */ - sdesc->rs_ntup = (HeapTuple) - heapgettup(sdesc->rs_rd, - (sdesc->rs_ctup == NULL) ? - (ItemPointer)NULL : &sdesc->rs_ctup->t_ctid, - 1, - &sdesc->rs_nbuf, - sdesc->rs_tr, - sdesc->rs_nkeys, - sdesc->rs_key); - } - - /* ---------------- - * Should not unpin the buffer pages. They may still be in use. - * ---------------- - */ - if (sdesc->rs_ptup != NULL) { - sdesc->rs_mptid = sdesc->rs_ptup->t_ctid; - } else { - ItemPointerSetInvalid(&sdesc->rs_mptid); - } - if (sdesc->rs_ctup != NULL) { - sdesc->rs_mctid = sdesc->rs_ctup->t_ctid; - } else { - ItemPointerSetInvalid(&sdesc->rs_mctid); - } - if (sdesc->rs_ntup != NULL) { - sdesc->rs_mntid = sdesc->rs_ntup->t_ctid; - } else { - ItemPointerSetInvalid(&sdesc->rs_mntid); - } + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_markpos); + IncrHeapAccessStat(global_markpos); + + /* Note: no locking manipulations needed */ + + if (sdesc->rs_ptup == NULL && + BufferIsUnknown(sdesc->rs_pbuf)) + { /* == NONTUP */ + sdesc->rs_ptup = (HeapTuple) + heapgettup(sdesc->rs_rd, + (sdesc->rs_ctup == NULL) ? + (ItemPointer) NULL : &sdesc->rs_ctup->t_ctid, + -1, + &sdesc->rs_pbuf, + sdesc->rs_tr, + sdesc->rs_nkeys, + sdesc->rs_key); + + } + else if (sdesc->rs_ntup == NULL && + BufferIsUnknown(sdesc->rs_nbuf)) + { /* == NONTUP */ + sdesc->rs_ntup = (HeapTuple) + heapgettup(sdesc->rs_rd, + (sdesc->rs_ctup == NULL) ? + (ItemPointer) NULL : &sdesc->rs_ctup->t_ctid, + 1, + &sdesc->rs_nbuf, + sdesc->rs_tr, + sdesc->rs_nkeys, + sdesc->rs_key); + } + + /* ---------------- + * Should not unpin the buffer pages. They may still be in use. + * ---------------- + */ + if (sdesc->rs_ptup != NULL) + { + sdesc->rs_mptid = sdesc->rs_ptup->t_ctid; + } + else + { + ItemPointerSetInvalid(&sdesc->rs_mptid); + } + if (sdesc->rs_ctup != NULL) + { + sdesc->rs_mctid = sdesc->rs_ctup->t_ctid; + } + else + { + ItemPointerSetInvalid(&sdesc->rs_mctid); + } + if (sdesc->rs_ntup != NULL) + { + sdesc->rs_mntid = sdesc->rs_ntup->t_ctid; + } + else + { + ItemPointerSetInvalid(&sdesc->rs_mntid); + } } /* ---------------- - * heap_restrpos - restore position to marked location + * heap_restrpos - restore position to marked location * - * Note: there are bad side effects here. If we were past the end - * of a relation when heapmarkpos is called, then if the relation is - * extended via insert, then the next call to heaprestrpos will set - * cause the added tuples to be visible when the scan continues. - * Problems also arise if the TID's are rearranged!!! + * Note: there are bad side effects here. If we were past the end + * of a relation when heapmarkpos is called, then if the relation is + * extended via insert, then the next call to heaprestrpos will set + * cause the added tuples to be visible when the scan continues. + * Problems also arise if the TID's are rearranged!!! * - * Now pins buffer once for each valid tuple pointer (rs_ptup, - * rs_ctup, rs_ntup) referencing it. - * - 01/13/94 + * Now pins buffer once for each valid tuple pointer (rs_ptup, + * rs_ctup, rs_ntup) referencing it. + * - 01/13/94 * * XXX might be better to do direct access instead of - * using the generality of heapgettup(). + * using the generality of heapgettup(). * * XXX It is very possible that when a scan is restored, that a tuple * XXX which previously qualified may fail for time range purposes, unless @@ -1455,60 +1561,69 @@ heap_markpos(HeapScanDesc sdesc) void heap_restrpos(HeapScanDesc sdesc) { - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_restrpos); - IncrHeapAccessStat(global_restrpos); - - /* XXX no amrestrpos checking that ammarkpos called */ - - /* Note: no locking manipulations needed */ - - unpinsdesc(sdesc); - - /* force heapgettup to pin buffer for each loaded tuple */ - sdesc->rs_pbuf = InvalidBuffer; - sdesc->rs_cbuf = InvalidBuffer; - sdesc->rs_nbuf = InvalidBuffer; - - if (!ItemPointerIsValid(&sdesc->rs_mptid)) { - sdesc->rs_ptup = NULL; - } else { - sdesc->rs_ptup = (HeapTuple) - heapgettup(sdesc->rs_rd, - &sdesc->rs_mptid, - 0, - &sdesc->rs_pbuf, - NowTimeQual, - 0, - (ScanKey) NULL); - } - - if (!ItemPointerIsValid(&sdesc->rs_mctid)) { - sdesc->rs_ctup = NULL; - } else { - sdesc->rs_ctup = (HeapTuple) - heapgettup(sdesc->rs_rd, - &sdesc->rs_mctid, - 0, - &sdesc->rs_cbuf, - NowTimeQual, - 0, - (ScanKey) NULL); - } - - if (!ItemPointerIsValid(&sdesc->rs_mntid)) { - sdesc->rs_ntup = NULL; - } else { - sdesc->rs_ntup = (HeapTuple) - heapgettup(sdesc->rs_rd, - &sdesc->rs_mntid, - 0, - &sdesc->rs_nbuf, - NowTimeQual, - 0, - (ScanKey) NULL); - } + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_restrpos); + IncrHeapAccessStat(global_restrpos); + + /* XXX no amrestrpos checking that ammarkpos called */ + + /* Note: no locking manipulations needed */ + + unpinsdesc(sdesc); + + /* force heapgettup to pin buffer for each loaded tuple */ + sdesc->rs_pbuf = InvalidBuffer; + sdesc->rs_cbuf = InvalidBuffer; + sdesc->rs_nbuf = InvalidBuffer; + + if (!ItemPointerIsValid(&sdesc->rs_mptid)) + { + sdesc->rs_ptup = NULL; + } + else + { + sdesc->rs_ptup = (HeapTuple) + heapgettup(sdesc->rs_rd, + &sdesc->rs_mptid, + 0, + &sdesc->rs_pbuf, + NowTimeQual, + 0, + (ScanKey) NULL); + } + + if (!ItemPointerIsValid(&sdesc->rs_mctid)) + { + sdesc->rs_ctup = NULL; + } + else + { + sdesc->rs_ctup = (HeapTuple) + heapgettup(sdesc->rs_rd, + &sdesc->rs_mctid, + 0, + &sdesc->rs_cbuf, + NowTimeQual, + 0, + (ScanKey) NULL); + } + + if (!ItemPointerIsValid(&sdesc->rs_mntid)) + { + sdesc->rs_ntup = NULL; + } + else + { + sdesc->rs_ntup = (HeapTuple) + heapgettup(sdesc->rs_rd, + &sdesc->rs_mntid, + 0, + &sdesc->rs_nbuf, + NowTimeQual, + 0, + (ScanKey) NULL); + } } diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index f172a404708..0854b69bf0b 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * hio.c-- - * POSTGRES heap access method input/output code. + * POSTGRES heap access method input/output code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Id: hio.c,v 1.9 1996/11/05 09:53:02 scrappy Exp $ + * $Id: hio.c,v 1.10 1997/09/07 04:38:11 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,64 +21,65 @@ /* * amputunique - place tuple at tid - * Currently on errors, calls elog. Perhaps should return -1? - * Possible errors include the addition of a tuple to the page - * between the time the linep is chosen and the page is L_UP'd. + * Currently on errors, calls elog. Perhaps should return -1? + * Possible errors include the addition of a tuple to the page + * between the time the linep is chosen and the page is L_UP'd. * - * This should be coordinated with the B-tree code. - * Probably needs to have an amdelunique to allow for - * internal index records to be deleted and reordered as needed. - * For the heap AM, this should never be needed. + * This should be coordinated with the B-tree code. + * Probably needs to have an amdelunique to allow for + * internal index records to be deleted and reordered as needed. + * For the heap AM, this should never be needed. */ void RelationPutHeapTuple(Relation relation, - BlockNumber blockIndex, - HeapTuple tuple) + BlockNumber blockIndex, + HeapTuple tuple) { - Buffer buffer; - Page pageHeader; - BlockNumber numberOfBlocks; - OffsetNumber offnum; - unsigned int len; - ItemId itemId; - Item item; - - /* ---------------- - * increment access statistics - * ---------------- - */ - IncrHeapAccessStat(local_RelationPutHeapTuple); - IncrHeapAccessStat(global_RelationPutHeapTuple); - - Assert(RelationIsValid(relation)); - Assert(HeapTupleIsValid(tuple)); - - numberOfBlocks = RelationGetNumberOfBlocks(relation); - Assert(blockIndex < numberOfBlocks); - - buffer = ReadBuffer(relation, blockIndex); + Buffer buffer; + Page pageHeader; + BlockNumber numberOfBlocks; + OffsetNumber offnum; + unsigned int len; + ItemId itemId; + Item item; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_RelationPutHeapTuple); + IncrHeapAccessStat(global_RelationPutHeapTuple); + + Assert(RelationIsValid(relation)); + Assert(HeapTupleIsValid(tuple)); + + numberOfBlocks = RelationGetNumberOfBlocks(relation); + Assert(blockIndex < numberOfBlocks); + + buffer = ReadBuffer(relation, blockIndex); #ifndef NO_BUFFERISVALID - if (!BufferIsValid(buffer)) { - elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s", - blockIndex, &relation->rd_rel->relname); - } + if (!BufferIsValid(buffer)) + { + elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s", + blockIndex, &relation->rd_rel->relname); + } #endif - - pageHeader = (Page)BufferGetPage(buffer); - len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */ - Assert((int)len <= PageGetFreeSpace(pageHeader)); - - offnum = PageAddItem((Page)pageHeader, (Item)tuple, - tuple->t_len, InvalidOffsetNumber, LP_USED); - - itemId = PageGetItemId((Page)pageHeader, offnum); - item = PageGetItem((Page)pageHeader, itemId); - - ItemPointerSet(&((HeapTuple)item)->t_ctid, blockIndex, offnum); - - WriteBuffer(buffer); - /* return an accurate tuple */ - ItemPointerSet(&tuple->t_ctid, blockIndex, offnum); + + pageHeader = (Page) BufferGetPage(buffer); + len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */ + Assert((int) len <= PageGetFreeSpace(pageHeader)); + + offnum = PageAddItem((Page) pageHeader, (Item) tuple, + tuple->t_len, InvalidOffsetNumber, LP_USED); + + itemId = PageGetItemId((Page) pageHeader, offnum); + item = PageGetItem((Page) pageHeader, itemId); + + ItemPointerSet(&((HeapTuple) item)->t_ctid, blockIndex, offnum); + + WriteBuffer(buffer); + /* return an accurate tuple */ + ItemPointerSet(&tuple->t_ctid, blockIndex, offnum); } /* @@ -91,7 +92,7 @@ RelationPutHeapTuple(Relation relation, * Eventually, we should cache the number of blocks in a relation somewhere. * Until that time, this code will have to do an lseek to determine the number * of blocks in a relation. - * + * * This code should ideally do at most 4 semops, 1 lseek, and possibly 1 write * to do an append; it's possible to eliminate 2 of the semops if we do direct * buffer stuff (!); the lseek and the write can go if we get @@ -107,70 +108,70 @@ RelationPutHeapTuple(Relation relation, void RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple) { - Buffer buffer; - Page pageHeader; - BlockNumber lastblock; - OffsetNumber offnum; - unsigned int len; - ItemId itemId; - Item item; - - Assert(RelationIsValid(relation)); - Assert(HeapTupleIsValid(tuple)); - - /* - * XXX This does an lseek - VERY expensive - but at the moment it - * is the only way to accurately determine how many blocks are in - * a relation. A good optimization would be to get this to actually - * work properly. - */ - - lastblock = RelationGetNumberOfBlocks(relation); - - if (lastblock == 0) + Buffer buffer; + Page pageHeader; + BlockNumber lastblock; + OffsetNumber offnum; + unsigned int len; + ItemId itemId; + Item item; + + Assert(RelationIsValid(relation)); + Assert(HeapTupleIsValid(tuple)); + + /* + * XXX This does an lseek - VERY expensive - but at the moment it is + * the only way to accurately determine how many blocks are in a + * relation. A good optimization would be to get this to actually + * work properly. + */ + + lastblock = RelationGetNumberOfBlocks(relation); + + if (lastblock == 0) { - buffer = ReadBuffer(relation, lastblock); - pageHeader = (Page)BufferGetPage(buffer); - if (PageIsNew((PageHeader) pageHeader)) + buffer = ReadBuffer(relation, lastblock); + pageHeader = (Page) BufferGetPage(buffer); + if (PageIsNew((PageHeader) pageHeader)) { - buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW); - pageHeader = (Page)BufferGetPage(buffer); - PageInit(pageHeader, BufferGetPageSize(buffer), 0); + buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW); + pageHeader = (Page) BufferGetPage(buffer); + PageInit(pageHeader, BufferGetPageSize(buffer), 0); } } - else - buffer = ReadBuffer(relation, lastblock - 1); - - pageHeader = (Page)BufferGetPage(buffer); - len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */ - - /* - * Note that this is true if the above returned a bogus page, which - * it will do for a completely empty relation. - */ - - if (len > PageGetFreeSpace(pageHeader)) + else + buffer = ReadBuffer(relation, lastblock - 1); + + pageHeader = (Page) BufferGetPage(buffer); + len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */ + + /* + * Note that this is true if the above returned a bogus page, which it + * will do for a completely empty relation. + */ + + if (len > PageGetFreeSpace(pageHeader)) { - buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW); - pageHeader = (Page)BufferGetPage(buffer); - PageInit(pageHeader, BufferGetPageSize(buffer), 0); - - if (len > PageGetFreeSpace(pageHeader)) - elog(WARN, "Tuple is too big: size %d", len); + buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW); + pageHeader = (Page) BufferGetPage(buffer); + PageInit(pageHeader, BufferGetPageSize(buffer), 0); + + if (len > PageGetFreeSpace(pageHeader)) + elog(WARN, "Tuple is too big: size %d", len); } - - offnum = PageAddItem((Page)pageHeader, (Item)tuple, - tuple->t_len, InvalidOffsetNumber, LP_USED); - - itemId = PageGetItemId((Page)pageHeader, offnum); - item = PageGetItem((Page)pageHeader, itemId); - - lastblock = BufferGetBlockNumber(buffer); - - ItemPointerSet(&((HeapTuple)item)->t_ctid, lastblock, offnum); - - /* return an accurate tuple */ - ItemPointerSet(&tuple->t_ctid, lastblock, offnum); - - WriteBuffer(buffer); + + offnum = PageAddItem((Page) pageHeader, (Item) tuple, + tuple->t_len, InvalidOffsetNumber, LP_USED); + + itemId = PageGetItemId((Page) pageHeader, offnum); + item = PageGetItem((Page) pageHeader, itemId); + + lastblock = BufferGetBlockNumber(buffer); + + ItemPointerSet(&((HeapTuple) item)->t_ctid, lastblock, offnum); + + /* return an accurate tuple */ + ItemPointerSet(&tuple->t_ctid, lastblock, offnum); + + WriteBuffer(buffer); } diff --git a/src/backend/access/heap/stats.c b/src/backend/access/heap/stats.c index ae8273ac81c..aa16803779c 100644 --- a/src/backend/access/heap/stats.c +++ b/src/backend/access/heap/stats.c @@ -1,16 +1,16 @@ /*------------------------------------------------------------------------- * * stats.c-- - * heap access method debugging statistic collection routines + * heap access method debugging statistic collection routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/stats.c,v 1.11 1997/08/19 21:29:21 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/stats.c,v 1.12 1997/09/07 04:38:13 momjian Exp $ * * NOTES - * initam should be moved someplace else. + * initam should be moved someplace else. * *------------------------------------------------------------------------- */ @@ -23,322 +23,327 @@ #include <utils/mcxt.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static void InitHeapAccessStatistics(void); +static void InitHeapAccessStatistics(void); /* ---------------- - * InitHeapAccessStatistics + * InitHeapAccessStatistics * ---------------- */ HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL; - + static void -InitHeapAccessStatistics() +InitHeapAccessStatistics() { - MemoryContext oldContext; - HeapAccessStatistics stats; - - /* ---------------- - * make sure we don't initialize things twice - * ---------------- - */ - if (heap_access_stats != NULL) - return; - - /* ---------------- - * allocate statistics structure from the top memory context - * ---------------- - */ - oldContext = MemoryContextSwitchTo(TopMemoryContext); - - stats = (HeapAccessStatistics) - palloc(sizeof(HeapAccessStatisticsData)); - - /* ---------------- - * initialize fields to default values - * ---------------- - */ - stats->global_open = 0; - stats->global_openr = 0; - stats->global_close = 0; - stats->global_beginscan = 0; - stats->global_rescan = 0; - stats->global_endscan = 0; - stats->global_getnext = 0; - stats->global_fetch = 0; - stats->global_insert = 0; - stats->global_delete = 0; - stats->global_replace = 0; - stats->global_markpos = 0; - stats->global_restrpos = 0; - stats->global_BufferGetRelation = 0; - stats->global_RelationIdGetRelation = 0; - stats->global_RelationIdGetRelation_Buf = 0; - stats->global_getreldesc = 0; - stats->global_heapgettup = 0; - stats->global_RelationPutHeapTuple = 0; - stats->global_RelationPutLongHeapTuple = 0; - - stats->local_open = 0; - stats->local_openr = 0; - stats->local_close = 0; - stats->local_beginscan = 0; - stats->local_rescan = 0; - stats->local_endscan = 0; - stats->local_getnext = 0; - stats->local_fetch = 0; - stats->local_insert = 0; - stats->local_delete = 0; - stats->local_replace = 0; - stats->local_markpos = 0; - stats->local_restrpos = 0; - stats->local_BufferGetRelation = 0; - stats->local_RelationIdGetRelation = 0; - stats->local_RelationIdGetRelation_Buf = 0; - stats->local_getreldesc = 0; - stats->local_heapgettup = 0; - stats->local_RelationPutHeapTuple = 0; - stats->local_RelationPutLongHeapTuple = 0; - stats->local_RelationNameGetRelation = 0; - stats->global_RelationNameGetRelation = 0; - - /* ---------------- - * record init times - * ---------------- - */ - time(&stats->init_global_timestamp); - time(&stats->local_reset_timestamp); - time(&stats->last_request_timestamp); - - /* ---------------- - * return to old memory context - * ---------------- - */ - MemoryContextSwitchTo(oldContext); - - heap_access_stats = stats; + MemoryContext oldContext; + HeapAccessStatistics stats; + + /* ---------------- + * make sure we don't initialize things twice + * ---------------- + */ + if (heap_access_stats != NULL) + return; + + /* ---------------- + * allocate statistics structure from the top memory context + * ---------------- + */ + oldContext = MemoryContextSwitchTo(TopMemoryContext); + + stats = (HeapAccessStatistics) + palloc(sizeof(HeapAccessStatisticsData)); + + /* ---------------- + * initialize fields to default values + * ---------------- + */ + stats->global_open = 0; + stats->global_openr = 0; + stats->global_close = 0; + stats->global_beginscan = 0; + stats->global_rescan = 0; + stats->global_endscan = 0; + stats->global_getnext = 0; + stats->global_fetch = 0; + stats->global_insert = 0; + stats->global_delete = 0; + stats->global_replace = 0; + stats->global_markpos = 0; + stats->global_restrpos = 0; + stats->global_BufferGetRelation = 0; + stats->global_RelationIdGetRelation = 0; + stats->global_RelationIdGetRelation_Buf = 0; + stats->global_getreldesc = 0; + stats->global_heapgettup = 0; + stats->global_RelationPutHeapTuple = 0; + stats->global_RelationPutLongHeapTuple = 0; + + stats->local_open = 0; + stats->local_openr = 0; + stats->local_close = 0; + stats->local_beginscan = 0; + stats->local_rescan = 0; + stats->local_endscan = 0; + stats->local_getnext = 0; + stats->local_fetch = 0; + stats->local_insert = 0; + stats->local_delete = 0; + stats->local_replace = 0; + stats->local_markpos = 0; + stats->local_restrpos = 0; + stats->local_BufferGetRelation = 0; + stats->local_RelationIdGetRelation = 0; + stats->local_RelationIdGetRelation_Buf = 0; + stats->local_getreldesc = 0; + stats->local_heapgettup = 0; + stats->local_RelationPutHeapTuple = 0; + stats->local_RelationPutLongHeapTuple = 0; + stats->local_RelationNameGetRelation = 0; + stats->global_RelationNameGetRelation = 0; + + /* ---------------- + * record init times + * ---------------- + */ + time(&stats->init_global_timestamp); + time(&stats->local_reset_timestamp); + time(&stats->last_request_timestamp); + + /* ---------------- + * return to old memory context + * ---------------- + */ + MemoryContextSwitchTo(oldContext); + + heap_access_stats = stats; } #ifdef NOT_USED /* ---------------- - * ResetHeapAccessStatistics + * ResetHeapAccessStatistics * ---------------- */ void -ResetHeapAccessStatistics() +ResetHeapAccessStatistics() { - HeapAccessStatistics stats; - - /* ---------------- - * do nothing if stats aren't initialized - * ---------------- - */ - if (heap_access_stats == NULL) - return; - - stats = heap_access_stats; - - /* ---------------- - * reset local counts - * ---------------- - */ - stats->local_open = 0; - stats->local_openr = 0; - stats->local_close = 0; - stats->local_beginscan = 0; - stats->local_rescan = 0; - stats->local_endscan = 0; - stats->local_getnext = 0; - stats->local_fetch = 0; - stats->local_insert = 0; - stats->local_delete = 0; - stats->local_replace = 0; - stats->local_markpos = 0; - stats->local_restrpos = 0; - stats->local_BufferGetRelation = 0; - stats->local_RelationIdGetRelation = 0; - stats->local_RelationIdGetRelation_Buf = 0; - stats->local_getreldesc = 0; - stats->local_heapgettup = 0; - stats->local_RelationPutHeapTuple = 0; - stats->local_RelationPutLongHeapTuple = 0; - - /* ---------------- - * reset local timestamps - * ---------------- - */ - time(&stats->local_reset_timestamp); - time(&stats->last_request_timestamp); + HeapAccessStatistics stats; + + /* ---------------- + * do nothing if stats aren't initialized + * ---------------- + */ + if (heap_access_stats == NULL) + return; + + stats = heap_access_stats; + + /* ---------------- + * reset local counts + * ---------------- + */ + stats->local_open = 0; + stats->local_openr = 0; + stats->local_close = 0; + stats->local_beginscan = 0; + stats->local_rescan = 0; + stats->local_endscan = 0; + stats->local_getnext = 0; + stats->local_fetch = 0; + stats->local_insert = 0; + stats->local_delete = 0; + stats->local_replace = 0; + stats->local_markpos = 0; + stats->local_restrpos = 0; + stats->local_BufferGetRelation = 0; + stats->local_RelationIdGetRelation = 0; + stats->local_RelationIdGetRelation_Buf = 0; + stats->local_getreldesc = 0; + stats->local_heapgettup = 0; + stats->local_RelationPutHeapTuple = 0; + stats->local_RelationPutLongHeapTuple = 0; + + /* ---------------- + * reset local timestamps + * ---------------- + */ + time(&stats->local_reset_timestamp); + time(&stats->last_request_timestamp); } + #endif #ifdef NOT_USED /* ---------------- - * GetHeapAccessStatistics + * GetHeapAccessStatistics * ---------------- */ -HeapAccessStatistics GetHeapAccessStatistics() +HeapAccessStatistics +GetHeapAccessStatistics() { - HeapAccessStatistics stats; - - /* ---------------- - * return nothing if stats aren't initialized - * ---------------- - */ - if (heap_access_stats == NULL) - return NULL; - - /* ---------------- - * record the current request time - * ---------------- - */ - time(&heap_access_stats->last_request_timestamp); - - /* ---------------- - * allocate a copy of the stats and return it to the caller. - * ---------------- - */ - stats = (HeapAccessStatistics) - palloc(sizeof(HeapAccessStatisticsData)); - - memmove(stats, - heap_access_stats, - sizeof(HeapAccessStatisticsData)); - - return stats; + HeapAccessStatistics stats; + + /* ---------------- + * return nothing if stats aren't initialized + * ---------------- + */ + if (heap_access_stats == NULL) + return NULL; + + /* ---------------- + * record the current request time + * ---------------- + */ + time(&heap_access_stats->last_request_timestamp); + + /* ---------------- + * allocate a copy of the stats and return it to the caller. + * ---------------- + */ + stats = (HeapAccessStatistics) + palloc(sizeof(HeapAccessStatisticsData)); + + memmove(stats, + heap_access_stats, + sizeof(HeapAccessStatisticsData)); + + return stats; } + #endif #ifdef NOT_USED /* ---------------- - * PrintHeapAccessStatistics + * PrintHeapAccessStatistics * ---------------- */ void PrintHeapAccessStatistics(HeapAccessStatistics stats) { - /* ---------------- - * return nothing if stats aren't valid - * ---------------- - */ - if (stats == NULL) - return; - - printf("======== heap am statistics ========\n"); - printf("init_global_timestamp: %s", - ctime(&(stats->init_global_timestamp))); - - printf("local_reset_timestamp: %s", - ctime(&(stats->local_reset_timestamp))); - - printf("last_request_timestamp: %s", - ctime(&(stats->last_request_timestamp))); - - printf("local/global_open: %6d/%6d\n", - stats->local_open, stats->global_open); - - printf("local/global_openr: %6d/%6d\n", - stats->local_openr, stats->global_openr); - - printf("local/global_close: %6d/%6d\n", - stats->local_close, stats->global_close); - - printf("local/global_beginscan: %6d/%6d\n", - stats->local_beginscan, stats->global_beginscan); - - printf("local/global_rescan: %6d/%6d\n", - stats->local_rescan, stats->global_rescan); - - printf("local/global_endscan: %6d/%6d\n", - stats->local_endscan, stats->global_endscan); - - printf("local/global_getnext: %6d/%6d\n", - stats->local_getnext, stats->global_getnext); - - printf("local/global_fetch: %6d/%6d\n", - stats->local_fetch, stats->global_fetch); - - printf("local/global_insert: %6d/%6d\n", - stats->local_insert, stats->global_insert); - - printf("local/global_delete: %6d/%6d\n", - stats->local_delete, stats->global_delete); - - printf("local/global_replace: %6d/%6d\n", - stats->local_replace, stats->global_replace); - - printf("local/global_markpos: %6d/%6d\n", - stats->local_markpos, stats->global_markpos); - - printf("local/global_restrpos: %6d/%6d\n", - stats->local_restrpos, stats->global_restrpos); - - printf("================\n"); - - printf("local/global_BufferGetRelation: %6d/%6d\n", - stats->local_BufferGetRelation, - stats->global_BufferGetRelation); - - printf("local/global_RelationIdGetRelation: %6d/%6d\n", - stats->local_RelationIdGetRelation, - stats->global_RelationIdGetRelation); - - printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n", - stats->local_RelationIdGetRelation_Buf, - stats->global_RelationIdGetRelation_Buf); - - printf("local/global_getreldesc: %6d/%6d\n", - stats->local_getreldesc, stats->global_getreldesc); - - printf("local/global_heapgettup: %6d/%6d\n", - stats->local_heapgettup, stats->global_heapgettup); - - printf("local/global_RelationPutHeapTuple: %6d/%6d\n", - stats->local_RelationPutHeapTuple, - stats->global_RelationPutHeapTuple); - - printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n", - stats->local_RelationPutLongHeapTuple, - stats->global_RelationPutLongHeapTuple); - - printf("===================================\n"); - - printf("\n"); + /* ---------------- + * return nothing if stats aren't valid + * ---------------- + */ + if (stats == NULL) + return; + + printf("======== heap am statistics ========\n"); + printf("init_global_timestamp: %s", + ctime(&(stats->init_global_timestamp))); + + printf("local_reset_timestamp: %s", + ctime(&(stats->local_reset_timestamp))); + + printf("last_request_timestamp: %s", + ctime(&(stats->last_request_timestamp))); + + printf("local/global_open: %6d/%6d\n", + stats->local_open, stats->global_open); + + printf("local/global_openr: %6d/%6d\n", + stats->local_openr, stats->global_openr); + + printf("local/global_close: %6d/%6d\n", + stats->local_close, stats->global_close); + + printf("local/global_beginscan: %6d/%6d\n", + stats->local_beginscan, stats->global_beginscan); + + printf("local/global_rescan: %6d/%6d\n", + stats->local_rescan, stats->global_rescan); + + printf("local/global_endscan: %6d/%6d\n", + stats->local_endscan, stats->global_endscan); + + printf("local/global_getnext: %6d/%6d\n", + stats->local_getnext, stats->global_getnext); + + printf("local/global_fetch: %6d/%6d\n", + stats->local_fetch, stats->global_fetch); + + printf("local/global_insert: %6d/%6d\n", + stats->local_insert, stats->global_insert); + + printf("local/global_delete: %6d/%6d\n", + stats->local_delete, stats->global_delete); + + printf("local/global_replace: %6d/%6d\n", + stats->local_replace, stats->global_replace); + + printf("local/global_markpos: %6d/%6d\n", + stats->local_markpos, stats->global_markpos); + + printf("local/global_restrpos: %6d/%6d\n", + stats->local_restrpos, stats->global_restrpos); + + printf("================\n"); + + printf("local/global_BufferGetRelation: %6d/%6d\n", + stats->local_BufferGetRelation, + stats->global_BufferGetRelation); + + printf("local/global_RelationIdGetRelation: %6d/%6d\n", + stats->local_RelationIdGetRelation, + stats->global_RelationIdGetRelation); + + printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n", + stats->local_RelationIdGetRelation_Buf, + stats->global_RelationIdGetRelation_Buf); + + printf("local/global_getreldesc: %6d/%6d\n", + stats->local_getreldesc, stats->global_getreldesc); + + printf("local/global_heapgettup: %6d/%6d\n", + stats->local_heapgettup, stats->global_heapgettup); + + printf("local/global_RelationPutHeapTuple: %6d/%6d\n", + stats->local_RelationPutHeapTuple, + stats->global_RelationPutHeapTuple); + + printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n", + stats->local_RelationPutLongHeapTuple, + stats->global_RelationPutLongHeapTuple); + + printf("===================================\n"); + + printf("\n"); } + #endif #ifdef NOT_USED /* ---------------- - * PrintAndFreeHeapAccessStatistics + * PrintAndFreeHeapAccessStatistics * ---------------- */ void PrintAndFreeHeapAccessStatistics(HeapAccessStatistics stats) { - PrintHeapAccessStatistics(stats); - if (stats != NULL) - pfree(stats); + PrintHeapAccessStatistics(stats); + if (stats != NULL) + pfree(stats); } + #endif /* ---------------------------------------------------------------- - * access method initialization + * access method initialization * ---------------------------------------------------------------- */ /* ---------------- - * initam should someday be moved someplace else. + * initam should someday be moved someplace else. * ---------------- */ void initam(void) { - /* ---------------- - * initialize heap statistics. - * ---------------- - */ - InitHeapAccessStatistics(); + /* ---------------- + * initialize heap statistics. + * ---------------- + */ + InitHeapAccessStatistics(); } diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 52b7b1473bf..da7fc0dc09f 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * genam.c-- - * general index access method routines + * general index access method routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.7 1997/08/19 21:29:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.8 1997/09/07 04:38:17 momjian Exp $ * * NOTES - * many of the old access method routines have been turned into - * macros and moved to genam.h -cim 4/30/91 + * many of the old access method routines have been turned into + * macros and moved to genam.h -cim 4/30/91 * *------------------------------------------------------------------------- */ @@ -29,18 +29,18 @@ * previous, current, next. Note that the case of reverse scans works * identically. * - * State Result - * (1) + + - + 0 0 (if the next item pointer is invalid) - * (2) + X - (otherwise) - * (3) * 0 0 * 0 0 (no change) - * (4) + X 0 X 0 0 (shift) - * (5) * + X + X - (shift, add unknown) + * State Result + * (1) + + - + 0 0 (if the next item pointer is invalid) + * (2) + X - (otherwise) + * (3) * 0 0 * 0 0 (no change) + * (4) + X 0 X 0 0 (shift) + * (5) * + X + X - (shift, add unknown) * * All other states cannot occur. * * Note: *It would be possible to cache the status of the previous and - * next item pointer using the flags. + * next item pointer using the flags. * ---------------------------------------------------------------- */ @@ -51,220 +51,234 @@ #include <storage/bufmgr.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* ---------------------------------------------------------------- - * general access method routines + * general access method routines * - * All indexed access methods use an identical scan structure. - * We don't know how the various AMs do locking, however, so we don't - * do anything about that here. + * All indexed access methods use an identical scan structure. + * We don't know how the various AMs do locking, however, so we don't + * do anything about that here. * - * The intent is that an AM implementor will define a front-end routine - * that calls this one, to fill in the scan, and then does whatever kind - * of locking he wants. + * The intent is that an AM implementor will define a front-end routine + * that calls this one, to fill in the scan, and then does whatever kind + * of locking he wants. * ---------------------------------------------------------------- */ /* ---------------- - * RelationGetIndexScan -- Create and fill an IndexScanDesc. + * RelationGetIndexScan -- Create and fill an IndexScanDesc. * - * This routine creates an index scan structure and sets its contents - * up correctly. This routine calls AMrescan to set up the scan with - * the passed key. + * This routine creates an index scan structure and sets its contents + * up correctly. This routine calls AMrescan to set up the scan with + * the passed key. * - * Parameters: - * relation -- index relation for scan. - * scanFromEnd -- if true, begin scan at one of the index's - * endpoints. - * numberOfKeys -- count of scan keys (more than one won't - * necessarily do anything useful, yet). - * key -- the ScanKey for the starting position of the scan. + * Parameters: + * relation -- index relation for scan. + * scanFromEnd -- if true, begin scan at one of the index's + * endpoints. + * numberOfKeys -- count of scan keys (more than one won't + * necessarily do anything useful, yet). + * key -- the ScanKey for the starting position of the scan. * - * Returns: - * An initialized IndexScanDesc. + * Returns: + * An initialized IndexScanDesc. + * + * Side Effects: + * Bumps the ref count on the relation to keep it in the cache. * - * Side Effects: - * Bumps the ref count on the relation to keep it in the cache. - * * ---------------- */ IndexScanDesc RelationGetIndexScan(Relation relation, - bool scanFromEnd, - uint16 numberOfKeys, - ScanKey key) + bool scanFromEnd, + uint16 numberOfKeys, + ScanKey key) { - IndexScanDesc scan; - - if (! RelationIsValid(relation)) - elog(WARN, "RelationGetIndexScan: relation invalid"); - - scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData)); - - scan->relation = relation; - scan->opaque = NULL; - scan->numberOfKeys = numberOfKeys; - - ItemPointerSetInvalid(&scan->previousItemData); - ItemPointerSetInvalid(&scan->currentItemData); - ItemPointerSetInvalid(&scan->nextItemData); - ItemPointerSetInvalid(&scan->previousMarkData); - ItemPointerSetInvalid(&scan->currentMarkData); - ItemPointerSetInvalid(&scan->nextMarkData); + IndexScanDesc scan; + + if (!RelationIsValid(relation)) + elog(WARN, "RelationGetIndexScan: relation invalid"); + + scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData)); - if (numberOfKeys > 0) { - scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys); - } else { - scan->keyData = NULL; - } + scan->relation = relation; + scan->opaque = NULL; + scan->numberOfKeys = numberOfKeys; + + ItemPointerSetInvalid(&scan->previousItemData); + ItemPointerSetInvalid(&scan->currentItemData); + ItemPointerSetInvalid(&scan->nextItemData); + ItemPointerSetInvalid(&scan->previousMarkData); + ItemPointerSetInvalid(&scan->currentMarkData); + ItemPointerSetInvalid(&scan->nextMarkData); + + if (numberOfKeys > 0) + { + scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys); + } + else + { + scan->keyData = NULL; + } - index_rescan(scan, scanFromEnd, key); - - return (scan); + index_rescan(scan, scanFromEnd, key); + + return (scan); } #ifdef NOT_USED /* ---------------- - * IndexScanRestart -- Restart an index scan. + * IndexScanRestart -- Restart an index scan. * - * This routine isn't used by any existing access method. It's - * appropriate if relation level locks are what you want. + * This routine isn't used by any existing access method. It's + * appropriate if relation level locks are what you want. * - * Returns: - * None. + * Returns: + * None. * - * Side Effects: - * None. + * Side Effects: + * None. * ---------------- */ void IndexScanRestart(IndexScanDesc scan, - bool scanFromEnd, - ScanKey key) + bool scanFromEnd, + ScanKey key) { - if (! IndexScanIsValid(scan)) - elog(WARN, "IndexScanRestart: invalid scan"); - - ItemPointerSetInvalid(&scan->previousItemData); - ItemPointerSetInvalid(&scan->currentItemData); - ItemPointerSetInvalid(&scan->nextItemData); - - if (RelationGetNumberOfBlocks(scan->relation) == 0) - scan->flags = ScanUnmarked; - else if (scanFromEnd) - scan->flags = ScanUnmarked | ScanUncheckedPrevious; - else - scan->flags = ScanUnmarked | ScanUncheckedNext; - - scan->scanFromEnd = (bool) scanFromEnd; - - if (scan->numberOfKeys > 0) - memmove(scan->keyData, - key, - scan->numberOfKeys * sizeof(ScanKeyData)); + if (!IndexScanIsValid(scan)) + elog(WARN, "IndexScanRestart: invalid scan"); + + ItemPointerSetInvalid(&scan->previousItemData); + ItemPointerSetInvalid(&scan->currentItemData); + ItemPointerSetInvalid(&scan->nextItemData); + + if (RelationGetNumberOfBlocks(scan->relation) == 0) + scan->flags = ScanUnmarked; + else if (scanFromEnd) + scan->flags = ScanUnmarked | ScanUncheckedPrevious; + else + scan->flags = ScanUnmarked | ScanUncheckedNext; + + scan->scanFromEnd = (bool) scanFromEnd; + + if (scan->numberOfKeys > 0) + memmove(scan->keyData, + key, + scan->numberOfKeys * sizeof(ScanKeyData)); } + #endif #ifdef NOT_USED /* ---------------- - * IndexScanEnd -- End and index scan. + * IndexScanEnd -- End and index scan. * - * This routine is not used by any existing access method, but is - * suitable for use if you don't want to do sophisticated locking. + * This routine is not used by any existing access method, but is + * suitable for use if you don't want to do sophisticated locking. * - * Returns: - * None. + * Returns: + * None. * - * Side Effects: - * None. + * Side Effects: + * None. * ---------------- */ void IndexScanEnd(IndexScanDesc scan) { - if (! IndexScanIsValid(scan)) - elog(WARN, "IndexScanEnd: invalid scan"); - - pfree(scan); + if (!IndexScanIsValid(scan)) + elog(WARN, "IndexScanEnd: invalid scan"); + + pfree(scan); } + #endif /* ---------------- - * IndexScanMarkPosition -- Mark current position in a scan. + * IndexScanMarkPosition -- Mark current position in a scan. * - * This routine isn't used by any existing access method, but is the - * one that AM implementors should use, if they don't want to do any - * special locking. If relation-level locking is sufficient, this is - * the routine for you. + * This routine isn't used by any existing access method, but is the + * one that AM implementors should use, if they don't want to do any + * special locking. If relation-level locking is sufficient, this is + * the routine for you. * - * Returns: - * None. + * Returns: + * None. * - * Side Effects: - * None. + * Side Effects: + * None. * ---------------- */ void IndexScanMarkPosition(IndexScanDesc scan) { - RetrieveIndexResult result; - - if (scan->flags & ScanUncheckedPrevious) { - result = - index_getnext(scan, BackwardScanDirection); - - if (result != NULL) { - scan->previousItemData = result->index_iptr; - } else { - ItemPointerSetInvalid(&scan->previousItemData); + RetrieveIndexResult result; + + if (scan->flags & ScanUncheckedPrevious) + { + result = + index_getnext(scan, BackwardScanDirection); + + if (result != NULL) + { + scan->previousItemData = result->index_iptr; + } + else + { + ItemPointerSetInvalid(&scan->previousItemData); + } + } - - } else if (scan->flags & ScanUncheckedNext) { - result = (RetrieveIndexResult) - index_getnext(scan, ForwardScanDirection); - - if (result != NULL) { - scan->nextItemData = result->index_iptr; - } else { - ItemPointerSetInvalid(&scan->nextItemData); + else if (scan->flags & ScanUncheckedNext) + { + result = (RetrieveIndexResult) + index_getnext(scan, ForwardScanDirection); + + if (result != NULL) + { + scan->nextItemData = result->index_iptr; + } + else + { + ItemPointerSetInvalid(&scan->nextItemData); + } } - } - - scan->previousMarkData = scan->previousItemData; - scan->currentMarkData = scan->currentItemData; - scan->nextMarkData = scan->nextItemData; - - scan->flags = 0x0; /* XXX should have a symbolic name */ + + scan->previousMarkData = scan->previousItemData; + scan->currentMarkData = scan->currentItemData; + scan->nextMarkData = scan->nextItemData; + + scan->flags = 0x0; /* XXX should have a symbolic name */ } /* ---------------- - * IndexScanRestorePosition -- Restore position on a marked scan. + * IndexScanRestorePosition -- Restore position on a marked scan. * - * This routine isn't used by any existing access method, but is the - * one that AM implementors should use if they don't want to do any - * special locking. If relation-level locking is sufficient, then - * this is the one you want. + * This routine isn't used by any existing access method, but is the + * one that AM implementors should use if they don't want to do any + * special locking. If relation-level locking is sufficient, then + * this is the one you want. * - * Returns: - * None. + * Returns: + * None. * - * Side Effects: - * None. + * Side Effects: + * None. * ---------------- */ void IndexScanRestorePosition(IndexScanDesc scan) -{ - if (scan->flags & ScanUnmarked) - elog(WARN, "IndexScanRestorePosition: no mark to restore"); - - scan->previousItemData = scan->previousMarkData; - scan->currentItemData = scan->currentMarkData; - scan->nextItemData = scan->nextMarkData; - - scan->flags = 0x0; /* XXX should have a symbolic name */ +{ + if (scan->flags & ScanUnmarked) + elog(WARN, "IndexScanRestorePosition: no mark to restore"); + + scan->previousItemData = scan->previousMarkData; + scan->currentItemData = scan->currentMarkData; + scan->nextItemData = scan->nextMarkData; + + scan->flags = 0x0; /* XXX should have a symbolic name */ } diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 3068f7cceed..6841899fa39 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -1,80 +1,80 @@ /*------------------------------------------------------------------------- * * indexam.c-- - * general index access method routines + * general index access method routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.13 1997/08/26 23:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.14 1997/09/07 04:38:26 momjian Exp $ * * INTERFACE ROUTINES - * index_open - open an index relation by relationId - * index_openr - open a index relation by name - * index_close - close a index relation - * index_beginscan - start a scan of an index - * index_rescan - restart a scan of an index - * index_endscan - end a scan - * index_insert - insert an index tuple into a relation - * index_delete - delete an item from an index relation - * index_markpos - mark a scan position - * index_restrpos - restore a scan position - * index_getnext - get the next tuple from a scan - * ** index_fetch - retrieve tuple with tid + * index_open - open an index relation by relationId + * index_openr - open a index relation by name + * index_close - close a index relation + * index_beginscan - start a scan of an index + * index_rescan - restart a scan of an index + * index_endscan - end a scan + * index_insert - insert an index tuple into a relation + * index_delete - delete an item from an index relation + * index_markpos - mark a scan position + * index_restrpos - restore a scan position + * index_getnext - get the next tuple from a scan + * ** index_fetch - retrieve tuple with tid * ** index_replace - replace a tuple * ** index_getattr - get an attribute from an index tuple - * index_getprocid - get a support procedure id from the rel tuple - * - * IndexScanIsValid - check index scan + * index_getprocid - get a support procedure id from the rel tuple + * + * IndexScanIsValid - check index scan * * NOTES - * This file contains the index_ routines which used - * to be a scattered collection of stuff in access/genam. + * This file contains the index_ routines which used + * to be a scattered collection of stuff in access/genam. * - * The ** routines: index_fetch, index_replace, and index_getattr - * have not yet been implemented. They may not be needed. + * The ** routines: index_fetch, index_replace, and index_getattr + * have not yet been implemented. They may not be needed. * * old comments - * Scans are implemented as follows: + * Scans are implemented as follows: * - * `0' represents an invalid item pointer. - * `-' represents an unknown item pointer. - * `X' represents a known item pointers. - * `+' represents known or invalid item pointers. - * `*' represents any item pointers. + * `0' represents an invalid item pointer. + * `-' represents an unknown item pointer. + * `X' represents a known item pointers. + * `+' represents known or invalid item pointers. + * `*' represents any item pointers. * - * State is represented by a triple of these symbols in the order of - * previous, current, next. Note that the case of reverse scans works - * identically. + * State is represented by a triple of these symbols in the order of + * previous, current, next. Note that the case of reverse scans works + * identically. * - * State Result - * (1) + + - + 0 0 (if the next item pointer is invalid) - * (2) + X - (otherwise) - * (3) * 0 0 * 0 0 (no change) - * (4) + X 0 X 0 0 (shift) - * (5) * + X + X - (shift, add unknown) + * State Result + * (1) + + - + 0 0 (if the next item pointer is invalid) + * (2) + X - (otherwise) + * (3) * 0 0 * 0 0 (no change) + * (4) + X 0 X 0 0 (shift) + * (5) * + X + X - (shift, add unknown) * - * All other states cannot occur. + * All other states cannot occur. * - * Note: It would be possible to cache the status of the previous and - * next item pointer using the flags. + * Note: It would be possible to cache the status of the previous and + * next item pointer using the flags. * *------------------------------------------------------------------------- */ #include <postgres.h> - -#include <access/genam.h> + +#include <access/genam.h> #include <utils/relcache.h> #include <fmgr.h> #include <storage/lmgr.h> #include <access/heapam.h> /* ---------------- - * undefine macros we aren't going to use that would otherwise - * get in our way.. delete is defined in c.h and the am's are - * defined in heapam.h + * undefine macros we aren't going to use that would otherwise + * get in our way.. delete is defined in c.h and the am's are + * defined in heapam.h * ---------------- */ #undef delete @@ -88,314 +88,320 @@ #undef amgettuple /* ---------------------------------------------------------------- - * macros used in index_ routines + * macros used in index_ routines * ---------------------------------------------------------------- */ #define RELATION_CHECKS \ Assert(RelationIsValid(relation)); \ - Assert(PointerIsValid(relation->rd_am)) - + Assert(PointerIsValid(relation->rd_am)) + #define SCAN_CHECKS \ - Assert(IndexScanIsValid(scan)); \ - Assert(RelationIsValid(scan->relation)); \ - Assert(PointerIsValid(scan->relation->rd_am)) - + Assert(IndexScanIsValid(scan)); \ + Assert(RelationIsValid(scan->relation)); \ + Assert(PointerIsValid(scan->relation->rd_am)) + #define GET_REL_PROCEDURE(x,y) \ - procedure = relation->rd_am->y; \ - if (! RegProcedureIsValid(procedure)) \ - elog(WARN, "index_%s: invalid %s regproc", \ - CppAsString(x), CppAsString(y)) - + procedure = relation->rd_am->y; \ + if (! RegProcedureIsValid(procedure)) \ + elog(WARN, "index_%s: invalid %s regproc", \ + CppAsString(x), CppAsString(y)) + #define GET_SCAN_PROCEDURE(x,y) \ - procedure = scan->relation->rd_am->y; \ - if (! RegProcedureIsValid(procedure)) \ - elog(WARN, "index_%s: invalid %s regproc", \ - CppAsString(x), CppAsString(y)) - - + procedure = scan->relation->rd_am->y; \ + if (! RegProcedureIsValid(procedure)) \ + elog(WARN, "index_%s: invalid %s regproc", \ + CppAsString(x), CppAsString(y)) + + /* ---------------------------------------------------------------- - * index_ interface functions + * index_ interface functions * ---------------------------------------------------------------- */ /* ---------------- - * index_open - open an index relation by relationId + * index_open - open an index relation by relationId * - * presently the relcache routines do all the work we need - * to open/close index relations. + * presently the relcache routines do all the work we need + * to open/close index relations. * ---------------- */ Relation index_open(Oid relationId) { - return RelationIdGetRelation(relationId); + return RelationIdGetRelation(relationId); } /* ---------------- - * index_openr - open a index relation by name + * index_openr - open a index relation by name * - * presently the relcache routines do all the work we need - * to open/close index relations. + * presently the relcache routines do all the work we need + * to open/close index relations. * ---------------- */ Relation index_openr(char *relationName) { - return RelationNameGetRelation(relationName); + return RelationNameGetRelation(relationName); } /* ---------------- - * index_close - close a index relation + * index_close - close a index relation * - * presently the relcache routines do all the work we need - * to open/close index relations. + * presently the relcache routines do all the work we need + * to open/close index relations. * ---------------- */ void index_close(Relation relation) { - RelationClose(relation); + RelationClose(relation); } /* ---------------- - * index_insert - insert an index tuple into a relation + * index_insert - insert an index tuple into a relation * ---------------- */ InsertIndexResult index_insert(Relation relation, - Datum *datum, - char *nulls, - ItemPointer heap_t_ctid, - Relation heapRel) + Datum * datum, + char *nulls, + ItemPointer heap_t_ctid, + Relation heapRel) { - RegProcedure procedure; - InsertIndexResult specificResult; - - RELATION_CHECKS; - GET_REL_PROCEDURE(insert,aminsert); - - /* ---------------- - * have the am's insert proc do all the work. - * ---------------- - */ - specificResult = (InsertIndexResult) - fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL); - - /* ---------------- - * the insert proc is supposed to return a "specific result" and - * this routine has to return a "general result" so after we get - * something back from the insert proc, we allocate a - * "general result" and copy some crap between the two. - * - * As far as I'm concerned all this result shit is needlessly c - * omplicated and should be eliminated. -cim 1/19/91 - * - * mao concurs. regardless of how we feel here, however, it is - * important to free memory we don't intend to return to anyone. - * 2/28/91 - * - * this "general result" crap is now gone. -ay 3/6/95 - * ---------------- - */ - - return (specificResult); + RegProcedure procedure; + InsertIndexResult specificResult; + + RELATION_CHECKS; + GET_REL_PROCEDURE(insert, aminsert); + + /* ---------------- + * have the am's insert proc do all the work. + * ---------------- + */ + specificResult = (InsertIndexResult) + fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL); + + /* ---------------- + * the insert proc is supposed to return a "specific result" and + * this routine has to return a "general result" so after we get + * something back from the insert proc, we allocate a + * "general result" and copy some crap between the two. + * + * As far as I'm concerned all this result shit is needlessly c + * omplicated and should be eliminated. -cim 1/19/91 + * + * mao concurs. regardless of how we feel here, however, it is + * important to free memory we don't intend to return to anyone. + * 2/28/91 + * + * this "general result" crap is now gone. -ay 3/6/95 + * ---------------- + */ + + return (specificResult); } /* ---------------- - * index_delete - delete an item from an index relation + * index_delete - delete an item from an index relation * ---------------- */ void index_delete(Relation relation, ItemPointer indexItem) { - RegProcedure procedure; - - RELATION_CHECKS; - GET_REL_PROCEDURE(delete,amdelete); - - fmgr(procedure, relation, indexItem); + RegProcedure procedure; + + RELATION_CHECKS; + GET_REL_PROCEDURE(delete, amdelete); + + fmgr(procedure, relation, indexItem); } /* ---------------- - * index_beginscan - start a scan of an index + * index_beginscan - start a scan of an index * ---------------- */ IndexScanDesc index_beginscan(Relation relation, - bool scanFromEnd, - uint16 numberOfKeys, - ScanKey key) + bool scanFromEnd, + uint16 numberOfKeys, + ScanKey key) { - IndexScanDesc scandesc; - RegProcedure procedure; - - RELATION_CHECKS; - GET_REL_PROCEDURE(beginscan,ambeginscan); - - RelationSetRIntentLock(relation); - - scandesc = (IndexScanDesc) - fmgr(procedure, relation, scanFromEnd, numberOfKeys, key); - - return scandesc; + IndexScanDesc scandesc; + RegProcedure procedure; + + RELATION_CHECKS; + GET_REL_PROCEDURE(beginscan, ambeginscan); + + RelationSetRIntentLock(relation); + + scandesc = (IndexScanDesc) + fmgr(procedure, relation, scanFromEnd, numberOfKeys, key); + + return scandesc; } /* ---------------- - * index_rescan - restart a scan of an index + * index_rescan - restart a scan of an index * ---------------- */ void index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key) { - RegProcedure procedure; - - SCAN_CHECKS; - GET_SCAN_PROCEDURE(rescan,amrescan); - - fmgr(procedure, scan, scanFromEnd, key); + RegProcedure procedure; + + SCAN_CHECKS; + GET_SCAN_PROCEDURE(rescan, amrescan); + + fmgr(procedure, scan, scanFromEnd, key); } /* ---------------- - * index_endscan - end a scan + * index_endscan - end a scan * ---------------- */ void index_endscan(IndexScanDesc scan) { - RegProcedure procedure; - - SCAN_CHECKS; - GET_SCAN_PROCEDURE(endscan,amendscan); - - fmgr(procedure, scan); - - RelationUnsetRIntentLock(scan->relation); + RegProcedure procedure; + + SCAN_CHECKS; + GET_SCAN_PROCEDURE(endscan, amendscan); + + fmgr(procedure, scan); + + RelationUnsetRIntentLock(scan->relation); } #ifdef NOT_USED /* ---------------- - * index_markpos - mark a scan position + * index_markpos - mark a scan position * ---------------- */ void index_markpos(IndexScanDesc scan) { - RegProcedure procedure; - - SCAN_CHECKS; - GET_SCAN_PROCEDURE(markpos,ammarkpos); - - fmgr(procedure, scan); + RegProcedure procedure; + + SCAN_CHECKS; + GET_SCAN_PROCEDURE(markpos, ammarkpos); + + fmgr(procedure, scan); } + #endif #ifdef NOT_USED /* ---------------- - * index_restrpos - restore a scan position + * index_restrpos - restore a scan position * ---------------- */ void index_restrpos(IndexScanDesc scan) { - RegProcedure procedure; - - SCAN_CHECKS; - GET_SCAN_PROCEDURE(restrpos,amrestrpos); - - fmgr(procedure, scan); + RegProcedure procedure; + + SCAN_CHECKS; + GET_SCAN_PROCEDURE(restrpos, amrestrpos); + + fmgr(procedure, scan); } + #endif /* ---------------- - * index_getnext - get the next tuple from a scan + * index_getnext - get the next tuple from a scan * - * A RetrieveIndexResult is a index tuple/heap tuple pair + * A RetrieveIndexResult is a index tuple/heap tuple pair * ---------------- */ RetrieveIndexResult index_getnext(IndexScanDesc scan, - ScanDirection direction) + ScanDirection direction) { - RegProcedure procedure; - RetrieveIndexResult result; - - SCAN_CHECKS; - GET_SCAN_PROCEDURE(getnext,amgettuple); - - /* ---------------- - * have the am's gettuple proc do all the work. - * ---------------- - */ - result = (RetrieveIndexResult) - fmgr(procedure, scan, direction); - - return result; + RegProcedure procedure; + RetrieveIndexResult result; + + SCAN_CHECKS; + GET_SCAN_PROCEDURE(getnext, amgettuple); + + /* ---------------- + * have the am's gettuple proc do all the work. + * ---------------- + */ + result = (RetrieveIndexResult) + fmgr(procedure, scan, direction); + + return result; } /* ---------------- - * index_getprocid + * index_getprocid * - * Some indexed access methods may require support routines that are - * not in the operator class/operator model imposed by pg_am. These - * access methods may store the OIDs of registered procedures they - * need in pg_amproc. These registered procedure OIDs are ordered in - * a way that makes sense to the access method, and used only by the - * access method. The general index code doesn't know anything about - * the routines involved; it just builds an ordered list of them for - * each attribute on which an index is defined. + * Some indexed access methods may require support routines that are + * not in the operator class/operator model imposed by pg_am. These + * access methods may store the OIDs of registered procedures they + * need in pg_amproc. These registered procedure OIDs are ordered in + * a way that makes sense to the access method, and used only by the + * access method. The general index code doesn't know anything about + * the routines involved; it just builds an ordered list of them for + * each attribute on which an index is defined. * - * This routine returns the requested procedure OID for a particular - * indexed attribute. + * This routine returns the requested procedure OID for a particular + * indexed attribute. * ---------------- */ RegProcedure index_getprocid(Relation irel, - AttrNumber attnum, - uint16 procnum) + AttrNumber attnum, + uint16 procnum) { - RegProcedure *loc; - int natts; - - natts = irel->rd_rel->relnatts; - - loc = irel->rd_support; - - Assert(loc != NULL); - - return (loc[(natts * (procnum - 1)) + (attnum - 1)]); + RegProcedure *loc; + int natts; + + natts = irel->rd_rel->relnatts; + + loc = irel->rd_support; + + Assert(loc != NULL); + + return (loc[(natts * (procnum - 1)) + (attnum - 1)]); } Datum GetIndexValue(HeapTuple tuple, - TupleDesc hTupDesc, - int attOff, - AttrNumber attrNums[], - FuncIndexInfo *fInfo, - bool *attNull, - Buffer buffer) + TupleDesc hTupDesc, + int attOff, + AttrNumber attrNums[], + FuncIndexInfo * fInfo, + bool * attNull, + Buffer buffer) { - Datum returnVal; - bool isNull; - - if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid) { - int i; - Datum *attData = (Datum *)palloc(FIgetnArgs(fInfo)*sizeof(Datum)); - - for (i = 0; i < FIgetnArgs(fInfo); i++) { - attData[i] = (Datum) heap_getattr(tuple, - buffer, - attrNums[i], - hTupDesc, - attNull); + Datum returnVal; + bool isNull; + + if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid) + { + int i; + Datum *attData = (Datum *) palloc(FIgetnArgs(fInfo) * sizeof(Datum)); + + for (i = 0; i < FIgetnArgs(fInfo); i++) + { + attData[i] = (Datum) heap_getattr(tuple, + buffer, + attrNums[i], + hTupDesc, + attNull); + } + returnVal = (Datum) fmgr_array_args(FIgetProcOid(fInfo), + FIgetnArgs(fInfo), + (char **) attData, + &isNull); + pfree(attData); + *attNull = FALSE; + } + else + { + returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff], + hTupDesc, attNull); } - returnVal = (Datum)fmgr_array_args(FIgetProcOid(fInfo), - FIgetnArgs(fInfo), - (char **) attData, - &isNull); - pfree(attData); - *attNull = FALSE; - }else { - returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff], - hTupDesc, attNull); - } - return returnVal; + return returnVal; } diff --git a/src/backend/access/index/istrat.c b/src/backend/access/index/istrat.c index 5c143f0aa5f..35158c22170 100644 --- a/src/backend/access/index/istrat.c +++ b/src/backend/access/index/istrat.c @@ -1,689 +1,730 @@ /*------------------------------------------------------------------------- * * istrat.c-- - * index scan strategy manipulation code and index strategy manipulation - * operator code. + * index scan strategy manipulation code and index strategy manipulation + * operator code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.9 1997/08/22 16:48:14 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.10 1997/09/07 04:38:32 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <catalog/pg_proc.h> #include <catalog/pg_operator.h> #include <catalog/catname.h> #include <catalog/pg_index.h> #include <catalog/pg_amop.h> #include <catalog/pg_amproc.h> -#include <utils/memutils.h> /* could have been access/itup.h */ +#include <utils/memutils.h> /* could have been access/itup.h */ #include <access/heapam.h> #include <access/istrat.h> #include <fmgr.h> -#ifndef NO_ASSERT_CHECKING -static bool StrategyEvaluationIsValid(StrategyEvaluation evaluation); -static bool StrategyExpressionIsValid(StrategyExpression expression, - StrategyNumber maxStrategy); -static ScanKey StrategyMapGetScanKeyEntry(StrategyMap map, - StrategyNumber strategyNumber); -static bool StrategyOperatorIsValid(StrategyOperator operator, - StrategyNumber maxStrategy); -static bool StrategyTermIsValid(StrategyTerm term, - StrategyNumber maxStrategy); +#ifndef NO_ASSERT_CHECKING +static bool StrategyEvaluationIsValid(StrategyEvaluation evaluation); +static bool +StrategyExpressionIsValid(StrategyExpression expression, + StrategyNumber maxStrategy); +static ScanKey +StrategyMapGetScanKeyEntry(StrategyMap map, + StrategyNumber strategyNumber); +static bool +StrategyOperatorIsValid(StrategyOperator operator, + StrategyNumber maxStrategy); +static bool +StrategyTermIsValid(StrategyTerm term, + StrategyNumber maxStrategy); + #endif /* ---------------------------------------------------------------- - * misc strategy support routines + * misc strategy support routines * ---------------------------------------------------------------- */ - -/* - * StrategyNumberIsValid - * StrategyNumberIsInBounds - * StrategyMapIsValid - * StrategyTransformMapIsValid - * IndexStrategyIsValid + +/* + * StrategyNumberIsValid + * StrategyNumberIsInBounds + * StrategyMapIsValid + * StrategyTransformMapIsValid + * IndexStrategyIsValid * - * ... are now macros in istrat.h -cim 4/27/91 + * ... are now macros in istrat.h -cim 4/27/91 */ - + /* * StrategyMapGetScanKeyEntry -- - * Returns a scan key entry of a index strategy mapping member. + * Returns a scan key entry of a index strategy mapping member. * * Note: - * Assumes that the index strategy mapping is valid. - * Assumes that the index strategy number is valid. - * Bounds checking should be done outside this routine. + * Assumes that the index strategy mapping is valid. + * Assumes that the index strategy number is valid. + * Bounds checking should be done outside this routine. */ -static ScanKey +static ScanKey StrategyMapGetScanKeyEntry(StrategyMap map, - StrategyNumber strategyNumber) + StrategyNumber strategyNumber) { - Assert(StrategyMapIsValid(map)); - Assert(StrategyNumberIsValid(strategyNumber)); - return (&map->entry[strategyNumber - 1]); + Assert(StrategyMapIsValid(map)); + Assert(StrategyNumberIsValid(strategyNumber)); + return (&map->entry[strategyNumber - 1]); } /* * IndexStrategyGetStrategyMap -- - * Returns an index strategy mapping of an index strategy. + * Returns an index strategy mapping of an index strategy. * * Note: - * Assumes that the index strategy is valid. - * Assumes that the number of index strategies is valid. - * Bounds checking should be done outside this routine. + * Assumes that the index strategy is valid. + * Assumes that the number of index strategies is valid. + * Bounds checking should be done outside this routine. */ StrategyMap IndexStrategyGetStrategyMap(IndexStrategy indexStrategy, - StrategyNumber maxStrategyNum, - AttrNumber attrNum) + StrategyNumber maxStrategyNum, + AttrNumber attrNum) { - Assert(IndexStrategyIsValid(indexStrategy)); - Assert(StrategyNumberIsValid(maxStrategyNum)); - Assert(AttributeNumberIsValid(attrNum)); - - maxStrategyNum = AMStrategies(maxStrategyNum); /* XXX */ - return - &indexStrategy->strategyMapData[maxStrategyNum * (attrNum - 1)]; + Assert(IndexStrategyIsValid(indexStrategy)); + Assert(StrategyNumberIsValid(maxStrategyNum)); + Assert(AttributeNumberIsValid(attrNum)); + + maxStrategyNum = AMStrategies(maxStrategyNum); /* XXX */ + return + &indexStrategy->strategyMapData[maxStrategyNum * (attrNum - 1)]; } /* * AttributeNumberGetIndexStrategySize -- - * Computes the size of an index strategy. + * Computes the size of an index strategy. */ Size AttributeNumberGetIndexStrategySize(AttrNumber maxAttributeNumber, - StrategyNumber maxStrategyNumber) + StrategyNumber maxStrategyNumber) { - maxStrategyNumber = AMStrategies(maxStrategyNumber); /* XXX */ - return - maxAttributeNumber * maxStrategyNumber * sizeof (ScanKeyData); + maxStrategyNumber = AMStrategies(maxStrategyNumber); /* XXX */ + return + maxAttributeNumber * maxStrategyNumber * sizeof(ScanKeyData); } -#ifndef NO_ASSERT_CHECKING -/* +#ifndef NO_ASSERT_CHECKING +/* * StrategyTransformMapIsValid is now a macro in istrat.h -cim 4/27/91 */ /* ---------------- - * StrategyOperatorIsValid + * StrategyOperatorIsValid * ---------------- */ -static bool +static bool StrategyOperatorIsValid(StrategyOperator operator, - StrategyNumber maxStrategy) + StrategyNumber maxStrategy) { - return (bool) + return (bool) (PointerIsValid(operator) && StrategyNumberIsInBounds(operator->strategy, maxStrategy) && !(operator->flags & ~(SK_NEGATE | SK_COMMUTE))); } /* ---------------- - * StrategyTermIsValid + * StrategyTermIsValid * ---------------- */ -static bool +static bool StrategyTermIsValid(StrategyTerm term, - StrategyNumber maxStrategy) + StrategyNumber maxStrategy) { - Index index; - - if (! PointerIsValid(term) || term->degree == 0) - return false; - - for (index = 0; index < term->degree; index += 1) { - if (! StrategyOperatorIsValid(&term->operatorData[index], - maxStrategy)) { - - return false; + Index index; + + if (!PointerIsValid(term) || term->degree == 0) + return false; + + for (index = 0; index < term->degree; index += 1) + { + if (!StrategyOperatorIsValid(&term->operatorData[index], + maxStrategy)) + { + + return false; + } } - } - - return true; + + return true; } /* ---------------- - * StrategyExpressionIsValid + * StrategyExpressionIsValid * ---------------- */ -static bool +static bool StrategyExpressionIsValid(StrategyExpression expression, - StrategyNumber maxStrategy) + StrategyNumber maxStrategy) { - StrategyTerm *termP; - - if (!PointerIsValid(expression)) - return true; - - if (!StrategyTermIsValid(expression->term[0], maxStrategy)) - return false; - - termP = &expression->term[1]; - while (StrategyTermIsValid(*termP, maxStrategy)) - termP += 1; - - return (bool) - (! PointerIsValid(*termP)); + StrategyTerm *termP; + + if (!PointerIsValid(expression)) + return true; + + if (!StrategyTermIsValid(expression->term[0], maxStrategy)) + return false; + + termP = &expression->term[1]; + while (StrategyTermIsValid(*termP, maxStrategy)) + termP += 1; + + return (bool) + (!PointerIsValid(*termP)); } /* ---------------- - * StrategyEvaluationIsValid + * StrategyEvaluationIsValid * ---------------- */ -static bool +static bool StrategyEvaluationIsValid(StrategyEvaluation evaluation) { - Index index; - - if (! PointerIsValid(evaluation) || - ! StrategyNumberIsValid(evaluation->maxStrategy) || - ! StrategyTransformMapIsValid(evaluation->negateTransform) || - ! StrategyTransformMapIsValid(evaluation->commuteTransform) || - ! StrategyTransformMapIsValid(evaluation->negateCommuteTransform)) { - - return false; - } - - for (index = 0; index < evaluation->maxStrategy; index += 1) { - if (! StrategyExpressionIsValid(evaluation->expression[index], - evaluation->maxStrategy)) { - - return false; + Index index; + + if (!PointerIsValid(evaluation) || + !StrategyNumberIsValid(evaluation->maxStrategy) || + !StrategyTransformMapIsValid(evaluation->negateTransform) || + !StrategyTransformMapIsValid(evaluation->commuteTransform) || + !StrategyTransformMapIsValid(evaluation->negateCommuteTransform)) + { + + return false; } - } - return true; + + for (index = 0; index < evaluation->maxStrategy; index += 1) + { + if (!StrategyExpressionIsValid(evaluation->expression[index], + evaluation->maxStrategy)) + { + + return false; + } + } + return true; } + #endif /* ---------------- - * StrategyTermEvaluate + * StrategyTermEvaluate * ---------------- */ -static bool +static bool StrategyTermEvaluate(StrategyTerm term, - StrategyMap map, - Datum left, - Datum right) + StrategyMap map, + Datum left, + Datum right) { - Index index; - long tmpres = 0; - bool result = 0; - StrategyOperator operator; - ScanKey entry; - - for (index = 0, operator = &term->operatorData[0]; - index < term->degree; index += 1, operator += 1) { - - entry = &map->entry[operator->strategy - 1]; - - Assert(RegProcedureIsValid(entry->sk_procedure)); - - switch (operator->flags ^ entry->sk_flags) { - case 0x0: - tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, - left, right); - break; - - case SK_NEGATE: - tmpres = (long) !FMGR_PTR2(entry->sk_func, entry->sk_procedure, - left, right); - break; - - case SK_COMMUTE: - tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, - right, left); - break; - - case SK_NEGATE | SK_COMMUTE: - tmpres = (long) !FMGR_PTR2(entry->sk_func, entry->sk_procedure, - right, left); - break; - - default: - elog(FATAL, "StrategyTermEvaluate: impossible case %d", - operator->flags ^ entry->sk_flags); + Index index; + long tmpres = 0; + bool result = 0; + StrategyOperator operator; + ScanKey entry; + + for (index = 0, operator = &term->operatorData[0]; + index < term->degree; index += 1, operator += 1) + { + + entry = &map->entry[operator->strategy - 1]; + + Assert(RegProcedureIsValid(entry->sk_procedure)); + + switch (operator->flags ^ entry->sk_flags) + { + case 0x0: + tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, + left, right); + break; + + case SK_NEGATE: + tmpres = (long) !FMGR_PTR2(entry->sk_func, entry->sk_procedure, + left, right); + break; + + case SK_COMMUTE: + tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, + right, left); + break; + + case SK_NEGATE | SK_COMMUTE: + tmpres = (long) !FMGR_PTR2(entry->sk_func, entry->sk_procedure, + right, left); + break; + + default: + elog(FATAL, "StrategyTermEvaluate: impossible case %d", + operator->flags ^ entry->sk_flags); + } + + result = (bool) tmpres; + if (!result) + return result; } - - result = (bool) tmpres; - if (!result) - return result; - } - - return result; + + return result; } /* ---------------- - * RelationGetStrategy + * RelationGetStrategy * ---------------- */ StrategyNumber RelationGetStrategy(Relation relation, - AttrNumber attributeNumber, - StrategyEvaluation evaluation, - RegProcedure procedure) + AttrNumber attributeNumber, + StrategyEvaluation evaluation, + RegProcedure procedure) { - StrategyNumber strategy; - StrategyMap strategyMap; - ScanKey entry; - Index index; - int numattrs; - - Assert(RelationIsValid(relation)); - numattrs = RelationGetNumberOfAttributes(relation); - - Assert(relation->rd_rel->relkind == RELKIND_INDEX); /* XXX use accessor */ - Assert(AttributeNumberIsValid(attributeNumber)); - Assert( (attributeNumber >= 1) && (attributeNumber < 1 + numattrs)); - - Assert(StrategyEvaluationIsValid(evaluation)); - Assert(RegProcedureIsValid(procedure)); - - strategyMap = - IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), - evaluation->maxStrategy, - attributeNumber); - - /* get a strategy number for the procedure ignoring flags for now */ - for (index = 0; index < evaluation->maxStrategy; index += 1) { - if (strategyMap->entry[index].sk_procedure == procedure) { - break; + StrategyNumber strategy; + StrategyMap strategyMap; + ScanKey entry; + Index index; + int numattrs; + + Assert(RelationIsValid(relation)); + numattrs = RelationGetNumberOfAttributes(relation); + + Assert(relation->rd_rel->relkind == RELKIND_INDEX); /* XXX use accessor */ + Assert(AttributeNumberIsValid(attributeNumber)); + Assert((attributeNumber >= 1) && (attributeNumber < 1 + numattrs)); + + Assert(StrategyEvaluationIsValid(evaluation)); + Assert(RegProcedureIsValid(procedure)); + + strategyMap = + IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), + evaluation->maxStrategy, + attributeNumber); + + /* get a strategy number for the procedure ignoring flags for now */ + for (index = 0; index < evaluation->maxStrategy; index += 1) + { + if (strategyMap->entry[index].sk_procedure == procedure) + { + break; + } } - } - - if (index == evaluation->maxStrategy) - return InvalidStrategy; - - strategy = 1 + index; - entry = StrategyMapGetScanKeyEntry(strategyMap, strategy); - - Assert(!(entry->sk_flags & ~(SK_NEGATE | SK_COMMUTE))); - - switch (entry->sk_flags & (SK_NEGATE | SK_COMMUTE)) { - case 0x0: - return strategy; - - case SK_NEGATE: - strategy = evaluation->negateTransform->strategy[strategy - 1]; - break; - - case SK_COMMUTE: - strategy = evaluation->commuteTransform->strategy[strategy - 1]; - break; - - case SK_NEGATE | SK_COMMUTE: - strategy = evaluation->negateCommuteTransform->strategy[strategy - 1]; - break; - - default: - elog(FATAL, "RelationGetStrategy: impossible case %d", entry->sk_flags); - } - - - if (! StrategyNumberIsInBounds(strategy, evaluation->maxStrategy)) { - if (! StrategyNumberIsValid(strategy)) { - elog(WARN, "RelationGetStrategy: corrupted evaluation"); + + if (index == evaluation->maxStrategy) + return InvalidStrategy; + + strategy = 1 + index; + entry = StrategyMapGetScanKeyEntry(strategyMap, strategy); + + Assert(!(entry->sk_flags & ~(SK_NEGATE | SK_COMMUTE))); + + switch (entry->sk_flags & (SK_NEGATE | SK_COMMUTE)) + { + case 0x0: + return strategy; + + case SK_NEGATE: + strategy = evaluation->negateTransform->strategy[strategy - 1]; + break; + + case SK_COMMUTE: + strategy = evaluation->commuteTransform->strategy[strategy - 1]; + break; + + case SK_NEGATE | SK_COMMUTE: + strategy = evaluation->negateCommuteTransform->strategy[strategy - 1]; + break; + + default: + elog(FATAL, "RelationGetStrategy: impossible case %d", entry->sk_flags); } - } - - return strategy; + + + if (!StrategyNumberIsInBounds(strategy, evaluation->maxStrategy)) + { + if (!StrategyNumberIsValid(strategy)) + { + elog(WARN, "RelationGetStrategy: corrupted evaluation"); + } + } + + return strategy; } /* ---------------- - * RelationInvokeStrategy + * RelationInvokeStrategy * ---------------- */ -bool /* XXX someday, this may return Datum */ +bool /* XXX someday, this may return Datum */ RelationInvokeStrategy(Relation relation, - StrategyEvaluation evaluation, - AttrNumber attributeNumber, - StrategyNumber strategy, - Datum left, - Datum right) + StrategyEvaluation evaluation, + AttrNumber attributeNumber, + StrategyNumber strategy, + Datum left, + Datum right) { - StrategyNumber newStrategy; - StrategyMap strategyMap; - ScanKey entry; - StrategyTermData termData; - int numattrs; - - Assert(RelationIsValid(relation)); - Assert(relation->rd_rel->relkind == RELKIND_INDEX); /* XXX use accessor */ - numattrs = RelationGetNumberOfAttributes(relation); - - Assert(StrategyEvaluationIsValid(evaluation)); - Assert(AttributeNumberIsValid(attributeNumber)); - Assert( (attributeNumber >= 1) && (attributeNumber < 1 + numattrs)); - - Assert(StrategyNumberIsInBounds(strategy, evaluation->maxStrategy)); - - termData.degree = 1; - - strategyMap = - IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), - evaluation->maxStrategy, - attributeNumber); - - entry = StrategyMapGetScanKeyEntry(strategyMap, strategy); - - if (RegProcedureIsValid(entry->sk_procedure)) { - termData.operatorData[0].strategy = strategy; - termData.operatorData[0].flags = 0x0; - - return - StrategyTermEvaluate(&termData, strategyMap, left, right); - } - - - newStrategy = evaluation->negateTransform->strategy[strategy - 1]; - if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) { - - entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); - - if (RegProcedureIsValid(entry->sk_procedure)) { - termData.operatorData[0].strategy = newStrategy; - termData.operatorData[0].flags = SK_NEGATE; - - return - StrategyTermEvaluate(&termData, strategyMap, left, right); + StrategyNumber newStrategy; + StrategyMap strategyMap; + ScanKey entry; + StrategyTermData termData; + int numattrs; + + Assert(RelationIsValid(relation)); + Assert(relation->rd_rel->relkind == RELKIND_INDEX); /* XXX use accessor */ + numattrs = RelationGetNumberOfAttributes(relation); + + Assert(StrategyEvaluationIsValid(evaluation)); + Assert(AttributeNumberIsValid(attributeNumber)); + Assert((attributeNumber >= 1) && (attributeNumber < 1 + numattrs)); + + Assert(StrategyNumberIsInBounds(strategy, evaluation->maxStrategy)); + + termData.degree = 1; + + strategyMap = + IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), + evaluation->maxStrategy, + attributeNumber); + + entry = StrategyMapGetScanKeyEntry(strategyMap, strategy); + + if (RegProcedureIsValid(entry->sk_procedure)) + { + termData.operatorData[0].strategy = strategy; + termData.operatorData[0].flags = 0x0; + + return + StrategyTermEvaluate(&termData, strategyMap, left, right); } - } - - newStrategy = evaluation->commuteTransform->strategy[strategy - 1]; - if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) { - - entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); - - if (RegProcedureIsValid(entry->sk_procedure)) { - termData.operatorData[0].strategy = newStrategy; - termData.operatorData[0].flags = SK_COMMUTE; - - return - StrategyTermEvaluate(&termData, strategyMap, left, right); + + + newStrategy = evaluation->negateTransform->strategy[strategy - 1]; + if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) + { + + entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); + + if (RegProcedureIsValid(entry->sk_procedure)) + { + termData.operatorData[0].strategy = newStrategy; + termData.operatorData[0].flags = SK_NEGATE; + + return + StrategyTermEvaluate(&termData, strategyMap, left, right); + } } - } - - newStrategy = evaluation->negateCommuteTransform->strategy[strategy - 1]; - if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) { - - entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); - - if (RegProcedureIsValid(entry->sk_procedure)) { - termData.operatorData[0].strategy = newStrategy; - termData.operatorData[0].flags = SK_NEGATE | SK_COMMUTE; - - return - StrategyTermEvaluate(&termData, strategyMap, left, right); + + newStrategy = evaluation->commuteTransform->strategy[strategy - 1]; + if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) + { + + entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); + + if (RegProcedureIsValid(entry->sk_procedure)) + { + termData.operatorData[0].strategy = newStrategy; + termData.operatorData[0].flags = SK_COMMUTE; + + return + StrategyTermEvaluate(&termData, strategyMap, left, right); + } } - } - - if (PointerIsValid(evaluation->expression[strategy - 1])) { - StrategyTerm *termP; - - termP = &evaluation->expression[strategy - 1]->term[0]; - while (PointerIsValid(*termP)) { - Index index; - - for (index = 0; index < (*termP)->degree; index += 1) { - entry = StrategyMapGetScanKeyEntry(strategyMap, - (*termP)->operatorData[index].strategy); - - if (! RegProcedureIsValid(entry->sk_procedure)) { - break; + + newStrategy = evaluation->negateCommuteTransform->strategy[strategy - 1]; + if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) + { + + entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); + + if (RegProcedureIsValid(entry->sk_procedure)) + { + termData.operatorData[0].strategy = newStrategy; + termData.operatorData[0].flags = SK_NEGATE | SK_COMMUTE; + + return + StrategyTermEvaluate(&termData, strategyMap, left, right); } - } - - if (index == (*termP)->degree) { - return - StrategyTermEvaluate(*termP, strategyMap, left, right); - } - - termP += 1; } - } - - elog(WARN, "RelationInvokeStrategy: cannot evaluate strategy %d", - strategy); - /* not reached, just to make compiler happy */ - return FALSE; + if (PointerIsValid(evaluation->expression[strategy - 1])) + { + StrategyTerm *termP; + + termP = &evaluation->expression[strategy - 1]->term[0]; + while (PointerIsValid(*termP)) + { + Index index; + + for (index = 0; index < (*termP)->degree; index += 1) + { + entry = StrategyMapGetScanKeyEntry(strategyMap, + (*termP)->operatorData[index].strategy); + + if (!RegProcedureIsValid(entry->sk_procedure)) + { + break; + } + } + + if (index == (*termP)->degree) + { + return + StrategyTermEvaluate(*termP, strategyMap, left, right); + } + + termP += 1; + } + } + + elog(WARN, "RelationInvokeStrategy: cannot evaluate strategy %d", + strategy); + + /* not reached, just to make compiler happy */ + return FALSE; } /* ---------------- - * OperatorRelationFillScanKeyEntry + * OperatorRelationFillScanKeyEntry * ---------------- */ static void OperatorRelationFillScanKeyEntry(Relation operatorRelation, - Oid operatorObjectId, - ScanKey entry) + Oid operatorObjectId, + ScanKey entry) { - HeapScanDesc scan; - ScanKeyData scanKeyData; - HeapTuple tuple; - - ScanKeyEntryInitialize(&scanKeyData, 0, - ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(operatorObjectId)); - - scan = heap_beginscan(operatorRelation, false, NowTimeQual, - 1, &scanKeyData); - - tuple = heap_getnext(scan, false, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(WARN, "OperatorObjectIdFillScanKeyEntry: unknown operator %lu", - (uint32) operatorObjectId); - } - - entry->sk_flags = 0; - entry->sk_procedure = - ((OperatorTupleForm) GETSTRUCT(tuple))->oprcode; - fmgr_info(entry->sk_procedure, &entry->sk_func, &entry->sk_nargs); - - if (! RegProcedureIsValid(entry->sk_procedure)) { - elog(WARN, - "OperatorObjectIdFillScanKeyEntry: no procedure for operator %lu", - (uint32) operatorObjectId); - } - - heap_endscan(scan); + HeapScanDesc scan; + ScanKeyData scanKeyData; + HeapTuple tuple; + + ScanKeyEntryInitialize(&scanKeyData, 0, + ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(operatorObjectId)); + + scan = heap_beginscan(operatorRelation, false, NowTimeQual, + 1, &scanKeyData); + + tuple = heap_getnext(scan, false, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "OperatorObjectIdFillScanKeyEntry: unknown operator %lu", + (uint32) operatorObjectId); + } + + entry->sk_flags = 0; + entry->sk_procedure = + ((OperatorTupleForm) GETSTRUCT(tuple))->oprcode; + fmgr_info(entry->sk_procedure, &entry->sk_func, &entry->sk_nargs); + + if (!RegProcedureIsValid(entry->sk_procedure)) + { + elog(WARN, + "OperatorObjectIdFillScanKeyEntry: no procedure for operator %lu", + (uint32) operatorObjectId); + } + + heap_endscan(scan); } /* * IndexSupportInitialize -- - * Initializes an index strategy and associated support procedures. + * Initializes an index strategy and associated support procedures. */ void IndexSupportInitialize(IndexStrategy indexStrategy, - RegProcedure *indexSupport, - Oid indexObjectId, - Oid accessMethodObjectId, - StrategyNumber maxStrategyNumber, - StrategyNumber maxSupportNumber, - AttrNumber maxAttributeNumber) + RegProcedure * indexSupport, + Oid indexObjectId, + Oid accessMethodObjectId, + StrategyNumber maxStrategyNumber, + StrategyNumber maxSupportNumber, + AttrNumber maxAttributeNumber) { - Relation relation; - Relation operatorRelation; - HeapScanDesc scan; - HeapTuple tuple; - ScanKeyData entry[2]; - StrategyMap map; - AttrNumber attributeNumber; - int attributeIndex; - Oid operatorClassObjectId[ MaxIndexAttributeNumber ]; - - maxStrategyNumber = AMStrategies(maxStrategyNumber); - - ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_index_indexrelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(indexObjectId)); - - relation = heap_openr(IndexRelationName); - scan = heap_beginscan(relation, false, NowTimeQual, 1, entry); - tuple = heap_getnext(scan, 0, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) - elog(WARN, "IndexSupportInitialize: corrupted catalogs"); - - /* - * XXX note that the following assumes the INDEX tuple is well formed and - * that the key[] and class[] are 0 terminated. - */ - for (attributeIndex=0; attributeIndex<maxAttributeNumber; attributeIndex++) + Relation relation; + Relation operatorRelation; + HeapScanDesc scan; + HeapTuple tuple; + ScanKeyData entry[2]; + StrategyMap map; + AttrNumber attributeNumber; + int attributeIndex; + Oid operatorClassObjectId[MaxIndexAttributeNumber]; + + maxStrategyNumber = AMStrategies(maxStrategyNumber); + + ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_index_indexrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(indexObjectId)); + + relation = heap_openr(IndexRelationName); + scan = heap_beginscan(relation, false, NowTimeQual, 1, entry); + tuple = heap_getnext(scan, 0, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + elog(WARN, "IndexSupportInitialize: corrupted catalogs"); + + /* + * XXX note that the following assumes the INDEX tuple is well formed + * and that the key[] and class[] are 0 terminated. + */ + for (attributeIndex = 0; attributeIndex < maxAttributeNumber; attributeIndex++) { - IndexTupleForm iform; - - iform = (IndexTupleForm) GETSTRUCT(tuple); - - if (!OidIsValid(iform->indkey[attributeIndex])) { - if (attributeIndex == 0) { - elog(WARN, "IndexSupportInitialize: no pg_index tuple"); + IndexTupleForm iform; + + iform = (IndexTupleForm) GETSTRUCT(tuple); + + if (!OidIsValid(iform->indkey[attributeIndex])) + { + if (attributeIndex == 0) + { + elog(WARN, "IndexSupportInitialize: no pg_index tuple"); + } + break; } - break; - } - - operatorClassObjectId[attributeIndex] - = iform->indclass[attributeIndex]; + + operatorClassObjectId[attributeIndex] + = iform->indclass[attributeIndex]; } - - heap_endscan(scan); - heap_close(relation); - - /* if support routines exist for this access method, load them */ - if (maxSupportNumber > 0) { - - ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_amproc_amid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(accessMethodObjectId)); - - ScanKeyEntryInitialize(&entry[1], 0, Anum_pg_amproc_amopclaid, - ObjectIdEqualRegProcedure, 0); - -/* relation = heap_openr(Name_pg_amproc); */ - relation = heap_openr(AccessMethodProcedureRelationName); - - + + heap_endscan(scan); + heap_close(relation); + + /* if support routines exist for this access method, load them */ + if (maxSupportNumber > 0) + { + + ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_amproc_amid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(accessMethodObjectId)); + + ScanKeyEntryInitialize(&entry[1], 0, Anum_pg_amproc_amopclaid, + ObjectIdEqualRegProcedure, 0); + +/* relation = heap_openr(Name_pg_amproc); */ + relation = heap_openr(AccessMethodProcedureRelationName); + + + for (attributeNumber = maxAttributeNumber; attributeNumber > 0; + attributeNumber--) + { + + int16 support; + Form_pg_amproc form; + RegProcedure *loc; + + loc = &indexSupport[((attributeNumber - 1) * maxSupportNumber)]; + + for (support = maxSupportNumber; --support >= 0;) + { + loc[support] = InvalidOid; + } + + entry[1].sk_argument = + ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]); + + scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); + + while (tuple = heap_getnext(scan, 0, (Buffer *) NULL), + HeapTupleIsValid(tuple)) + { + + form = (Form_pg_amproc) GETSTRUCT(tuple); + loc[(form->amprocnum - 1)] = form->amproc; + } + + heap_endscan(scan); + } + heap_close(relation); + } + + ScanKeyEntryInitialize(&entry[0], 0, + Anum_pg_amop_amopid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(accessMethodObjectId)); + + ScanKeyEntryInitialize(&entry[1], 0, + Anum_pg_amop_amopclaid, + ObjectIdEqualRegProcedure, 0); + + relation = heap_openr(AccessMethodOperatorRelationName); + operatorRelation = heap_openr(OperatorRelationName); + for (attributeNumber = maxAttributeNumber; attributeNumber > 0; - attributeNumber--) { - - int16 support; - Form_pg_amproc form; - RegProcedure *loc; - - loc = &indexSupport[((attributeNumber - 1) * maxSupportNumber)]; - - for (support = maxSupportNumber; --support >= 0; ) { - loc[support] = InvalidOid; - } - - entry[1].sk_argument = - ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]); - - scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); - - while (tuple = heap_getnext(scan, 0, (Buffer *)NULL), - HeapTupleIsValid(tuple)) { - - form = (Form_pg_amproc) GETSTRUCT(tuple); - loc[(form->amprocnum - 1)] = form->amproc; - } - - heap_endscan(scan); + attributeNumber--) + { + + StrategyNumber strategy; + + entry[1].sk_argument = + ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]); + + map = IndexStrategyGetStrategyMap(indexStrategy, + maxStrategyNumber, + attributeNumber); + + for (strategy = 1; strategy <= maxStrategyNumber; strategy++) + ScanKeyEntrySetIllegal(StrategyMapGetScanKeyEntry(map, strategy)); + + scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); + + while (tuple = heap_getnext(scan, 0, (Buffer *) NULL), + HeapTupleIsValid(tuple)) + { + Form_pg_amop form; + + form = (Form_pg_amop) GETSTRUCT(tuple); + + OperatorRelationFillScanKeyEntry(operatorRelation, + form->amopopr, + StrategyMapGetScanKeyEntry(map, form->amopstrategy)); + } + + heap_endscan(scan); } + + heap_close(operatorRelation); heap_close(relation); - } - - ScanKeyEntryInitialize(&entry[0], 0, - Anum_pg_amop_amopid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(accessMethodObjectId)); - - ScanKeyEntryInitialize(&entry[1], 0, - Anum_pg_amop_amopclaid, - ObjectIdEqualRegProcedure, 0); - - relation = heap_openr(AccessMethodOperatorRelationName); - operatorRelation = heap_openr(OperatorRelationName); - - for (attributeNumber = maxAttributeNumber; attributeNumber > 0; - attributeNumber--) { - - StrategyNumber strategy; - - entry[1].sk_argument = - ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]); - - map = IndexStrategyGetStrategyMap(indexStrategy, - maxStrategyNumber, - attributeNumber); - - for (strategy = 1; strategy <= maxStrategyNumber; strategy++) - ScanKeyEntrySetIllegal(StrategyMapGetScanKeyEntry(map, strategy)); - - scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); - - while (tuple = heap_getnext(scan, 0, (Buffer *)NULL), - HeapTupleIsValid(tuple)) { - Form_pg_amop form; - - form = (Form_pg_amop) GETSTRUCT(tuple); - - OperatorRelationFillScanKeyEntry(operatorRelation, - form->amopopr, - StrategyMapGetScanKeyEntry(map, form->amopstrategy)); - } - - heap_endscan(scan); - } - - heap_close(operatorRelation); - heap_close(relation); } /* ---------------- - * IndexStrategyDisplay + * IndexStrategyDisplay * ---------------- */ #ifdef ISTRATDEBUG int IndexStrategyDisplay(IndexStrategy indexStrategy, - StrategyNumber numberOfStrategies, - int numberOfAttributes) + StrategyNumber numberOfStrategies, + int numberOfAttributes) { - StrategyMap strategyMap; - AttrNumber attributeNumber; - StrategyNumber strategyNumber; - - for (attributeNumber = 1; attributeNumber <= numberOfAttributes; - attributeNumber += 1) { - - strategyMap = IndexStrategyGetStrategyMap(indexStrategy, - numberOfStrategies, - attributeNumber); - - for (strategyNumber = 1; - strategyNumber <= AMStrategies(numberOfStrategies); - strategyNumber += 1) { - - printf(":att %d\t:str %d\t:opr 0x%x(%d)\n", - attributeNumber, strategyNumber, - strategyMap->entry[strategyNumber - 1].sk_procedure, - strategyMap->entry[strategyNumber - 1].sk_procedure); + StrategyMap strategyMap; + AttrNumber attributeNumber; + StrategyNumber strategyNumber; + + for (attributeNumber = 1; attributeNumber <= numberOfAttributes; + attributeNumber += 1) + { + + strategyMap = IndexStrategyGetStrategyMap(indexStrategy, + numberOfStrategies, + attributeNumber); + + for (strategyNumber = 1; + strategyNumber <= AMStrategies(numberOfStrategies); + strategyNumber += 1) + { + + printf(":att %d\t:str %d\t:opr 0x%x(%d)\n", + attributeNumber, strategyNumber, + strategyMap->entry[strategyNumber - 1].sk_procedure, + strategyMap->entry[strategyNumber - 1].sk_procedure); + } } - } } -#endif /* defined(ISTRATDEBUG) */ - +#endif /* defined(ISTRATDEBUG) */ diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c index f005509be07..0312bbb69d7 100644 --- a/src/backend/access/nbtree/nbtcompare.c +++ b/src/backend/access/nbtree/nbtcompare.c @@ -1,22 +1,22 @@ /*------------------------------------------------------------------------- * * nbtcompare.c-- - * Comparison functions for btree access method. + * Comparison functions for btree access method. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.10 1997/06/11 05:20:05 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.11 1997/09/07 04:38:39 momjian Exp $ * - * NOTES - * These functions are stored in pg_amproc. For each operator class - * defined on btrees, they compute + * NOTES + * These functions are stored in pg_amproc. For each operator class + * defined on btrees, they compute * - * compare(a, b): - * < 0 if a < b, - * = 0 if a == b, - * > 0 if a > b. + * compare(a, b): + * < 0 if a < b, + * = 0 if a == b, + * > 0 if a > b. *------------------------------------------------------------------------- */ @@ -30,168 +30,171 @@ int32 btint2cmp(int16 a, int16 b) { - return ((int32) (a - b)); + return ((int32) (a - b)); } int32 btint4cmp(int32 a, int32 b) { - return (a - b); + return (a - b); } int32 btint24cmp(int16 a, int32 b) { - return (((int32) a) - b); + return (((int32) a) - b); } int32 btint42cmp(int32 a, int16 b) { - return (a - ((int32) b)); + return (a - ((int32) b)); } int32 btfloat4cmp(float32 a, float32 b) { - if (*a > *b) - return (1); - else if (*a == *b) - return (0); - else - return (-1); + if (*a > *b) + return (1); + else if (*a == *b) + return (0); + else + return (-1); } int32 btfloat8cmp(float64 a, float64 b) { - if (*a > *b) - return (1); - else if (*a == *b) - return (0); - else - return (-1); + if (*a > *b) + return (1); + else if (*a == *b) + return (0); + else + return (-1); } int32 btoidcmp(Oid a, Oid b) { - if (a > b) - return (1); - else if (a == b) - return (0); - else - return (-1); + if (a > b) + return (1); + else if (a == b) + return (0); + else + return (-1); } int32 btabstimecmp(AbsoluteTime a, AbsoluteTime b) { - if (AbsoluteTimeIsBefore(a, b)) - return (-1); - else if (AbsoluteTimeIsBefore(b, a)) - return (1); - else - return (0); + if (AbsoluteTimeIsBefore(a, b)) + return (-1); + else if (AbsoluteTimeIsBefore(b, a)) + return (1); + else + return (0); } int32 btcharcmp(char a, char b) { - return ((int32) ((uint8)a - (uint8)b)); + return ((int32) ((uint8) a - (uint8) b)); } int32 btchar2cmp(uint16 a, uint16 b) { - return (strncmp((char *) &a, (char *) &b, 2)); + return (strncmp((char *) &a, (char *) &b, 2)); } int32 btchar4cmp(uint32 a, uint32 b) { - return (strncmp((char *) &a, (char *) &b, 4)); + return (strncmp((char *) &a, (char *) &b, 4)); } int32 btchar8cmp(char *a, char *b) { - return (strncmp(a, b, 8)); + return (strncmp(a, b, 8)); } int32 btchar16cmp(char *a, char *b) { - return (strncmp(a, b, 16)); + return (strncmp(a, b, 16)); } int32 -btnamecmp(NameData *a, NameData *b) +btnamecmp(NameData * a, NameData * b) { - return (strncmp(a->data, b->data, NAMEDATALEN)); + return (strncmp(a->data, b->data, NAMEDATALEN)); } int32 -bttextcmp(struct varlena *a, struct varlena *b) +bttextcmp(struct varlena * a, struct varlena * b) { - int res; - unsigned char *ap, *bp; + int res; + unsigned char *ap, + *bp; #ifdef USE_LOCALE - int la = VARSIZE(a) - VARHDRSZ; - int lb = VARSIZE(b) - VARHDRSZ; - - ap = (unsigned char *) palloc (la + 1); - bp = (unsigned char *) palloc (lb + 1); - - memcpy(ap, VARDATA(a), la); - *(ap + la) = '\0'; - memcpy(bp, VARDATA(b), lb); - *(bp + lb) = '\0'; - - res = strcoll (ap, bp); - - pfree (ap); - pfree (bp); + int la = VARSIZE(a) - VARHDRSZ; + int lb = VARSIZE(b) - VARHDRSZ; + + ap = (unsigned char *) palloc(la + 1); + bp = (unsigned char *) palloc(lb + 1); + + memcpy(ap, VARDATA(a), la); + *(ap + la) = '\0'; + memcpy(bp, VARDATA(b), lb); + *(bp + lb) = '\0'; + + res = strcoll(ap, bp); + + pfree(ap); + pfree(bp); #else - int len = VARSIZE(a); - - /* len is the length of the shorter of the two strings */ - if ( len > VARSIZE(b) ) - len = VARSIZE(b); - - len -= VARHDRSZ; - - ap = (unsigned char *) VARDATA(a); - bp = (unsigned char *) VARDATA(b); - - /* - * If the two strings differ in the first len bytes, or if they're - * the same in the first len bytes and they're both len bytes long, - * we're done. - */ - - res = 0; - if (len > 0) { - do { - res = (int) (*ap++ - *bp++); - len--; - } while (res == 0 && len != 0); - } + int len = VARSIZE(a); + + /* len is the length of the shorter of the two strings */ + if (len > VARSIZE(b)) + len = VARSIZE(b); + + len -= VARHDRSZ; + + ap = (unsigned char *) VARDATA(a); + bp = (unsigned char *) VARDATA(b); + + /* + * If the two strings differ in the first len bytes, or if they're the + * same in the first len bytes and they're both len bytes long, we're + * done. + */ + + res = 0; + if (len > 0) + { + do + { + res = (int) (*ap++ - *bp++); + len--; + } while (res == 0 && len != 0); + } #endif - - if (res != 0 || VARSIZE(a) == VARSIZE(b)) - return (res); - - /* - * The two strings are the same in the first len bytes, and they - * are of different lengths. - */ - - if (VARSIZE(a) < VARSIZE(b)) - return (-1); - else - return (1); + + if (res != 0 || VARSIZE(a) == VARSIZE(b)) + return (res); + + /* + * The two strings are the same in the first len bytes, and they are + * of different lengths. + */ + + if (VARSIZE(a) < VARSIZE(b)) + return (-1); + else + return (1); } diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index 4dfa6fd2558..4bafbc2ddbb 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * btinsert.c-- - * Item insertion in Lehman and Yao btrees for Postgres. + * Item insertion in Lehman and Yao btrees for Postgres. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.17 1997/08/20 14:53:15 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.18 1997/09/07 04:38:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,1386 +22,1437 @@ #include <fmgr.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif static InsertIndexResult _bt_insertonpg(Relation rel, Buffer buf, BTStack stack, int keysz, ScanKey scankey, BTItem btitem, BTItem afteritem); -static Buffer _bt_split(Relation rel, Buffer buf, OffsetNumber firstright); +static Buffer _bt_split(Relation rel, Buffer buf, OffsetNumber firstright); static OffsetNumber _bt_findsplitloc(Relation rel, Page page, OffsetNumber start, OffsetNumber maxoff, Size llimit); -static void _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf); +static void _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf); static OffsetNumber _bt_pgaddtup(Relation rel, Buffer buf, int keysz, ScanKey itup_scankey, Size itemsize, BTItem btitem, BTItem afteritem); -static bool _bt_goesonpg(Relation rel, Buffer buf, Size keysz, ScanKey scankey, BTItem afteritem); -static void _bt_updateitem(Relation rel, Size keysz, Buffer buf, BTItem oldItem, BTItem newItem); -static bool _bt_isequal (TupleDesc itupdesc, Page page, OffsetNumber offnum, int keysz, ScanKey scankey); +static bool _bt_goesonpg(Relation rel, Buffer buf, Size keysz, ScanKey scankey, BTItem afteritem); +static void _bt_updateitem(Relation rel, Size keysz, Buffer buf, BTItem oldItem, BTItem newItem); +static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum, int keysz, ScanKey scankey); /* - * _bt_doinsert() -- Handle insertion of a single btitem in the tree. + * _bt_doinsert() -- Handle insertion of a single btitem in the tree. * - * This routine is called by the public interface routines, btbuild - * and btinsert. By here, btitem is filled in, and has a unique - * (xid, seqno) pair. + * This routine is called by the public interface routines, btbuild + * and btinsert. By here, btitem is filled in, and has a unique + * (xid, seqno) pair. */ InsertIndexResult _bt_doinsert(Relation rel, BTItem btitem, bool index_is_unique, Relation heapRel) { - ScanKey itup_scankey; - IndexTuple itup; - BTStack stack; - Buffer buf; - BlockNumber blkno; - int natts = rel->rd_rel->relnatts; - InsertIndexResult res; - - itup = &(btitem->bti_itup); - - /* we need a scan key to do our search, so build one */ - itup_scankey = _bt_mkscankey(rel, itup); - - /* find the page containing this key */ - stack = _bt_search(rel, natts, itup_scankey, &buf); - - blkno = BufferGetBlockNumber(buf); - - /* trade in our read lock for a write lock */ - _bt_relbuf(rel, buf, BT_READ); - buf = _bt_getbuf(rel, blkno, BT_WRITE); - - /* - * 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. In this - * case, we need to move right in the tree. See Lehman and Yao for - * an excruciatingly precise description. - */ - - buf = _bt_moveright(rel, buf, natts, itup_scankey, BT_WRITE); - - /* if we're not allowing duplicates, make sure the key isn't */ - /* already in the node */ - if ( index_is_unique ) - { - OffsetNumber offset, maxoff; - Page page; + ScanKey itup_scankey; + IndexTuple itup; + BTStack stack; + Buffer buf; + BlockNumber blkno; + int natts = rel->rd_rel->relnatts; + InsertIndexResult res; - page = BufferGetPage(buf); - maxoff = PageGetMaxOffsetNumber (page); + itup = &(btitem->bti_itup); + + /* we need a scan key to do our search, so build one */ + itup_scankey = _bt_mkscankey(rel, itup); + + /* find the page containing this key */ + stack = _bt_search(rel, natts, itup_scankey, &buf); - offset = _bt_binsrch(rel, buf, natts, itup_scankey, BT_DESCENT); + blkno = BufferGetBlockNumber(buf); - /* make sure the offset we're given points to an actual */ - /* key on the page before trying to compare it */ - if ( !PageIsEmpty (page) && offset <= maxoff ) + /* trade in our read lock for a write lock */ + _bt_relbuf(rel, buf, BT_READ); + buf = _bt_getbuf(rel, blkno, BT_WRITE); + + /* + * 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. In this case, we + * need to move right in the tree. See Lehman and Yao for an + * excruciatingly precise description. + */ + + buf = _bt_moveright(rel, buf, natts, itup_scankey, BT_WRITE); + + /* if we're not allowing duplicates, make sure the key isn't */ + /* already in the node */ + if (index_is_unique) { - TupleDesc itupdesc; - BTItem btitem; - IndexTuple itup; - HeapTuple htup; - BTPageOpaque opaque; - Buffer nbuf; - BlockNumber blkno; - - itupdesc = RelationGetTupleDescriptor(rel); - nbuf = InvalidBuffer; - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - /* - * _bt_compare returns 0 for (1,NULL) and (1,NULL) - - * this's how we handling NULLs - and so we must not use - * _bt_compare in real comparison, but only for - * ordering/finding items on pages. - vadim 03/24/97 - - while ( !_bt_compare (rel, itupdesc, page, - natts, itup_scankey, offset) ) - */ - while ( _bt_isequal (itupdesc, page, offset, natts, itup_scankey) ) - { /* they're equal */ - btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offset)); - itup = &(btitem->bti_itup); - htup = heap_fetch (heapRel, SelfTimeQual, &(itup->t_tid), NULL); - if ( htup != (HeapTuple) NULL ) - { /* it is a duplicate */ - elog(WARN, "Cannot insert a duplicate key into a unique index."); - } - /* get next offnum */ - if ( offset < maxoff ) - { - offset = OffsetNumberNext(offset); - } - else - { /* move right ? */ - if ( P_RIGHTMOST (opaque) ) - break; - if ( !_bt_isequal (itupdesc, page, P_HIKEY, - natts, itup_scankey) ) - break; - /* - * min key of the right page is the same, - * ooh - so many dead duplicates... - */ - blkno = opaque->btpo_next; - if ( nbuf != InvalidBuffer ) - _bt_relbuf (rel, nbuf, BT_READ); - for (nbuf = InvalidBuffer; ; ) - { - nbuf = _bt_getbuf (rel, blkno, BT_READ); - page = BufferGetPage (nbuf); - maxoff = PageGetMaxOffsetNumber(page); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - offset = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - if ( ! PageIsEmpty (page) && offset <= maxoff ) - { /* Found some key */ - break; - } - else - { /* Empty or "pseudo"-empty page - get next */ - blkno = opaque->btpo_next; - _bt_relbuf (rel, nbuf, BT_READ); - nbuf = InvalidBuffer; - if ( blkno == P_NONE ) - break; + OffsetNumber offset, + maxoff; + Page page; + + page = BufferGetPage(buf); + maxoff = PageGetMaxOffsetNumber(page); + + offset = _bt_binsrch(rel, buf, natts, itup_scankey, BT_DESCENT); + + /* make sure the offset we're given points to an actual */ + /* key on the page before trying to compare it */ + if (!PageIsEmpty(page) && offset <= maxoff) + { + TupleDesc itupdesc; + BTItem btitem; + IndexTuple itup; + HeapTuple htup; + BTPageOpaque opaque; + Buffer nbuf; + BlockNumber blkno; + + itupdesc = RelationGetTupleDescriptor(rel); + nbuf = InvalidBuffer; + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + /* + * _bt_compare returns 0 for (1,NULL) and (1,NULL) - this's + * how we handling NULLs - and so we must not use _bt_compare + * in real comparison, but only for ordering/finding items on + * pages. - vadim 03/24/97 + * + * while ( !_bt_compare (rel, itupdesc, page, natts, + * itup_scankey, offset) ) + */ + while (_bt_isequal(itupdesc, page, offset, natts, itup_scankey)) + { /* they're equal */ + btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offset)); + itup = &(btitem->bti_itup); + htup = heap_fetch(heapRel, SelfTimeQual, &(itup->t_tid), NULL); + if (htup != (HeapTuple) NULL) + { /* it is a duplicate */ + elog(WARN, "Cannot insert a duplicate key into a unique index."); + } + /* get next offnum */ + if (offset < maxoff) + { + offset = OffsetNumberNext(offset); + } + else + { /* move right ? */ + if (P_RIGHTMOST(opaque)) + break; + if (!_bt_isequal(itupdesc, page, P_HIKEY, + natts, itup_scankey)) + break; + + /* + * min key of the right page is the same, ooh - so + * many dead duplicates... + */ + blkno = opaque->btpo_next; + if (nbuf != InvalidBuffer) + _bt_relbuf(rel, nbuf, BT_READ); + for (nbuf = InvalidBuffer;;) + { + nbuf = _bt_getbuf(rel, blkno, BT_READ); + page = BufferGetPage(nbuf); + maxoff = PageGetMaxOffsetNumber(page); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + offset = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + if (!PageIsEmpty(page) && offset <= maxoff) + { /* Found some key */ + break; + } + else + { /* Empty or "pseudo"-empty page - get next */ + blkno = opaque->btpo_next; + _bt_relbuf(rel, nbuf, BT_READ); + nbuf = InvalidBuffer; + if (blkno == P_NONE) + break; + } + } + if (nbuf == InvalidBuffer) + break; + } } - } - if ( nbuf == InvalidBuffer ) - break; - } - } - if ( nbuf != InvalidBuffer ) - _bt_relbuf(rel, nbuf, BT_READ); + if (nbuf != InvalidBuffer) + _bt_relbuf(rel, nbuf, BT_READ); + } } - } - - /* do the insertion */ - res = _bt_insertonpg(rel, buf, stack, natts, itup_scankey, - btitem, (BTItem) NULL); - - /* be tidy */ - _bt_freestack(stack); - _bt_freeskey(itup_scankey); - - return (res); + + /* do the insertion */ + res = _bt_insertonpg(rel, buf, stack, natts, itup_scankey, + btitem, (BTItem) NULL); + + /* be tidy */ + _bt_freestack(stack); + _bt_freeskey(itup_scankey); + + return (res); } /* - * _bt_insertonpg() -- Insert a tuple on a particular page in the index. + * _bt_insertonpg() -- Insert a tuple on a particular page in the index. * - * This recursive procedure does the following things: + * This recursive procedure does the following things: * - * + if necessary, splits the target page. - * + finds the right place to insert the tuple (taking into - * account any changes induced by a split). - * + inserts the tuple. - * + if the page was split, pops the parent stack, and finds the - * right place to insert the new child pointer (by walking - * right using information stored in the parent stack). - * + invoking itself with the appropriate tuple for the right - * child page on the parent. + * + if necessary, splits the target page. + * + finds the right place to insert the tuple (taking into + * account any changes induced by a split). + * + inserts the tuple. + * + if the page was split, pops the parent stack, and finds the + * right place to insert the new child pointer (by walking + * right using information stored in the parent stack). + * + invoking itself with the appropriate tuple for the right + * child page on the parent. * - * 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. * - * The locking interactions in this code are critical. You should - * grok Lehman and Yao's paper before making any changes. In addition, - * you need to understand how we disambiguate duplicate keys in this - * implementation, in order to be able to find our location using - * L&Y "move right" operations. Since we may insert duplicate user - * keys, and since these dups may propogate up the tree, we use the - * 'afteritem' parameter to position ourselves correctly for the - * insertion on internal pages. + * The locking interactions in this code are critical. You should + * grok Lehman and Yao's paper before making any changes. In addition, + * you need to understand how we disambiguate duplicate keys in this + * implementation, in order to be able to find our location using + * L&Y "move right" operations. Since we may insert duplicate user + * keys, and since these dups may propogate up the tree, we use the + * 'afteritem' parameter to position ourselves correctly for the + * insertion on internal pages. */ -static InsertIndexResult +static InsertIndexResult _bt_insertonpg(Relation rel, - Buffer buf, - BTStack stack, - int keysz, - ScanKey scankey, - BTItem btitem, - BTItem afteritem) + Buffer buf, + BTStack stack, + int keysz, + ScanKey scankey, + BTItem btitem, + BTItem afteritem) { - InsertIndexResult res; - Page page; - BTPageOpaque lpageop; - BlockNumber itup_blkno; - OffsetNumber itup_off; - OffsetNumber firstright = InvalidOffsetNumber; - int itemsz; - bool do_split = false; - bool keys_equal = false; - - page = BufferGetPage(buf); - lpageop = (BTPageOpaque) PageGetSpecialPointer(page); - - itemsz = IndexTupleDSize(btitem->bti_itup) - + (sizeof(BTItemData) - sizeof(IndexTupleData)); - - itemsz = DOUBLEALIGN(itemsz); /* be safe, PageAddItem will do this - but we need to be consistent */ - /* - * If we have to insert item on the leftmost page which is the first - * page in the chain of duplicates then: - * 1. if scankey == hikey (i.e. - new duplicate item) then - * insert it here; - * 2. if scankey < hikey then: - * 2.a if there is duplicate key(s) here - we force splitting; - * 2.b else - we may "eat" this page from duplicates chain. - */ - if ( lpageop->btpo_flags & BTP_CHAIN ) - { - OffsetNumber maxoff = PageGetMaxOffsetNumber (page); - ItemId hitemid; - BTItem hitem; - - Assert ( !P_RIGHTMOST(lpageop) ); - hitemid = PageGetItemId(page, P_HIKEY); - hitem = (BTItem) PageGetItem(page, hitemid); - if ( maxoff > P_HIKEY && - !_bt_itemcmp (rel, keysz, hitem, - (BTItem) PageGetItem(page, PageGetItemId(page, P_FIRSTKEY)), - BTEqualStrategyNumber) ) - elog (FATAL, "btree: bad key on the page in the chain of duplicates"); - - if ( !_bt_skeycmp (rel, keysz, scankey, page, hitemid, - BTEqualStrategyNumber) ) - { - if ( !P_LEFTMOST(lpageop) ) - elog (FATAL, "btree: attempt to insert bad key on the non-leftmost page in the chain of duplicates"); - if ( !_bt_skeycmp (rel, keysz, scankey, page, hitemid, - BTLessStrategyNumber) ) - elog (FATAL, "btree: attempt to insert higher key on the leftmost page in the chain of duplicates"); - if ( maxoff > P_HIKEY ) /* have duplicate(s) */ - { - firstright = P_FIRSTKEY; - do_split = true; - } - else /* "eat" page */ - { - Buffer pbuf; - Page ppage; - - itup_blkno = BufferGetBlockNumber(buf); - itup_off = PageAddItem(page, (Item) btitem, itemsz, - P_FIRSTKEY, LP_USED); - if ( itup_off == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add item"); - lpageop->btpo_flags &= ~BTP_CHAIN; - pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); - ppage = BufferGetPage(pbuf); - PageIndexTupleDelete(ppage, stack->bts_offset); - pfree(stack->bts_btitem); - stack->bts_btitem = _bt_formitem(&(btitem->bti_itup)); - ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), - itup_blkno, P_HIKEY); - _bt_wrtbuf(rel, buf); - res = _bt_insertonpg(rel, pbuf, stack->bts_parent, - keysz, scankey, stack->bts_btitem, - NULL); - ItemPointerSet(&(res->pointerData), itup_blkno, itup_off); - return (res); - } - } - else - { - keys_equal = true; - if ( PageGetFreeSpace(page) < itemsz ) - do_split = true; - } - } - else if ( PageGetFreeSpace(page) < itemsz ) - do_split = true; - else if ( PageGetFreeSpace(page) < 3*itemsz + 2*sizeof(ItemIdData) ) - { - OffsetNumber offnum = (P_RIGHTMOST(lpageop)) ? P_HIKEY : P_FIRSTKEY; - OffsetNumber maxoff = PageGetMaxOffsetNumber (page); - ItemId itid; - BTItem previtem, chkitem; - Size maxsize; - Size currsize; - - itid = PageGetItemId(page, offnum); - previtem = (BTItem) PageGetItem(page, itid); - maxsize = currsize = (ItemIdGetLength(itid) + sizeof(ItemIdData)); - for (offnum = OffsetNumberNext(offnum); - offnum <= maxoff; offnum = OffsetNumberNext(offnum) ) - { - itid = PageGetItemId(page, offnum); - chkitem = (BTItem) PageGetItem(page, itid); - if ( !_bt_itemcmp (rel, keysz, previtem, chkitem, - BTEqualStrategyNumber) ) - { - if ( currsize > maxsize ) - maxsize = currsize; - currsize = 0; - previtem = chkitem; - } - currsize += (ItemIdGetLength(itid) + sizeof(ItemIdData)); - } - if ( currsize > maxsize ) - maxsize = currsize; - maxsize += sizeof (PageHeaderData) + - DOUBLEALIGN (sizeof (BTPageOpaqueData)); - if ( maxsize >= PageGetPageSize (page) / 2 ) - do_split = true; - } - - if ( do_split ) - { - Buffer rbuf; - Page rpage; - BTItem ritem; - BlockNumber rbknum; - BTPageOpaque rpageop; - Buffer pbuf; - Page ppage; - BTPageOpaque ppageop; - BlockNumber bknum = BufferGetBlockNumber(buf); - BTItem lowLeftItem; - OffsetNumber maxoff; - bool shifted = false; - bool left_chained = ( lpageop->btpo_flags & BTP_CHAIN ) ? true : false; - - /* - * If we have to split leaf page in the chain of duplicates by - * new duplicate then we try to look at our right sibling first. - */ - if ( ( lpageop->btpo_flags & BTP_CHAIN ) && - ( lpageop->btpo_flags & BTP_LEAF ) && keys_equal ) - { - bool use_left = true; - - rbuf = _bt_getbuf(rel, lpageop->btpo_next, BT_WRITE); - rpage = BufferGetPage(rbuf); - rpageop = (BTPageOpaque) PageGetSpecialPointer(rpage); - if ( !P_RIGHTMOST (rpageop) ) /* non-rightmost page */ - { /* - * If we have the same hikey here then it's - * yet another page in chain. - */ - if ( _bt_skeycmp (rel, keysz, scankey, rpage, - PageGetItemId(rpage, P_HIKEY), - BTEqualStrategyNumber) ) - { - if ( !( rpageop->btpo_flags & BTP_CHAIN ) ) - elog (FATAL, "btree: lost page in the chain of duplicates"); - } - else if ( _bt_skeycmp (rel, keysz, scankey, rpage, - PageGetItemId(rpage, P_HIKEY), - BTGreaterStrategyNumber) ) - elog (FATAL, "btree: hikey is out of order"); - else if ( rpageop->btpo_flags & BTP_CHAIN ) - /* - * If hikey > scankey then it's last page in chain and - * BTP_CHAIN must be OFF - */ - elog (FATAL, "btree: lost last page in the chain of duplicates"); - - /* if there is room here then we use this page. */ - if ( PageGetFreeSpace (rpage) > itemsz ) - use_left = false; - } - else /* rightmost page */ - { - Assert ( !( rpageop->btpo_flags & BTP_CHAIN ) ); - /* if there is room here then we use this page. */ - if ( PageGetFreeSpace (rpage) > itemsz ) - use_left = false; - } - if ( !use_left ) /* insert on the right page */ - { - _bt_relbuf(rel, buf, BT_WRITE); - return ( _bt_insertonpg(rel, rbuf, stack, keysz, - scankey, btitem, afteritem) ); - } - _bt_relbuf(rel, rbuf, BT_WRITE); - } + InsertIndexResult res; + Page page; + BTPageOpaque lpageop; + BlockNumber itup_blkno; + OffsetNumber itup_off; + OffsetNumber firstright = InvalidOffsetNumber; + int itemsz; + bool do_split = false; + bool keys_equal = false; + + page = BufferGetPage(buf); + lpageop = (BTPageOpaque) PageGetSpecialPointer(page); + + itemsz = IndexTupleDSize(btitem->bti_itup) + + (sizeof(BTItemData) - sizeof(IndexTupleData)); + + itemsz = DOUBLEALIGN(itemsz); /* be safe, PageAddItem will do + * this but we need to be + * consistent */ + /* - * If after splitting un-chained page we'll got chain of pages - * with duplicates then we want to know - * 1. on which of two pages new btitem will go (current - * _bt_findsplitloc is quite bad); - * 2. what parent (if there's one) thinking about it - * (remember about deletions) + * If we have to insert item on the leftmost page which is the first + * page in the chain of duplicates then: 1. if scankey == hikey (i.e. + * - new duplicate item) then insert it here; 2. if scankey < hikey + * then: 2.a if there is duplicate key(s) here - we force splitting; + * 2.b else - we may "eat" this page from duplicates chain. */ - else if ( !( lpageop->btpo_flags & BTP_CHAIN ) ) + if (lpageop->btpo_flags & BTP_CHAIN) { - OffsetNumber start = ( P_RIGHTMOST(lpageop) ) ? P_HIKEY : P_FIRSTKEY; - Size llimit; - - maxoff = PageGetMaxOffsetNumber (page); - llimit = PageGetPageSize(page) - sizeof (PageHeaderData) - - DOUBLEALIGN (sizeof (BTPageOpaqueData)) - + sizeof(ItemIdData); - llimit /= 2; - firstright = _bt_findsplitloc(rel, page, start, maxoff, llimit); - - if ( _bt_itemcmp (rel, keysz, - (BTItem) PageGetItem(page, PageGetItemId(page, start)), - (BTItem) PageGetItem(page, PageGetItemId(page, firstright)), - BTEqualStrategyNumber) ) - { - if ( _bt_skeycmp (rel, keysz, scankey, page, - PageGetItemId(page, firstright), - BTLessStrategyNumber) ) - /* - * force moving current items to the new page: - * new item will go on the current page. - */ - firstright = start; - else - /* - * new btitem >= firstright, start item == firstright - - * new chain of duplicates: if this non-leftmost leaf - * page and parent item < start item then force moving - * all items to the new page - current page will be - * "empty" after it. - */ - { - if ( !P_LEFTMOST (lpageop) && - ( lpageop->btpo_flags & BTP_LEAF ) ) - { - ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), - bknum, P_HIKEY); - pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); - if ( _bt_itemcmp (rel, keysz, stack->bts_btitem, - (BTItem) PageGetItem(page, - PageGetItemId(page, start)), - BTLessStrategyNumber) ) - { - firstright = start; - shifted = true; - } - _bt_relbuf(rel, pbuf, BT_WRITE); - } - } - } /* else - no new chain if start item < firstright one */ - } - - /* split the buffer into left and right halves */ - rbuf = _bt_split(rel, buf, firstright); - - /* which new page (left half or right half) gets the tuple? */ - if (_bt_goesonpg(rel, buf, keysz, scankey, afteritem)) { - /* left page */ - itup_off = _bt_pgaddtup(rel, buf, keysz, scankey, - itemsz, btitem, afteritem); - itup_blkno = BufferGetBlockNumber(buf); - } else { - /* right page */ - itup_off = _bt_pgaddtup(rel, rbuf, keysz, scankey, - itemsz, btitem, afteritem); - itup_blkno = BufferGetBlockNumber(rbuf); + OffsetNumber maxoff = PageGetMaxOffsetNumber(page); + ItemId hitemid; + BTItem hitem; + + Assert(!P_RIGHTMOST(lpageop)); + hitemid = PageGetItemId(page, P_HIKEY); + hitem = (BTItem) PageGetItem(page, hitemid); + if (maxoff > P_HIKEY && + !_bt_itemcmp(rel, keysz, hitem, + (BTItem) PageGetItem(page, PageGetItemId(page, P_FIRSTKEY)), + BTEqualStrategyNumber)) + elog(FATAL, "btree: bad key on the page in the chain of duplicates"); + + if (!_bt_skeycmp(rel, keysz, scankey, page, hitemid, + BTEqualStrategyNumber)) + { + if (!P_LEFTMOST(lpageop)) + elog(FATAL, "btree: attempt to insert bad key on the non-leftmost page in the chain of duplicates"); + if (!_bt_skeycmp(rel, keysz, scankey, page, hitemid, + BTLessStrategyNumber)) + elog(FATAL, "btree: attempt to insert higher key on the leftmost page in the chain of duplicates"); + if (maxoff > P_HIKEY) /* have duplicate(s) */ + { + firstright = P_FIRSTKEY; + do_split = true; + } + else +/* "eat" page */ + { + Buffer pbuf; + Page ppage; + + itup_blkno = BufferGetBlockNumber(buf); + itup_off = PageAddItem(page, (Item) btitem, itemsz, + P_FIRSTKEY, LP_USED); + if (itup_off == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add item"); + lpageop->btpo_flags &= ~BTP_CHAIN; + pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); + ppage = BufferGetPage(pbuf); + PageIndexTupleDelete(ppage, stack->bts_offset); + pfree(stack->bts_btitem); + stack->bts_btitem = _bt_formitem(&(btitem->bti_itup)); + ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), + itup_blkno, P_HIKEY); + _bt_wrtbuf(rel, buf); + res = _bt_insertonpg(rel, pbuf, stack->bts_parent, + keysz, scankey, stack->bts_btitem, + NULL); + ItemPointerSet(&(res->pointerData), itup_blkno, itup_off); + return (res); + } + } + else + { + keys_equal = true; + if (PageGetFreeSpace(page) < itemsz) + do_split = true; + } } - - maxoff = PageGetMaxOffsetNumber (page); - if ( shifted ) - { - if ( maxoff > P_FIRSTKEY ) - elog (FATAL, "btree: shifted page is not empty"); - lowLeftItem = (BTItem) NULL; - } - else - { - if ( maxoff < P_FIRSTKEY ) - elog (FATAL, "btree: un-shifted page is empty"); - lowLeftItem = (BTItem) PageGetItem(page, - PageGetItemId(page, P_FIRSTKEY)); - if ( _bt_itemcmp (rel, keysz, lowLeftItem, - (BTItem) PageGetItem(page, PageGetItemId(page, P_HIKEY)), - BTEqualStrategyNumber) ) - lpageop->btpo_flags |= BTP_CHAIN; + else if (PageGetFreeSpace(page) < itemsz) + do_split = true; + else if (PageGetFreeSpace(page) < 3 * itemsz + 2 * sizeof(ItemIdData)) + { + OffsetNumber offnum = (P_RIGHTMOST(lpageop)) ? P_HIKEY : P_FIRSTKEY; + OffsetNumber maxoff = PageGetMaxOffsetNumber(page); + ItemId itid; + BTItem previtem, + chkitem; + Size maxsize; + Size currsize; + + itid = PageGetItemId(page, offnum); + previtem = (BTItem) PageGetItem(page, itid); + maxsize = currsize = (ItemIdGetLength(itid) + sizeof(ItemIdData)); + for (offnum = OffsetNumberNext(offnum); + offnum <= maxoff; offnum = OffsetNumberNext(offnum)) + { + itid = PageGetItemId(page, offnum); + chkitem = (BTItem) PageGetItem(page, itid); + if (!_bt_itemcmp(rel, keysz, previtem, chkitem, + BTEqualStrategyNumber)) + { + if (currsize > maxsize) + maxsize = currsize; + currsize = 0; + previtem = chkitem; + } + currsize += (ItemIdGetLength(itid) + sizeof(ItemIdData)); + } + if (currsize > maxsize) + maxsize = currsize; + maxsize += sizeof(PageHeaderData) + + DOUBLEALIGN(sizeof(BTPageOpaqueData)); + if (maxsize >= PageGetPageSize(page) / 2) + do_split = true; } - /* - * By here, - * - * + our target page has been split; - * + the original tuple has been inserted; - * + we have write locks on both the old (left half) and new - * (right half) buffers, after the split; and - * + we have the key we want to insert into the parent. - * - * Do the parent insertion. We need to hold onto the locks for - * the child pages until we locate the parent, but we can release - * them before doing the actual insertion (see Lehman and Yao for - * the reasoning). - */ - - if (stack == (BTStack) NULL) { - - /* create a new root node and release the split buffers */ - _bt_newroot(rel, buf, rbuf); - _bt_relbuf(rel, buf, BT_WRITE); - _bt_relbuf(rel, rbuf, BT_WRITE); - - } else { - ScanKey newskey; - InsertIndexResult newres; - BTItem new_item; - OffsetNumber upditem_offset = P_HIKEY; - bool do_update = false; - bool update_in_place = true; - bool parent_chained; - - /* form a index tuple that points at the new right page */ - rbknum = BufferGetBlockNumber(rbuf); - rpage = BufferGetPage(rbuf); - rpageop = (BTPageOpaque) PageGetSpecialPointer(rpage); - - /* - * By convention, the first entry (1) on every - * non-rightmost page is the high key for that page. In - * order to get the lowest key on the new right page, we - * actually look at its second (2) entry. - */ - - if (! P_RIGHTMOST(rpageop)) - { - ritem = (BTItem) PageGetItem(rpage, - PageGetItemId(rpage, P_FIRSTKEY)); - if ( _bt_itemcmp (rel, keysz, ritem, - (BTItem) PageGetItem(rpage, - PageGetItemId(rpage, P_HIKEY)), - BTEqualStrategyNumber) ) - rpageop->btpo_flags |= BTP_CHAIN; - } - else - ritem = (BTItem) PageGetItem(rpage, - PageGetItemId(rpage, P_HIKEY)); - - /* get a unique btitem for this key */ - new_item = _bt_formitem(&(ritem->bti_itup)); - - ItemPointerSet(&(new_item->bti_itup.t_tid), rbknum, P_HIKEY); - - /* - * Find the parent buffer and get the parent page. - * - * Oops - if we were moved right then we need to - * change stack item! We want to find parent pointing to - * where we are, right ? - vadim 05/27/97 - */ - ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), - bknum, P_HIKEY); - pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); - ppage = BufferGetPage(pbuf); - ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); - parent_chained = (( ppageop->btpo_flags & BTP_CHAIN )) ? true : false; - - if ( parent_chained && !left_chained ) - elog (FATAL, "nbtree: unexpected chained parent of unchained page"); - - /* - * If the key of new_item is < than the key of the item - * in the parent page pointing to the left page - * (stack->bts_btitem), we have to update the latter key; - * otherwise the keys on the parent page wouldn't be - * monotonically increasing after we inserted the new - * pointer to the right page (new_item). This only - * happens if our left page is the leftmost page and a - * new minimum key had been inserted before, which is not - * reflected in the parent page but didn't matter so - * far. If there are duplicate keys and this new minimum - * key spills over to our new right page, we get an - * inconsistency if we don't update the left key in the - * parent page. - * - * Also, new duplicates handling code require us to update - * parent item if some smaller items left on the left page - * (which is possible in splitting leftmost page) and - * current parent item == new_item. - vadim 05/27/97 - */ - if ( _bt_itemcmp (rel, keysz, stack->bts_btitem, new_item, - BTGreaterStrategyNumber) || - ( !shifted && - _bt_itemcmp(rel, keysz, stack->bts_btitem, - new_item, BTEqualStrategyNumber) && - _bt_itemcmp(rel, keysz, lowLeftItem, - new_item, BTLessStrategyNumber) ) ) - { - do_update = true; - /* - * figure out which key is leftmost (if the parent page - * is rightmost, too, it must be the root) + if (do_split) + { + Buffer rbuf; + Page rpage; + BTItem ritem; + BlockNumber rbknum; + BTPageOpaque rpageop; + Buffer pbuf; + Page ppage; + BTPageOpaque ppageop; + BlockNumber bknum = BufferGetBlockNumber(buf); + BTItem lowLeftItem; + OffsetNumber maxoff; + bool shifted = false; + bool left_chained = (lpageop->btpo_flags & BTP_CHAIN) ? true : false; + + /* + * If we have to split leaf page in the chain of duplicates by new + * duplicate then we try to look at our right sibling first. */ - if(P_RIGHTMOST(ppageop)) - upditem_offset = P_HIKEY; + if ((lpageop->btpo_flags & BTP_CHAIN) && + (lpageop->btpo_flags & BTP_LEAF) && keys_equal) + { + bool use_left = true; + + rbuf = _bt_getbuf(rel, lpageop->btpo_next, BT_WRITE); + rpage = BufferGetPage(rbuf); + rpageop = (BTPageOpaque) PageGetSpecialPointer(rpage); + if (!P_RIGHTMOST(rpageop)) /* non-rightmost page */ + { /* If we have the same hikey here then + * it's yet another page in chain. */ + if (_bt_skeycmp(rel, keysz, scankey, rpage, + PageGetItemId(rpage, P_HIKEY), + BTEqualStrategyNumber)) + { + if (!(rpageop->btpo_flags & BTP_CHAIN)) + elog(FATAL, "btree: lost page in the chain of duplicates"); + } + else if (_bt_skeycmp(rel, keysz, scankey, rpage, + PageGetItemId(rpage, P_HIKEY), + BTGreaterStrategyNumber)) + elog(FATAL, "btree: hikey is out of order"); + else if (rpageop->btpo_flags & BTP_CHAIN) + + /* + * If hikey > scankey then it's last page in chain and + * BTP_CHAIN must be OFF + */ + elog(FATAL, "btree: lost last page in the chain of duplicates"); + + /* if there is room here then we use this page. */ + if (PageGetFreeSpace(rpage) > itemsz) + use_left = false; + } + else +/* rightmost page */ + { + Assert(!(rpageop->btpo_flags & BTP_CHAIN)); + /* if there is room here then we use this page. */ + if (PageGetFreeSpace(rpage) > itemsz) + use_left = false; + } + if (!use_left) /* insert on the right page */ + { + _bt_relbuf(rel, buf, BT_WRITE); + return (_bt_insertonpg(rel, rbuf, stack, keysz, + scankey, btitem, afteritem)); + } + _bt_relbuf(rel, rbuf, BT_WRITE); + } + + /* + * If after splitting un-chained page we'll got chain of pages + * with duplicates then we want to know 1. on which of two pages + * new btitem will go (current _bt_findsplitloc is quite bad); 2. + * what parent (if there's one) thinking about it (remember about + * deletions) + */ + else if (!(lpageop->btpo_flags & BTP_CHAIN)) + { + OffsetNumber start = (P_RIGHTMOST(lpageop)) ? P_HIKEY : P_FIRSTKEY; + Size llimit; + + maxoff = PageGetMaxOffsetNumber(page); + llimit = PageGetPageSize(page) - sizeof(PageHeaderData) - + DOUBLEALIGN(sizeof(BTPageOpaqueData)) + + sizeof(ItemIdData); + llimit /= 2; + firstright = _bt_findsplitloc(rel, page, start, maxoff, llimit); + + if (_bt_itemcmp(rel, keysz, + (BTItem) PageGetItem(page, PageGetItemId(page, start)), + (BTItem) PageGetItem(page, PageGetItemId(page, firstright)), + BTEqualStrategyNumber)) + { + if (_bt_skeycmp(rel, keysz, scankey, page, + PageGetItemId(page, firstright), + BTLessStrategyNumber)) + + /* + * force moving current items to the new page: new + * item will go on the current page. + */ + firstright = start; + else + + /* + * new btitem >= firstright, start item == firstright + * - new chain of duplicates: if this non-leftmost + * leaf page and parent item < start item then force + * moving all items to the new page - current page + * will be "empty" after it. + */ + { + if (!P_LEFTMOST(lpageop) && + (lpageop->btpo_flags & BTP_LEAF)) + { + ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), + bknum, P_HIKEY); + pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); + if (_bt_itemcmp(rel, keysz, stack->bts_btitem, + (BTItem) PageGetItem(page, + PageGetItemId(page, start)), + BTLessStrategyNumber)) + { + firstright = start; + shifted = true; + } + _bt_relbuf(rel, pbuf, BT_WRITE); + } + } + } /* else - no new chain if start item < + * firstright one */ + } + + /* split the buffer into left and right halves */ + rbuf = _bt_split(rel, buf, firstright); + + /* which new page (left half or right half) gets the tuple? */ + if (_bt_goesonpg(rel, buf, keysz, scankey, afteritem)) + { + /* left page */ + itup_off = _bt_pgaddtup(rel, buf, keysz, scankey, + itemsz, btitem, afteritem); + itup_blkno = BufferGetBlockNumber(buf); + } else - upditem_offset = P_FIRSTKEY; - if ( !P_LEFTMOST(lpageop) || - stack->bts_offset != upditem_offset ) - elog (FATAL, "btree: items are out of order (leftmost %d, stack %u, update %u)", - P_LEFTMOST(lpageop), stack->bts_offset, upditem_offset); - } - - if ( do_update ) - { - if ( shifted ) - elog (FATAL, "btree: attempt to update parent for shifted page"); - /* - * Try to update in place. If out parent page is chained - * then we must forse insertion. + { + /* right page */ + itup_off = _bt_pgaddtup(rel, rbuf, keysz, scankey, + itemsz, btitem, afteritem); + itup_blkno = BufferGetBlockNumber(rbuf); + } + + maxoff = PageGetMaxOffsetNumber(page); + if (shifted) + { + if (maxoff > P_FIRSTKEY) + elog(FATAL, "btree: shifted page is not empty"); + lowLeftItem = (BTItem) NULL; + } + else + { + if (maxoff < P_FIRSTKEY) + elog(FATAL, "btree: un-shifted page is empty"); + lowLeftItem = (BTItem) PageGetItem(page, + PageGetItemId(page, P_FIRSTKEY)); + if (_bt_itemcmp(rel, keysz, lowLeftItem, + (BTItem) PageGetItem(page, PageGetItemId(page, P_HIKEY)), + BTEqualStrategyNumber)) + lpageop->btpo_flags |= BTP_CHAIN; + } + + /* + * By here, + * + * + our target page has been split; + the original tuple has been + * inserted; + we have write locks on both the old (left half) + * and new (right half) buffers, after the split; and + we have + * the key we want to insert into the parent. + * + * Do the parent insertion. We need to hold onto the locks for the + * child pages until we locate the parent, but we can release them + * before doing the actual insertion (see Lehman and Yao for the + * reasoning). */ - if ( !parent_chained && - DOUBLEALIGN (IndexTupleDSize (lowLeftItem->bti_itup)) == - DOUBLEALIGN (IndexTupleDSize (stack->bts_btitem->bti_itup)) ) - { - _bt_updateitem(rel, keysz, pbuf, - stack->bts_btitem, lowLeftItem); - _bt_relbuf(rel, buf, BT_WRITE); - _bt_relbuf(rel, rbuf, BT_WRITE); + + if (stack == (BTStack) NULL) + { + + /* create a new root node and release the split buffers */ + _bt_newroot(rel, buf, rbuf); + _bt_relbuf(rel, buf, BT_WRITE); + _bt_relbuf(rel, rbuf, BT_WRITE); + } else { - update_in_place = false; - PageIndexTupleDelete(ppage, upditem_offset); - - /* - * don't write anything out yet--we still have the write - * lock, and now we call another _bt_insertonpg to - * insert the correct key. - * First, make a new item, using the tuple data from - * lowLeftItem. Point it to the left child. - * Update it on the stack at the same time. - */ - pfree(stack->bts_btitem); - stack->bts_btitem = _bt_formitem(&(lowLeftItem->bti_itup)); - ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), - bknum, P_HIKEY); - - /* - * Unlock the children before doing this - * - * Mmm ... I foresee problems here. - vadim 06/10/97 - */ - _bt_relbuf(rel, buf, BT_WRITE); - _bt_relbuf(rel, rbuf, BT_WRITE); - - /* - * A regular _bt_binsrch should find the right place to - * put the new entry, since it should be lower than any - * other key on the page. - * Therefore set afteritem to NULL. - */ - newskey = _bt_mkscankey(rel, &(stack->bts_btitem->bti_itup)); - newres = _bt_insertonpg(rel, pbuf, stack->bts_parent, - keysz, newskey, stack->bts_btitem, - NULL); - - pfree(newres); - pfree(newskey); - - /* - * we have now lost our lock on the parent buffer, and - * need to get it back. - */ - pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); + ScanKey newskey; + InsertIndexResult newres; + BTItem new_item; + OffsetNumber upditem_offset = P_HIKEY; + bool do_update = false; + bool update_in_place = true; + bool parent_chained; + + /* form a index tuple that points at the new right page */ + rbknum = BufferGetBlockNumber(rbuf); + rpage = BufferGetPage(rbuf); + rpageop = (BTPageOpaque) PageGetSpecialPointer(rpage); + + /* + * By convention, the first entry (1) on every non-rightmost + * page is the high key for that page. In order to get the + * lowest key on the new right page, we actually look at its + * second (2) entry. + */ + + if (!P_RIGHTMOST(rpageop)) + { + ritem = (BTItem) PageGetItem(rpage, + PageGetItemId(rpage, P_FIRSTKEY)); + if (_bt_itemcmp(rel, keysz, ritem, + (BTItem) PageGetItem(rpage, + PageGetItemId(rpage, P_HIKEY)), + BTEqualStrategyNumber)) + rpageop->btpo_flags |= BTP_CHAIN; + } + else + ritem = (BTItem) PageGetItem(rpage, + PageGetItemId(rpage, P_HIKEY)); + + /* get a unique btitem for this key */ + new_item = _bt_formitem(&(ritem->bti_itup)); + + ItemPointerSet(&(new_item->bti_itup.t_tid), rbknum, P_HIKEY); + + /* + * Find the parent buffer and get the parent page. + * + * Oops - if we were moved right then we need to change stack + * item! We want to find parent pointing to where we are, + * right ? - vadim 05/27/97 + */ + ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), + bknum, P_HIKEY); + pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); + ppage = BufferGetPage(pbuf); + ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); + parent_chained = ((ppageop->btpo_flags & BTP_CHAIN)) ? true : false; + + if (parent_chained && !left_chained) + elog(FATAL, "nbtree: unexpected chained parent of unchained page"); + + /* + * If the key of new_item is < than the key of the item in the + * parent page pointing to the left page (stack->bts_btitem), + * we have to update the latter key; otherwise the keys on the + * parent page wouldn't be monotonically increasing after we + * inserted the new pointer to the right page (new_item). This + * only happens if our left page is the leftmost page and a + * new minimum key had been inserted before, which is not + * reflected in the parent page but didn't matter so far. If + * there are duplicate keys and this new minimum key spills + * over to our new right page, we get an inconsistency if we + * don't update the left key in the parent page. + * + * Also, new duplicates handling code require us to update parent + * item if some smaller items left on the left page (which is + * possible in splitting leftmost page) and current parent + * item == new_item. - vadim 05/27/97 + */ + if (_bt_itemcmp(rel, keysz, stack->bts_btitem, new_item, + BTGreaterStrategyNumber) || + (!shifted && + _bt_itemcmp(rel, keysz, stack->bts_btitem, + new_item, BTEqualStrategyNumber) && + _bt_itemcmp(rel, keysz, lowLeftItem, + new_item, BTLessStrategyNumber))) + { + do_update = true; + + /* + * figure out which key is leftmost (if the parent page is + * rightmost, too, it must be the root) + */ + if (P_RIGHTMOST(ppageop)) + upditem_offset = P_HIKEY; + else + upditem_offset = P_FIRSTKEY; + if (!P_LEFTMOST(lpageop) || + stack->bts_offset != upditem_offset) + elog(FATAL, "btree: items are out of order (leftmost %d, stack %u, update %u)", + P_LEFTMOST(lpageop), stack->bts_offset, upditem_offset); + } + + if (do_update) + { + if (shifted) + elog(FATAL, "btree: attempt to update parent for shifted page"); + + /* + * Try to update in place. If out parent page is chained + * then we must forse insertion. + */ + if (!parent_chained && + DOUBLEALIGN(IndexTupleDSize(lowLeftItem->bti_itup)) == + DOUBLEALIGN(IndexTupleDSize(stack->bts_btitem->bti_itup))) + { + _bt_updateitem(rel, keysz, pbuf, + stack->bts_btitem, lowLeftItem); + _bt_relbuf(rel, buf, BT_WRITE); + _bt_relbuf(rel, rbuf, BT_WRITE); + } + else + { + update_in_place = false; + PageIndexTupleDelete(ppage, upditem_offset); + + /* + * don't write anything out yet--we still have the + * write lock, and now we call another _bt_insertonpg + * to insert the correct key. First, make a new item, + * using the tuple data from lowLeftItem. Point it to + * the left child. Update it on the stack at the same + * time. + */ + pfree(stack->bts_btitem); + stack->bts_btitem = _bt_formitem(&(lowLeftItem->bti_itup)); + ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), + bknum, P_HIKEY); + + /* + * Unlock the children before doing this + * + * Mmm ... I foresee problems here. - vadim 06/10/97 + */ + _bt_relbuf(rel, buf, BT_WRITE); + _bt_relbuf(rel, rbuf, BT_WRITE); + + /* + * A regular _bt_binsrch should find the right place + * to put the new entry, since it should be lower than + * any other key on the page. Therefore set afteritem + * to NULL. + */ + newskey = _bt_mkscankey(rel, &(stack->bts_btitem->bti_itup)); + newres = _bt_insertonpg(rel, pbuf, stack->bts_parent, + keysz, newskey, stack->bts_btitem, + NULL); + + pfree(newres); + pfree(newskey); + + /* + * we have now lost our lock on the parent buffer, and + * need to get it back. + */ + pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); + } + } + else + { + _bt_relbuf(rel, buf, BT_WRITE); + _bt_relbuf(rel, rbuf, BT_WRITE); + } + + newskey = _bt_mkscankey(rel, &(new_item->bti_itup)); + + afteritem = stack->bts_btitem; + if (parent_chained && !update_in_place) + { + ppage = BufferGetPage(pbuf); + ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); + if (ppageop->btpo_flags & BTP_CHAIN) + elog(FATAL, "btree: unexpected BTP_CHAIN flag in parent after update"); + if (P_RIGHTMOST(ppageop)) + elog(FATAL, "btree: chained parent is RIGHTMOST after update"); + maxoff = PageGetMaxOffsetNumber(ppage); + if (maxoff != P_FIRSTKEY) + elog(FATAL, "btree: FIRSTKEY was unexpected in parent after update"); + if (_bt_skeycmp(rel, keysz, newskey, ppage, + PageGetItemId(ppage, P_FIRSTKEY), + BTLessEqualStrategyNumber)) + elog(FATAL, "btree: parent FIRSTKEY is >= duplicate key after update"); + if (!_bt_skeycmp(rel, keysz, newskey, ppage, + PageGetItemId(ppage, P_HIKEY), + BTEqualStrategyNumber)) + elog(FATAL, "btree: parent HIGHKEY is not equal duplicate key after update"); + afteritem = (BTItem) NULL; + } + else if (left_chained && !update_in_place) + { + ppage = BufferGetPage(pbuf); + ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); + if (!P_RIGHTMOST(ppageop) && + _bt_skeycmp(rel, keysz, newskey, ppage, + PageGetItemId(ppage, P_HIKEY), + BTGreaterStrategyNumber)) + afteritem = (BTItem) NULL; + } + if (afteritem == (BTItem) NULL) + { + rbuf = _bt_getbuf(rel, ppageop->btpo_next, BT_WRITE); + _bt_relbuf(rel, pbuf, BT_WRITE); + pbuf = rbuf; + } + + newres = _bt_insertonpg(rel, pbuf, stack->bts_parent, + keysz, newskey, new_item, + afteritem); + + /* be tidy */ + pfree(newres); + pfree(newskey); + pfree(new_item); } - } - else - { - _bt_relbuf(rel, buf, BT_WRITE); - _bt_relbuf(rel, rbuf, BT_WRITE); - } - - newskey = _bt_mkscankey(rel, &(new_item->bti_itup)); - - afteritem = stack->bts_btitem; - if ( parent_chained && !update_in_place ) - { - ppage = BufferGetPage(pbuf); - ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); - if ( ppageop->btpo_flags & BTP_CHAIN ) - elog (FATAL, "btree: unexpected BTP_CHAIN flag in parent after update"); - if ( P_RIGHTMOST (ppageop) ) - elog (FATAL, "btree: chained parent is RIGHTMOST after update"); - maxoff = PageGetMaxOffsetNumber (ppage); - if ( maxoff != P_FIRSTKEY ) - elog (FATAL, "btree: FIRSTKEY was unexpected in parent after update"); - if ( _bt_skeycmp (rel, keysz, newskey, ppage, - PageGetItemId(ppage, P_FIRSTKEY), - BTLessEqualStrategyNumber) ) - elog (FATAL, "btree: parent FIRSTKEY is >= duplicate key after update"); - if ( !_bt_skeycmp (rel, keysz, newskey, ppage, - PageGetItemId(ppage, P_HIKEY), - BTEqualStrategyNumber) ) - elog (FATAL, "btree: parent HIGHKEY is not equal duplicate key after update"); - afteritem = (BTItem) NULL; - } - else if ( left_chained && !update_in_place ) - { - ppage = BufferGetPage(pbuf); - ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); - if ( !P_RIGHTMOST (ppageop) && - _bt_skeycmp (rel, keysz, newskey, ppage, - PageGetItemId(ppage, P_HIKEY), - BTGreaterStrategyNumber) ) - afteritem = (BTItem) NULL; - } - if ( afteritem == (BTItem) NULL) - { - rbuf = _bt_getbuf(rel, ppageop->btpo_next, BT_WRITE); - _bt_relbuf(rel, pbuf, BT_WRITE); - pbuf = rbuf; - } - - newres = _bt_insertonpg(rel, pbuf, stack->bts_parent, - keysz, newskey, new_item, - afteritem); - - /* be tidy */ - pfree(newres); - pfree(newskey); - pfree(new_item); } - } else { - itup_off = _bt_pgaddtup(rel, buf, keysz, scankey, - itemsz, btitem, afteritem); - itup_blkno = BufferGetBlockNumber(buf); - - _bt_relbuf(rel, buf, BT_WRITE); - } - - /* by here, the new tuple is inserted */ - res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); - ItemPointerSet(&(res->pointerData), itup_blkno, itup_off); - - return (res); + else + { + itup_off = _bt_pgaddtup(rel, buf, keysz, scankey, + itemsz, btitem, afteritem); + itup_blkno = BufferGetBlockNumber(buf); + + _bt_relbuf(rel, buf, BT_WRITE); + } + + /* by here, the new tuple is inserted */ + res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); + ItemPointerSet(&(res->pointerData), itup_blkno, itup_off); + + return (res); } /* - * _bt_split() -- split a page in the btree. + * _bt_split() -- split a page in the btree. * - * On entry, buf is the page to split, and is write-locked and pinned. - * Returns the new right sibling of buf, pinned and write-locked. The - * pin and lock on buf are maintained. + * On entry, buf is the page to split, and is write-locked and pinned. + * Returns the new right sibling of buf, pinned and write-locked. The + * pin and lock on buf are maintained. */ -static Buffer +static Buffer _bt_split(Relation rel, Buffer buf, OffsetNumber firstright) { - Buffer rbuf; - Page origpage; - Page leftpage, rightpage; - BTPageOpaque ropaque, lopaque, oopaque; - Buffer sbuf; - Page spage; - BTPageOpaque sopaque; - Size itemsz; - ItemId itemid; - BTItem item; - OffsetNumber leftoff, rightoff; - OffsetNumber start; - OffsetNumber maxoff; - OffsetNumber i; - - rbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); - origpage = BufferGetPage(buf); - leftpage = PageGetTempPage(origpage, sizeof(BTPageOpaqueData)); - rightpage = BufferGetPage(rbuf); - - _bt_pageinit(rightpage, BufferGetPageSize(rbuf)); - _bt_pageinit(leftpage, BufferGetPageSize(buf)); - - /* init btree private data */ - oopaque = (BTPageOpaque) PageGetSpecialPointer(origpage); - lopaque = (BTPageOpaque) PageGetSpecialPointer(leftpage); - ropaque = (BTPageOpaque) PageGetSpecialPointer(rightpage); - - /* if we're splitting this page, it won't be the root when we're done */ - oopaque->btpo_flags &= ~BTP_ROOT; - oopaque->btpo_flags &= ~BTP_CHAIN; - lopaque->btpo_flags = ropaque->btpo_flags = oopaque->btpo_flags; - lopaque->btpo_prev = oopaque->btpo_prev; - ropaque->btpo_prev = BufferGetBlockNumber(buf); - lopaque->btpo_next = BufferGetBlockNumber(rbuf); - ropaque->btpo_next = oopaque->btpo_next; - - /* - * If the page we're splitting is not the rightmost page at its - * level in the tree, then the first (0) entry on the page is the - * high key for the page. We need to copy that to the right - * half. Otherwise (meaning the rightmost page case), we should - * treat the line pointers beginning at zero as user data. - * - * We leave a blank space at the start of the line table for the - * left page. We'll come back later and fill it in with the high - * key item we get from the right key. - */ - - leftoff = P_FIRSTKEY; - ropaque->btpo_next = oopaque->btpo_next; - if (! P_RIGHTMOST(oopaque)) { - /* splitting a non-rightmost page, start at the first data item */ - start = P_FIRSTKEY; - - itemid = PageGetItemId(origpage, P_HIKEY); - itemsz = ItemIdGetLength(itemid); - item = (BTItem) PageGetItem(origpage, itemid); - if ( PageAddItem(rightpage, (Item) item, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add hikey to the right sibling"); - rightoff = P_FIRSTKEY; - } else { - /* splitting a rightmost page, "high key" is the first data item */ - start = P_HIKEY; - - /* the new rightmost page will not have a high key */ - rightoff = P_HIKEY; - } - maxoff = PageGetMaxOffsetNumber(origpage); - if ( firstright == InvalidOffsetNumber ) - { - Size llimit = PageGetFreeSpace(leftpage) / 2; - firstright = _bt_findsplitloc(rel, origpage, start, maxoff, llimit); - } - - for (i = start; i <= maxoff; i = OffsetNumberNext(i)) { - itemid = PageGetItemId(origpage, i); + Buffer rbuf; + Page origpage; + Page leftpage, + rightpage; + BTPageOpaque ropaque, + lopaque, + oopaque; + Buffer sbuf; + Page spage; + BTPageOpaque sopaque; + Size itemsz; + ItemId itemid; + BTItem item; + OffsetNumber leftoff, + rightoff; + OffsetNumber start; + OffsetNumber maxoff; + OffsetNumber i; + + rbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); + origpage = BufferGetPage(buf); + leftpage = PageGetTempPage(origpage, sizeof(BTPageOpaqueData)); + rightpage = BufferGetPage(rbuf); + + _bt_pageinit(rightpage, BufferGetPageSize(rbuf)); + _bt_pageinit(leftpage, BufferGetPageSize(buf)); + + /* init btree private data */ + oopaque = (BTPageOpaque) PageGetSpecialPointer(origpage); + lopaque = (BTPageOpaque) PageGetSpecialPointer(leftpage); + ropaque = (BTPageOpaque) PageGetSpecialPointer(rightpage); + + /* if we're splitting this page, it won't be the root when we're done */ + oopaque->btpo_flags &= ~BTP_ROOT; + oopaque->btpo_flags &= ~BTP_CHAIN; + lopaque->btpo_flags = ropaque->btpo_flags = oopaque->btpo_flags; + lopaque->btpo_prev = oopaque->btpo_prev; + ropaque->btpo_prev = BufferGetBlockNumber(buf); + lopaque->btpo_next = BufferGetBlockNumber(rbuf); + ropaque->btpo_next = oopaque->btpo_next; + + /* + * If the page we're splitting is not the rightmost page at its level + * in the tree, then the first (0) entry on the page is the high key + * for the page. We need to copy that to the right half. Otherwise + * (meaning the rightmost page case), we should treat the line + * pointers beginning at zero as user data. + * + * We leave a blank space at the start of the line table for the left + * page. We'll come back later and fill it in with the high key item + * we get from the right key. + */ + + leftoff = P_FIRSTKEY; + ropaque->btpo_next = oopaque->btpo_next; + if (!P_RIGHTMOST(oopaque)) + { + /* splitting a non-rightmost page, start at the first data item */ + start = P_FIRSTKEY; + + itemid = PageGetItemId(origpage, P_HIKEY); + itemsz = ItemIdGetLength(itemid); + item = (BTItem) PageGetItem(origpage, itemid); + if (PageAddItem(rightpage, (Item) item, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add hikey to the right sibling"); + rightoff = P_FIRSTKEY; + } + else + { + /* splitting a rightmost page, "high key" is the first data item */ + start = P_HIKEY; + + /* the new rightmost page will not have a high key */ + rightoff = P_HIKEY; + } + maxoff = PageGetMaxOffsetNumber(origpage); + if (firstright == InvalidOffsetNumber) + { + Size llimit = PageGetFreeSpace(leftpage) / 2; + + firstright = _bt_findsplitloc(rel, origpage, start, maxoff, llimit); + } + + for (i = start; i <= maxoff; i = OffsetNumberNext(i)) + { + itemid = PageGetItemId(origpage, i); + itemsz = ItemIdGetLength(itemid); + item = (BTItem) PageGetItem(origpage, itemid); + + /* decide which page to put it on */ + if (i < firstright) + { + if (PageAddItem(leftpage, (Item) item, itemsz, leftoff, + LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add item to the left sibling"); + leftoff = OffsetNumberNext(leftoff); + } + else + { + if (PageAddItem(rightpage, (Item) item, itemsz, rightoff, + LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add item to the right sibling"); + rightoff = OffsetNumberNext(rightoff); + } + } + + /* + * Okay, page has been split, high key on right page is correct. Now + * set the high key on the left page to be the min key on the right + * page. + */ + + if (P_RIGHTMOST(ropaque)) + { + itemid = PageGetItemId(rightpage, P_HIKEY); + } + else + { + itemid = PageGetItemId(rightpage, P_FIRSTKEY); + } itemsz = ItemIdGetLength(itemid); - item = (BTItem) PageGetItem(origpage, itemid); - - /* decide which page to put it on */ - if (i < firstright) { - if ( PageAddItem(leftpage, (Item) item, itemsz, leftoff, - LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add item to the left sibling"); - leftoff = OffsetNumberNext(leftoff); - } else { - if ( PageAddItem(rightpage, (Item) item, itemsz, rightoff, - LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add item to the right sibling"); - rightoff = OffsetNumberNext(rightoff); + item = (BTItem) PageGetItem(rightpage, itemid); + + /* + * We left a hole for the high key on the left page; fill it. The + * modal crap is to tell the page manager to put the new item on the + * page and not screw around with anything else. Whoever designed + * this interface has presumably crawled back into the dung heap they + * came from. No one here will admit to it. + */ + + PageManagerModeSet(OverwritePageManagerMode); + if (PageAddItem(leftpage, (Item) item, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add hikey to the left sibling"); + PageManagerModeSet(ShufflePageManagerMode); + + /* + * By here, the original data page has been split into two new halves, + * and these are correct. The algorithm requires that the left page + * never move during a split, so we copy the new left page back on top + * of the original. Note that this is not a waste of time, since we + * also require (in the page management code) that the center of a + * page always be clean, and the most efficient way to guarantee this + * is just to compact the data by reinserting it into a new left page. + */ + + PageRestoreTempPage(leftpage, origpage); + + /* write these guys out */ + _bt_wrtnorelbuf(rel, rbuf); + _bt_wrtnorelbuf(rel, buf); + + /* + * Finally, we need to grab the right sibling (if any) and fix the + * prev pointer there. We are guaranteed that this is deadlock-free + * since no other writer will be moving holding a lock on that page + * and trying to move left, and all readers release locks on a page + * before trying to fetch its neighbors. + */ + + if (!P_RIGHTMOST(ropaque)) + { + sbuf = _bt_getbuf(rel, ropaque->btpo_next, BT_WRITE); + spage = BufferGetPage(sbuf); + sopaque = (BTPageOpaque) PageGetSpecialPointer(spage); + sopaque->btpo_prev = BufferGetBlockNumber(rbuf); + + /* write and release the old right sibling */ + _bt_wrtbuf(rel, sbuf); } - } - - /* - * Okay, page has been split, high key on right page is correct. Now - * set the high key on the left page to be the min key on the right - * page. - */ - - if (P_RIGHTMOST(ropaque)) { - itemid = PageGetItemId(rightpage, P_HIKEY); - } else { - itemid = PageGetItemId(rightpage, P_FIRSTKEY); - } - itemsz = ItemIdGetLength(itemid); - item = (BTItem) PageGetItem(rightpage, itemid); - - /* - * We left a hole for the high key on the left page; fill it. The - * modal crap is to tell the page manager to put the new item on the - * page and not screw around with anything else. Whoever designed - * this interface has presumably crawled back into the dung heap they - * came from. No one here will admit to it. - */ - - PageManagerModeSet(OverwritePageManagerMode); - if ( PageAddItem(leftpage, (Item) item, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add hikey to the left sibling"); - PageManagerModeSet(ShufflePageManagerMode); - - /* - * By here, the original data page has been split into two new halves, - * and these are correct. The algorithm requires that the left page - * never move during a split, so we copy the new left page back on top - * of the original. Note that this is not a waste of time, since we - * also require (in the page management code) that the center of a - * page always be clean, and the most efficient way to guarantee this - * is just to compact the data by reinserting it into a new left page. - */ - - PageRestoreTempPage(leftpage, origpage); - - /* write these guys out */ - _bt_wrtnorelbuf(rel, rbuf); - _bt_wrtnorelbuf(rel, buf); - - /* - * Finally, we need to grab the right sibling (if any) and fix the - * prev pointer there. We are guaranteed that this is deadlock-free - * since no other writer will be moving holding a lock on that page - * and trying to move left, and all readers release locks on a page - * before trying to fetch its neighbors. - */ - - if (! P_RIGHTMOST(ropaque)) { - sbuf = _bt_getbuf(rel, ropaque->btpo_next, BT_WRITE); - spage = BufferGetPage(sbuf); - sopaque = (BTPageOpaque) PageGetSpecialPointer(spage); - sopaque->btpo_prev = BufferGetBlockNumber(rbuf); - - /* write and release the old right sibling */ - _bt_wrtbuf(rel, sbuf); - } - - /* split's done */ - return (rbuf); + + /* split's done */ + return (rbuf); } /* - * _bt_findsplitloc() -- find a safe place to split a page. + * _bt_findsplitloc() -- find a safe place to split a page. * - * In order to guarantee the proper handling of searches for duplicate - * keys, the first duplicate in the chain must either be the first - * item on the page after the split, or the entire chain must be on - * one of the two pages. That is, - * [1 2 2 2 3 4 5] - * must become - * [1] [2 2 2 3 4 5] - * or - * [1 2 2 2] [3 4 5] - * but not - * [1 2 2] [2 3 4 5]. - * However, - * [2 2 2 2 2 3 4] - * may be split as - * [2 2 2 2] [2 3 4]. + * In order to guarantee the proper handling of searches for duplicate + * keys, the first duplicate in the chain must either be the first + * item on the page after the split, or the entire chain must be on + * one of the two pages. That is, + * [1 2 2 2 3 4 5] + * must become + * [1] [2 2 2 3 4 5] + * or + * [1 2 2 2] [3 4 5] + * but not + * [1 2 2] [2 3 4 5]. + * However, + * [2 2 2 2 2 3 4] + * may be split as + * [2 2 2 2] [2 3 4]. */ -static OffsetNumber +static OffsetNumber _bt_findsplitloc(Relation rel, - Page page, - OffsetNumber start, - OffsetNumber maxoff, - Size llimit) + Page page, + OffsetNumber start, + OffsetNumber maxoff, + Size llimit) { - OffsetNumber i; - OffsetNumber saferight; - ItemId nxtitemid, safeitemid; - BTItem safeitem, nxtitem; - Size nbytes; - int natts; - - if ( start >= maxoff ) - elog (FATAL, "btree: cannot split if start (%d) >= maxoff (%d)", - start, maxoff); - natts = rel->rd_rel->relnatts; - saferight = start; - safeitemid = PageGetItemId(page, saferight); - nbytes = ItemIdGetLength(safeitemid) + sizeof(ItemIdData); - safeitem = (BTItem) PageGetItem(page, safeitemid); - - i = OffsetNumberNext(start); - - while (nbytes < llimit) - { - /* check the next item on the page */ - nxtitemid = PageGetItemId(page, i); - nbytes += (ItemIdGetLength(nxtitemid) + sizeof(ItemIdData)); - nxtitem = (BTItem) PageGetItem(page, nxtitemid); - - /* - * Test against last known safe item: - * if the tuple we're looking at isn't equal to the last safe - * one we saw, then it's our new safe tuple. - */ - if ( !_bt_itemcmp (rel, natts, - safeitem, nxtitem, BTEqualStrategyNumber) ) + OffsetNumber i; + OffsetNumber saferight; + ItemId nxtitemid, + safeitemid; + BTItem safeitem, + nxtitem; + Size nbytes; + int natts; + + if (start >= maxoff) + elog(FATAL, "btree: cannot split if start (%d) >= maxoff (%d)", + start, maxoff); + natts = rel->rd_rel->relnatts; + saferight = start; + safeitemid = PageGetItemId(page, saferight); + nbytes = ItemIdGetLength(safeitemid) + sizeof(ItemIdData); + safeitem = (BTItem) PageGetItem(page, safeitemid); + + i = OffsetNumberNext(start); + + while (nbytes < llimit) { - safeitem = nxtitem; - saferight = i; + /* check the next item on the page */ + nxtitemid = PageGetItemId(page, i); + nbytes += (ItemIdGetLength(nxtitemid) + sizeof(ItemIdData)); + nxtitem = (BTItem) PageGetItem(page, nxtitemid); + + /* + * Test against last known safe item: if the tuple we're looking + * at isn't equal to the last safe one we saw, then it's our new + * safe tuple. + */ + if (!_bt_itemcmp(rel, natts, + safeitem, nxtitem, BTEqualStrategyNumber)) + { + safeitem = nxtitem; + saferight = i; + } + if (i < maxoff) + i = OffsetNumberNext(i); + else + break; } - if ( i < maxoff ) - i = OffsetNumberNext(i); - else - break; - } - - /* - * If the chain of dups starts at the beginning of the page and extends - * past the halfway mark, we can split it in the middle. - */ - - if (saferight == start) - saferight = i; - - if ( saferight == maxoff && ( maxoff - start ) > 1 ) - saferight = start + ( maxoff - start ) / 2; - - return (saferight); + + /* + * If the chain of dups starts at the beginning of the page and + * extends past the halfway mark, we can split it in the middle. + */ + + if (saferight == start) + saferight = i; + + if (saferight == maxoff && (maxoff - start) > 1) + saferight = start + (maxoff - start) / 2; + + return (saferight); } /* - * _bt_newroot() -- Create a new root page for the index. + * _bt_newroot() -- Create a new root page for the index. * - * We've just split the old root page and need to create a new one. - * In order to do this, we add a new root page to the file, then lock - * the metadata page and update it. This is guaranteed to be deadlock- - * free, because all readers release their locks on the metadata page - * before trying to lock the root, and all writers lock the root before - * trying to lock the metadata page. We have a write lock on the old - * root page, so we have not introduced any cycles into the waits-for - * graph. + * We've just split the old root page and need to create a new one. + * In order to do this, we add a new root page to the file, then lock + * the metadata page and update it. This is guaranteed to be deadlock- + * free, because all readers release their locks on the metadata page + * before trying to lock the root, and all writers lock the root before + * trying to lock the metadata page. We have a write lock on the old + * root page, so we have not introduced any cycles into the waits-for + * graph. * - * On entry, lbuf (the old root) and rbuf (its new peer) are write- - * locked. We don't drop the locks in this routine; that's done by - * the caller. On exit, a new root page exists with entries for the - * two new children. The new root page is neither pinned nor locked. + * On entry, lbuf (the old root) and rbuf (its new peer) are write- + * locked. We don't drop the locks in this routine; that's done by + * the caller. On exit, a new root page exists with entries for the + * two new children. The new root page is neither pinned nor locked. */ static void _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf) { - Buffer rootbuf; - Page lpage, rpage, rootpage; - BlockNumber lbkno, rbkno; - BlockNumber rootbknum; - BTPageOpaque rootopaque; - ItemId itemid; - BTItem item; - Size itemsz; - BTItem new_item; - - /* get a new root page */ - rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); - rootpage = BufferGetPage(rootbuf); - _bt_pageinit(rootpage, BufferGetPageSize(rootbuf)); - - /* set btree special data */ - rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage); - rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE; - rootopaque->btpo_flags |= BTP_ROOT; - - /* - * Insert the internal tuple pointers. - */ - - lbkno = BufferGetBlockNumber(lbuf); - rbkno = BufferGetBlockNumber(rbuf); - lpage = BufferGetPage(lbuf); - rpage = BufferGetPage(rbuf); - - /* - * step over the high key on the left page while building the - * left page pointer. - */ - itemid = PageGetItemId(lpage, P_FIRSTKEY); - itemsz = ItemIdGetLength(itemid); - item = (BTItem) PageGetItem(lpage, itemid); - new_item = _bt_formitem(&(item->bti_itup)); - ItemPointerSet(&(new_item->bti_itup.t_tid), lbkno, P_HIKEY); - - /* - * insert the left page pointer into the new root page. the root - * page is the rightmost page on its level so the "high key" item - * is the first data item. - */ - if ( PageAddItem(rootpage, (Item) new_item, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add leftkey to new root page"); - pfree(new_item); - - /* - * the right page is the rightmost page on the second level, so - * the "high key" item is the first data item on that page as well. - */ - itemid = PageGetItemId(rpage, P_HIKEY); - itemsz = ItemIdGetLength(itemid); - item = (BTItem) PageGetItem(rpage, itemid); - new_item = _bt_formitem(&(item->bti_itup)); - ItemPointerSet(&(new_item->bti_itup.t_tid), rbkno, P_HIKEY); - - /* - * insert the right page pointer into the new root page. - */ - if ( PageAddItem(rootpage, (Item) new_item, itemsz, P_FIRSTKEY, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add rightkey to new root page"); - pfree(new_item); - - /* write and let go of the root buffer */ - rootbknum = BufferGetBlockNumber(rootbuf); - _bt_wrtbuf(rel, rootbuf); - - /* update metadata page with new root block number */ - _bt_metaproot(rel, rootbknum, 0); + Buffer rootbuf; + Page lpage, + rpage, + rootpage; + BlockNumber lbkno, + rbkno; + BlockNumber rootbknum; + BTPageOpaque rootopaque; + ItemId itemid; + BTItem item; + Size itemsz; + BTItem new_item; + + /* get a new root page */ + rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); + rootpage = BufferGetPage(rootbuf); + _bt_pageinit(rootpage, BufferGetPageSize(rootbuf)); + + /* set btree special data */ + rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage); + rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE; + rootopaque->btpo_flags |= BTP_ROOT; + + /* + * Insert the internal tuple pointers. + */ + + lbkno = BufferGetBlockNumber(lbuf); + rbkno = BufferGetBlockNumber(rbuf); + lpage = BufferGetPage(lbuf); + rpage = BufferGetPage(rbuf); + + /* + * step over the high key on the left page while building the left + * page pointer. + */ + itemid = PageGetItemId(lpage, P_FIRSTKEY); + itemsz = ItemIdGetLength(itemid); + item = (BTItem) PageGetItem(lpage, itemid); + new_item = _bt_formitem(&(item->bti_itup)); + ItemPointerSet(&(new_item->bti_itup.t_tid), lbkno, P_HIKEY); + + /* + * insert the left page pointer into the new root page. the root page + * is the rightmost page on its level so the "high key" item is the + * first data item. + */ + if (PageAddItem(rootpage, (Item) new_item, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add leftkey to new root page"); + pfree(new_item); + + /* + * the right page is the rightmost page on the second level, so the + * "high key" item is the first data item on that page as well. + */ + itemid = PageGetItemId(rpage, P_HIKEY); + itemsz = ItemIdGetLength(itemid); + item = (BTItem) PageGetItem(rpage, itemid); + new_item = _bt_formitem(&(item->bti_itup)); + ItemPointerSet(&(new_item->bti_itup.t_tid), rbkno, P_HIKEY); + + /* + * insert the right page pointer into the new root page. + */ + if (PageAddItem(rootpage, (Item) new_item, itemsz, P_FIRSTKEY, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add rightkey to new root page"); + pfree(new_item); + + /* write and let go of the root buffer */ + rootbknum = BufferGetBlockNumber(rootbuf); + _bt_wrtbuf(rel, rootbuf); + + /* update metadata page with new root block number */ + _bt_metaproot(rel, rootbknum, 0); } /* - * _bt_pgaddtup() -- add a tuple to a particular page in the index. + * _bt_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. If - * afteritem is non-null, it's the item that we expect our new item - * to follow. Otherwise, we do a binary search for the correct place - * and insert the new item there. + * 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. If + * afteritem is non-null, it's the item that we expect our new item + * to follow. Otherwise, we do a binary search for the correct place + * and insert the new item there. */ -static OffsetNumber +static OffsetNumber _bt_pgaddtup(Relation rel, - Buffer buf, - int keysz, - ScanKey itup_scankey, - Size itemsize, - BTItem btitem, - BTItem afteritem) + Buffer buf, + int keysz, + ScanKey itup_scankey, + Size itemsize, + BTItem btitem, + BTItem afteritem) { - OffsetNumber itup_off; - OffsetNumber first; - Page page; - BTPageOpaque opaque; - BTItem chkitem; - - page = BufferGetPage(buf); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - first = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - - if (afteritem == (BTItem) NULL) { - itup_off = _bt_binsrch(rel, buf, keysz, itup_scankey, BT_INSERTION); - } else { - itup_off = first; - - do { - chkitem = - (BTItem) PageGetItem(page, PageGetItemId(page, itup_off)); - itup_off = OffsetNumberNext(itup_off); - } while ( ! BTItemSame (chkitem, afteritem) ); - } - - if ( PageAddItem(page, (Item) btitem, itemsize, itup_off, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add item to the page"); - - /* write the buffer, but hold our lock */ - _bt_wrtnorelbuf(rel, buf); - - return (itup_off); + OffsetNumber itup_off; + OffsetNumber first; + Page page; + BTPageOpaque opaque; + BTItem chkitem; + + page = BufferGetPage(buf); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + first = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + if (afteritem == (BTItem) NULL) + { + itup_off = _bt_binsrch(rel, buf, keysz, itup_scankey, BT_INSERTION); + } + else + { + itup_off = first; + + do + { + chkitem = + (BTItem) PageGetItem(page, PageGetItemId(page, itup_off)); + itup_off = OffsetNumberNext(itup_off); + } while (!BTItemSame(chkitem, afteritem)); + } + + if (PageAddItem(page, (Item) btitem, itemsize, itup_off, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add item to the page"); + + /* write the buffer, but hold our lock */ + _bt_wrtnorelbuf(rel, buf); + + return (itup_off); } /* - * _bt_goesonpg() -- Does a new tuple belong on this page? + * _bt_goesonpg() -- Does a new tuple belong on this page? * - * This is part of the complexity introduced by allowing duplicate - * keys into the index. The tuple belongs on this page if: + * This is part of the complexity introduced by allowing duplicate + * keys into the index. The tuple belongs on this page if: * - * + there is no page to the right of this one; or - * + it is less than the high key on the page; or - * + the item it is to follow ("afteritem") appears on this - * page. + * + there is no page to the right of this one; or + * + it is less than the high key on the page; or + * + the item it is to follow ("afteritem") appears on this + * page. */ -static bool +static bool _bt_goesonpg(Relation rel, - Buffer buf, - Size keysz, - ScanKey scankey, - BTItem afteritem) + Buffer buf, + Size keysz, + ScanKey scankey, + BTItem afteritem) { - Page page; - ItemId hikey; - BTPageOpaque opaque; - BTItem chkitem; - OffsetNumber offnum, maxoff; - bool found; - - page = BufferGetPage(buf); - - /* no right neighbor? */ - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - if (P_RIGHTMOST(opaque)) - return (true); - - /* - * this is a non-rightmost page, so it must have a high key item. - * - * If the scan key is < the high key (the min key on the next page), - * then it for sure belongs here. - */ - hikey = PageGetItemId(page, P_HIKEY); - if (_bt_skeycmp(rel, keysz, scankey, page, hikey, BTLessStrategyNumber)) - return (true); - - /* - * If the scan key is > the high key, then it for sure doesn't belong - * here. - */ - - if (_bt_skeycmp(rel, keysz, scankey, page, hikey, BTGreaterStrategyNumber)) - return (false); - - /* - * If we have no adjacency information, and the item is equal to the - * high key on the page (by here it is), then the item does not belong - * on this page. - * - * Now it's not true in all cases. - vadim 06/10/97 - */ - - if (afteritem == (BTItem) NULL) - { - if ( opaque->btpo_flags & BTP_LEAF ) - return (false); - if ( opaque->btpo_flags & BTP_CHAIN ) - return (true); - if ( _bt_skeycmp (rel, keysz, scankey, page, - PageGetItemId(page, P_FIRSTKEY), - BTEqualStrategyNumber) ) - return (true); - return (false); - } - - /* damn, have to work for it. i hate that. */ - maxoff = PageGetMaxOffsetNumber(page); - - /* - * Search the entire page for the afteroid. We need to do this, rather - * than doing a binary search and starting from there, because if the - * key we're searching for is the leftmost key in the tree at this - * level, then a binary search will do the wrong thing. Splits are - * pretty infrequent, so the cost isn't as bad as it could be. - */ - - found = false; - for (offnum = P_FIRSTKEY; - offnum <= maxoff; - offnum = OffsetNumberNext(offnum)) { - chkitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); - - if ( BTItemSame (chkitem, afteritem) ) { - found = true; - break; + Page page; + ItemId hikey; + BTPageOpaque opaque; + BTItem chkitem; + OffsetNumber offnum, + maxoff; + bool found; + + page = BufferGetPage(buf); + + /* no right neighbor? */ + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + if (P_RIGHTMOST(opaque)) + return (true); + + /* + * this is a non-rightmost page, so it must have a high key item. + * + * If the scan key is < the high key (the min key on the next page), then + * it for sure belongs here. + */ + hikey = PageGetItemId(page, P_HIKEY); + if (_bt_skeycmp(rel, keysz, scankey, page, hikey, BTLessStrategyNumber)) + return (true); + + /* + * If the scan key is > the high key, then it for sure doesn't belong + * here. + */ + + if (_bt_skeycmp(rel, keysz, scankey, page, hikey, BTGreaterStrategyNumber)) + return (false); + + /* + * If we have no adjacency information, and the item is equal to the + * high key on the page (by here it is), then the item does not belong + * on this page. + * + * Now it's not true in all cases. - vadim 06/10/97 + */ + + if (afteritem == (BTItem) NULL) + { + if (opaque->btpo_flags & BTP_LEAF) + return (false); + if (opaque->btpo_flags & BTP_CHAIN) + return (true); + if (_bt_skeycmp(rel, keysz, scankey, page, + PageGetItemId(page, P_FIRSTKEY), + BTEqualStrategyNumber)) + return (true); + return (false); + } + + /* damn, have to work for it. i hate that. */ + maxoff = PageGetMaxOffsetNumber(page); + + /* + * Search the entire page for the afteroid. We need to do this, + * rather than doing a binary search and starting from there, because + * if the key we're searching for is the leftmost key in the tree at + * this level, then a binary search will do the wrong thing. Splits + * are pretty infrequent, so the cost isn't as bad as it could be. + */ + + found = false; + for (offnum = P_FIRSTKEY; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) + { + chkitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); + + if (BTItemSame(chkitem, afteritem)) + { + found = true; + break; + } } - } - - return (found); + + return (found); } /* - * _bt_itemcmp() -- compare item1 to item2 using a requested - * strategy (<, <=, =, >=, >) + * _bt_itemcmp() -- compare item1 to item2 using a requested + * strategy (<, <=, =, >=, >) * */ bool _bt_itemcmp(Relation rel, - Size keysz, - BTItem item1, - BTItem item2, - StrategyNumber strat) + Size keysz, + BTItem item1, + BTItem item2, + StrategyNumber strat) { - TupleDesc tupDes; - IndexTuple indexTuple1, indexTuple2; - Datum attrDatum1, attrDatum2; - int i; - bool isFirstNull, isSecondNull; - bool compare; - bool useEqual = false; - - if ( strat == BTLessEqualStrategyNumber ) - { - useEqual = true; - strat = BTLessStrategyNumber; - } - else if ( strat == BTGreaterEqualStrategyNumber ) - { - useEqual = true; - strat = BTGreaterStrategyNumber; - } - - tupDes = RelationGetTupleDescriptor(rel); - indexTuple1 = &(item1->bti_itup); - indexTuple2 = &(item2->bti_itup); - - for (i = 1; i <= keysz; i++) { - attrDatum1 = index_getattr(indexTuple1, i, tupDes, &isFirstNull); - attrDatum2 = index_getattr(indexTuple2, i, tupDes, &isSecondNull); - - /* see comments about NULLs handling in btbuild */ - if ( isFirstNull ) /* attr in item1 is NULL */ + TupleDesc tupDes; + IndexTuple indexTuple1, + indexTuple2; + Datum attrDatum1, + attrDatum2; + int i; + bool isFirstNull, + isSecondNull; + bool compare; + bool useEqual = false; + + if (strat == BTLessEqualStrategyNumber) { - if ( isSecondNull ) /* attr in item2 is NULL too */ - compare = ( strat == BTEqualStrategyNumber ) ? true : false; - else - compare = ( strat == BTGreaterStrategyNumber ) ? true : false; - } - else if ( isSecondNull ) /* attr in item1 is NOT_NULL and */ - { /* and attr in item2 is NULL */ - compare = ( strat == BTLessStrategyNumber ) ? true : false; - } - else - { - compare = _bt_invokestrat(rel, i, strat, attrDatum1, attrDatum2); + useEqual = true; + strat = BTLessStrategyNumber; } - - if ( compare ) /* true for one of ">, <, =" */ + else if (strat == BTGreaterEqualStrategyNumber) { - if ( strat != BTEqualStrategyNumber ) - return (true); + useEqual = true; + strat = BTGreaterStrategyNumber; } - else /* false for one of ">, <, =" */ + + tupDes = RelationGetTupleDescriptor(rel); + indexTuple1 = &(item1->bti_itup); + indexTuple2 = &(item2->bti_itup); + + for (i = 1; i <= keysz; i++) { - if ( strat == BTEqualStrategyNumber ) - return (false); - /* - * if original strat was "<=, >=" OR - * "<, >" but some attribute(s) left - * - need to test for Equality - */ - if ( useEqual || i < keysz ) - { - if ( isFirstNull || isSecondNull ) - compare = ( isFirstNull && isSecondNull ) ? true : false; - else - compare = _bt_invokestrat(rel, i, BTEqualStrategyNumber, - attrDatum1, attrDatum2); - if ( compare ) /* item1' and item2' attributes are equal */ - continue; /* - try to compare next attributes */ - } - return (false); + attrDatum1 = index_getattr(indexTuple1, i, tupDes, &isFirstNull); + attrDatum2 = index_getattr(indexTuple2, i, tupDes, &isSecondNull); + + /* see comments about NULLs handling in btbuild */ + if (isFirstNull) /* attr in item1 is NULL */ + { + if (isSecondNull) /* attr in item2 is NULL too */ + compare = (strat == BTEqualStrategyNumber) ? true : false; + else + compare = (strat == BTGreaterStrategyNumber) ? true : false; + } + else if (isSecondNull) /* attr in item1 is NOT_NULL and */ + { /* and attr in item2 is NULL */ + compare = (strat == BTLessStrategyNumber) ? true : false; + } + else + { + compare = _bt_invokestrat(rel, i, strat, attrDatum1, attrDatum2); + } + + if (compare) /* true for one of ">, <, =" */ + { + if (strat != BTEqualStrategyNumber) + return (true); + } + else +/* false for one of ">, <, =" */ + { + if (strat == BTEqualStrategyNumber) + return (false); + + /* + * if original strat was "<=, >=" OR "<, >" but some + * attribute(s) left - need to test for Equality + */ + if (useEqual || i < keysz) + { + if (isFirstNull || isSecondNull) + compare = (isFirstNull && isSecondNull) ? true : false; + else + compare = _bt_invokestrat(rel, i, BTEqualStrategyNumber, + attrDatum1, attrDatum2); + if (compare) /* item1' and item2' attributes are equal */ + continue; /* - try to compare next attributes */ + } + return (false); + } } - } - return (true); + return (true); } /* - * _bt_updateitem() -- updates the key of the item identified by the - * oid with the key of newItem (done in place if - * possible) + * _bt_updateitem() -- updates the key of the item identified by the + * oid with the key of newItem (done in place if + * possible) * */ static void _bt_updateitem(Relation rel, - Size keysz, - Buffer buf, - BTItem oldItem, - BTItem newItem) + Size keysz, + Buffer buf, + BTItem oldItem, + BTItem newItem) { - Page page; - OffsetNumber maxoff; - OffsetNumber i; - ItemPointerData itemPtrData; - BTItem item; - IndexTuple oldIndexTuple, newIndexTuple; - int first; - - page = BufferGetPage(buf); - maxoff = PageGetMaxOffsetNumber(page); - - /* locate item on the page */ - first = P_RIGHTMOST((BTPageOpaque) PageGetSpecialPointer(page)) - ? P_HIKEY : P_FIRSTKEY; - i = first; - do { - item = (BTItem) PageGetItem(page, PageGetItemId(page, i)); - i = OffsetNumberNext(i); - } while (i <= maxoff && ! BTItemSame (item, oldItem)); - - /* this should never happen (in theory) */ - if ( ! BTItemSame (item, oldItem) ) { - elog(FATAL, "_bt_getstackbuf was lying!!"); - } - - /* - * It's defined by caller (_bt_insertonpg) - */ - /* - if(IndexTupleDSize(newItem->bti_itup) > - IndexTupleDSize(item->bti_itup)) { - elog(NOTICE, "trying to overwrite a smaller value with a bigger one in _bt_updateitem"); - elog(WARN, "this is not good."); - } - */ - - oldIndexTuple = &(item->bti_itup); - newIndexTuple = &(newItem->bti_itup); + Page page; + OffsetNumber maxoff; + OffsetNumber i; + ItemPointerData itemPtrData; + BTItem item; + IndexTuple oldIndexTuple, + newIndexTuple; + int first; + + page = BufferGetPage(buf); + maxoff = PageGetMaxOffsetNumber(page); + + /* locate item on the page */ + first = P_RIGHTMOST((BTPageOpaque) PageGetSpecialPointer(page)) + ? P_HIKEY : P_FIRSTKEY; + i = first; + do + { + item = (BTItem) PageGetItem(page, PageGetItemId(page, i)); + i = OffsetNumberNext(i); + } while (i <= maxoff && !BTItemSame(item, oldItem)); + + /* this should never happen (in theory) */ + if (!BTItemSame(item, oldItem)) + { + elog(FATAL, "_bt_getstackbuf was lying!!"); + } + + /* + * It's defined by caller (_bt_insertonpg) + */ + + /* + * if(IndexTupleDSize(newItem->bti_itup) > + * IndexTupleDSize(item->bti_itup)) { elog(NOTICE, "trying to + * overwrite a smaller value with a bigger one in _bt_updateitem"); + * elog(WARN, "this is not good."); } + */ + + oldIndexTuple = &(item->bti_itup); + newIndexTuple = &(newItem->bti_itup); /* keep the original item pointer */ - ItemPointerCopy(&(oldIndexTuple->t_tid), &itemPtrData); - CopyIndexTuple(newIndexTuple, &oldIndexTuple); - ItemPointerCopy(&itemPtrData, &(oldIndexTuple->t_tid)); - + ItemPointerCopy(&(oldIndexTuple->t_tid), &itemPtrData); + CopyIndexTuple(newIndexTuple, &oldIndexTuple); + ItemPointerCopy(&itemPtrData, &(oldIndexTuple->t_tid)); + } /* @@ -1409,177 +1460,179 @@ _bt_updateitem(Relation rel, * * Rule is simple: NOT_NULL not equal NULL, NULL not_equal NULL too. */ -static bool -_bt_isequal (TupleDesc itupdesc, Page page, OffsetNumber offnum, - int keysz, ScanKey scankey) +static bool +_bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum, + int keysz, ScanKey scankey) { - Datum datum; - BTItem btitem; - IndexTuple itup; - ScanKey entry; - AttrNumber attno; - long result; - int i; - bool null; - - btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &(btitem->bti_itup); - - for (i = 1; i <= keysz; i++) - { - entry = &scankey[i - 1]; - attno = entry->sk_attno; - Assert (attno == i); - datum = index_getattr(itup, attno, itupdesc, &null); - - /* NULLs are not equal */ - if ( entry->sk_flags & SK_ISNULL || null ) - return (false); - - result = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, - entry->sk_argument, datum); - if (result != 0) - return (false); - } - - /* by here, the keys are equal */ - return (true); + Datum datum; + BTItem btitem; + IndexTuple itup; + ScanKey entry; + AttrNumber attno; + long result; + int i; + bool null; + + btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &(btitem->bti_itup); + + for (i = 1; i <= keysz; i++) + { + entry = &scankey[i - 1]; + attno = entry->sk_attno; + Assert(attno == i); + datum = index_getattr(itup, attno, itupdesc, &null); + + /* NULLs are not equal */ + if (entry->sk_flags & SK_ISNULL || null) + return (false); + + result = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, + entry->sk_argument, datum); + if (result != 0) + return (false); + } + + /* by here, the keys are equal */ + return (true); } #ifdef NOT_USED /* - * _bt_shift - insert btitem on the passed page after shifting page - * to the right in the tree. + * _bt_shift - insert btitem on the passed page after shifting page + * to the right in the tree. * * NOTE: tested for shifting leftmost page only, having btitem < hikey. */ -static InsertIndexResult -_bt_shift (Relation rel, Buffer buf, BTStack stack, int keysz, - ScanKey scankey, BTItem btitem, BTItem hikey) +static InsertIndexResult +_bt_shift(Relation rel, Buffer buf, BTStack stack, int keysz, + ScanKey scankey, BTItem btitem, BTItem hikey) { - InsertIndexResult res; - int itemsz; - Page page; - BlockNumber bknum; - BTPageOpaque pageop; - Buffer rbuf; - Page rpage; - BTPageOpaque rpageop; - Buffer pbuf; - Page ppage; - BTPageOpaque ppageop; - Buffer nbuf; - Page npage; - BTPageOpaque npageop; - BlockNumber nbknum; - BTItem nitem; - OffsetNumber afteroff; - - btitem = _bt_formitem(&(btitem->bti_itup)); - hikey = _bt_formitem(&(hikey->bti_itup)); - - page = BufferGetPage(buf); - - /* grab new page */ - nbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); - nbknum = BufferGetBlockNumber(nbuf); - npage = BufferGetPage(nbuf); - _bt_pageinit(npage, BufferGetPageSize(nbuf)); - npageop = (BTPageOpaque) PageGetSpecialPointer(npage); - - /* copy content of the passed page */ - memmove ((char *) npage, (char *) page, BufferGetPageSize(buf)); - - /* re-init old (passed) page */ - _bt_pageinit(page, BufferGetPageSize(buf)); - pageop = (BTPageOpaque) PageGetSpecialPointer(page); - - /* init old page opaque */ - pageop->btpo_flags = npageop->btpo_flags; /* restore flags */ - pageop->btpo_flags &= ~BTP_CHAIN; - if ( _bt_itemcmp (rel, keysz, hikey, btitem, BTEqualStrategyNumber) ) - pageop->btpo_flags |= BTP_CHAIN; - pageop->btpo_prev = npageop->btpo_prev; /* restore prev */ - pageop->btpo_next = nbknum; /* next points to the new page */ - - /* init shifted page opaque */ - npageop->btpo_prev = bknum = BufferGetBlockNumber(buf); - - /* shifted page is ok, populate old page */ - - /* add passed hikey */ - itemsz = IndexTupleDSize(hikey->bti_itup) - + (sizeof(BTItemData) - sizeof(IndexTupleData)); - itemsz = DOUBLEALIGN(itemsz); - if ( PageAddItem(page, (Item) hikey, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add hikey in _bt_shift"); - pfree (hikey); - - /* add btitem */ - itemsz = IndexTupleDSize(btitem->bti_itup) - + (sizeof(BTItemData) - sizeof(IndexTupleData)); - itemsz = DOUBLEALIGN(itemsz); - if ( PageAddItem(page, (Item) btitem, itemsz, P_FIRSTKEY, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add firstkey in _bt_shift"); - pfree (btitem); - nitem = (BTItem) PageGetItem(page, PageGetItemId(page, P_FIRSTKEY)); - btitem = _bt_formitem(&(nitem->bti_itup)); - ItemPointerSet(&(btitem->bti_itup.t_tid), bknum, P_HIKEY); - - /* ok, write them out */ - _bt_wrtnorelbuf(rel, nbuf); - _bt_wrtnorelbuf(rel, buf); - - /* fix btpo_prev on right sibling of old page */ - if ( !P_RIGHTMOST (npageop) ) - { - rbuf = _bt_getbuf(rel, npageop->btpo_next, BT_WRITE); - rpage = BufferGetPage(rbuf); - rpageop = (BTPageOpaque) PageGetSpecialPointer(rpage); - rpageop->btpo_prev = nbknum; - _bt_wrtbuf(rel, rbuf); - } - - /* get parent pointing to the old page */ - ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), - bknum, P_HIKEY); - pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); - ppage = BufferGetPage(pbuf); - ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); - - _bt_relbuf(rel, nbuf, BT_WRITE); - _bt_relbuf(rel, buf, BT_WRITE); - - /* re-set parent' pointer - we shifted our page to the right ! */ - nitem = (BTItem) PageGetItem (ppage, - PageGetItemId (ppage, stack->bts_offset)); - ItemPointerSet(&(nitem->bti_itup.t_tid), nbknum, P_HIKEY); - ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), nbknum, P_HIKEY); - _bt_wrtnorelbuf(rel, pbuf); - - /* - * Now we want insert into the parent pointer to our old page. It has to - * be inserted before the pointer to new page. You may get problems here - * (in the _bt_goesonpg and/or _bt_pgaddtup), but may be not - I don't - * know. It works if old page is leftmost (nitem is NULL) and - * btitem < hikey and it's all what we need currently. - vadim 05/30/97 - */ - nitem = NULL; - afteroff = P_FIRSTKEY; - if ( !P_RIGHTMOST (ppageop) ) - afteroff = OffsetNumberNext (afteroff); - if ( stack->bts_offset >= afteroff ) - { - afteroff = OffsetNumberPrev (stack->bts_offset); - nitem = (BTItem) PageGetItem (ppage, PageGetItemId (ppage, afteroff)); - nitem = _bt_formitem(&(nitem->bti_itup)); - } - res = _bt_insertonpg(rel, pbuf, stack->bts_parent, - keysz, scankey, btitem, nitem); - pfree (btitem); - - ItemPointerSet(&(res->pointerData), nbknum, P_HIKEY); - - return (res); + InsertIndexResult res; + int itemsz; + Page page; + BlockNumber bknum; + BTPageOpaque pageop; + Buffer rbuf; + Page rpage; + BTPageOpaque rpageop; + Buffer pbuf; + Page ppage; + BTPageOpaque ppageop; + Buffer nbuf; + Page npage; + BTPageOpaque npageop; + BlockNumber nbknum; + BTItem nitem; + OffsetNumber afteroff; + + btitem = _bt_formitem(&(btitem->bti_itup)); + hikey = _bt_formitem(&(hikey->bti_itup)); + + page = BufferGetPage(buf); + + /* grab new page */ + nbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); + nbknum = BufferGetBlockNumber(nbuf); + npage = BufferGetPage(nbuf); + _bt_pageinit(npage, BufferGetPageSize(nbuf)); + npageop = (BTPageOpaque) PageGetSpecialPointer(npage); + + /* copy content of the passed page */ + memmove((char *) npage, (char *) page, BufferGetPageSize(buf)); + + /* re-init old (passed) page */ + _bt_pageinit(page, BufferGetPageSize(buf)); + pageop = (BTPageOpaque) PageGetSpecialPointer(page); + + /* init old page opaque */ + pageop->btpo_flags = npageop->btpo_flags; /* restore flags */ + pageop->btpo_flags &= ~BTP_CHAIN; + if (_bt_itemcmp(rel, keysz, hikey, btitem, BTEqualStrategyNumber)) + pageop->btpo_flags |= BTP_CHAIN; + pageop->btpo_prev = npageop->btpo_prev; /* restore prev */ + pageop->btpo_next = nbknum; /* next points to the new page */ + + /* init shifted page opaque */ + npageop->btpo_prev = bknum = BufferGetBlockNumber(buf); + + /* shifted page is ok, populate old page */ + + /* add passed hikey */ + itemsz = IndexTupleDSize(hikey->bti_itup) + + (sizeof(BTItemData) - sizeof(IndexTupleData)); + itemsz = DOUBLEALIGN(itemsz); + if (PageAddItem(page, (Item) hikey, itemsz, P_HIKEY, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add hikey in _bt_shift"); + pfree(hikey); + + /* add btitem */ + itemsz = IndexTupleDSize(btitem->bti_itup) + + (sizeof(BTItemData) - sizeof(IndexTupleData)); + itemsz = DOUBLEALIGN(itemsz); + if (PageAddItem(page, (Item) btitem, itemsz, P_FIRSTKEY, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add firstkey in _bt_shift"); + pfree(btitem); + nitem = (BTItem) PageGetItem(page, PageGetItemId(page, P_FIRSTKEY)); + btitem = _bt_formitem(&(nitem->bti_itup)); + ItemPointerSet(&(btitem->bti_itup.t_tid), bknum, P_HIKEY); + + /* ok, write them out */ + _bt_wrtnorelbuf(rel, nbuf); + _bt_wrtnorelbuf(rel, buf); + + /* fix btpo_prev on right sibling of old page */ + if (!P_RIGHTMOST(npageop)) + { + rbuf = _bt_getbuf(rel, npageop->btpo_next, BT_WRITE); + rpage = BufferGetPage(rbuf); + rpageop = (BTPageOpaque) PageGetSpecialPointer(rpage); + rpageop->btpo_prev = nbknum; + _bt_wrtbuf(rel, rbuf); + } + + /* get parent pointing to the old page */ + ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), + bknum, P_HIKEY); + pbuf = _bt_getstackbuf(rel, stack, BT_WRITE); + ppage = BufferGetPage(pbuf); + ppageop = (BTPageOpaque) PageGetSpecialPointer(ppage); + + _bt_relbuf(rel, nbuf, BT_WRITE); + _bt_relbuf(rel, buf, BT_WRITE); + + /* re-set parent' pointer - we shifted our page to the right ! */ + nitem = (BTItem) PageGetItem(ppage, + PageGetItemId(ppage, stack->bts_offset)); + ItemPointerSet(&(nitem->bti_itup.t_tid), nbknum, P_HIKEY); + ItemPointerSet(&(stack->bts_btitem->bti_itup.t_tid), nbknum, P_HIKEY); + _bt_wrtnorelbuf(rel, pbuf); + + /* + * Now we want insert into the parent pointer to our old page. It has + * to be inserted before the pointer to new page. You may get problems + * here (in the _bt_goesonpg and/or _bt_pgaddtup), but may be not - I + * don't know. It works if old page is leftmost (nitem is NULL) and + * btitem < hikey and it's all what we need currently. - vadim + * 05/30/97 + */ + nitem = NULL; + afteroff = P_FIRSTKEY; + if (!P_RIGHTMOST(ppageop)) + afteroff = OffsetNumberNext(afteroff); + if (stack->bts_offset >= afteroff) + { + afteroff = OffsetNumberPrev(stack->bts_offset); + nitem = (BTItem) PageGetItem(ppage, PageGetItemId(ppage, afteroff)); + nitem = _bt_formitem(&(nitem->bti_itup)); + } + res = _bt_insertonpg(rel, pbuf, stack->bts_parent, + keysz, scankey, btitem, nitem); + pfree(btitem); + + ItemPointerSet(&(res->pointerData), nbknum, P_HIKEY); + + return (res); } + #endif diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 9142c557378..6551af4c17c 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * nbtpage.c-- - * BTree-specific page management code for the Postgres btree access - * method. + * BTree-specific page management code for the Postgres btree access + * method. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtpage.c,v 1.9 1997/08/19 21:29:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtpage.c,v 1.10 1997/09/07 04:38:52 momjian Exp $ * - * NOTES - * Postgres btree pages look like ordinary relation pages. The opaque - * data at high addresses includes pointers to left and right siblings - * and flag data describing page state. The first page in a btree, page - * zero, is special -- it stores meta-information describing the tree. - * Pages one and higher store the actual tree data. + * NOTES + * Postgres btree pages look like ordinary relation pages. The opaque + * data at high addresses includes pointers to left and right siblings + * and flag data describing page state. The first page in a btree, page + * zero, is special -- it stores meta-information describing the tree. + * Pages one and higher store the actual tree data. * *------------------------------------------------------------------------- */ @@ -31,16 +31,16 @@ #include <storage/lmgr.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static void _bt_setpagelock(Relation rel, BlockNumber blkno, int access); -static void _bt_unsetpagelock(Relation rel, BlockNumber blkno, int access); +static void _bt_setpagelock(Relation rel, BlockNumber blkno, int access); +static void _bt_unsetpagelock(Relation rel, BlockNumber blkno, int access); #define BTREE_METAPAGE 0 -#define BTREE_MAGIC 0x053162 +#define BTREE_MAGIC 0x053162 #ifdef BTREE_VERSION_1 #define BTREE_VERSION 1 @@ -48,546 +48,574 @@ static void _bt_unsetpagelock(Relation rel, BlockNumber blkno, int access); #define BTREE_VERSION 0 #endif -typedef struct BTMetaPageData { - uint32 btm_magic; - uint32 btm_version; - BlockNumber btm_root; +typedef struct BTMetaPageData +{ + uint32 btm_magic; + uint32 btm_version; + BlockNumber btm_root; #ifdef BTREE_VERSION_1 - int32 btm_level; + int32 btm_level; #endif -} BTMetaPageData; +} BTMetaPageData; -#define BTPageGetMeta(p) \ - ((BTMetaPageData *) &((PageHeader) p)->pd_linp[0]) +#define BTPageGetMeta(p) \ + ((BTMetaPageData *) &((PageHeader) p)->pd_linp[0]) -extern bool BuildingBtree; +extern bool BuildingBtree; /* - * We use high-concurrency locking on btrees. There are two cases in - * which we don't do locking. One is when we're building the btree. - * Since the creating transaction has not committed, no one can see - * the index, and there's no reason to share locks. The second case - * is when we're just starting up the database system. We use some - * special-purpose initialization code in the relation cache manager - * (see utils/cache/relcache.c) to allow us to do indexed scans on - * the system catalogs before we'd normally be able to. This happens - * before the lock table is fully initialized, so we can't use it. - * Strictly speaking, this violates 2pl, but we don't do 2pl on the - * system catalogs anyway, so I declare this to be okay. + * We use high-concurrency locking on btrees. There are two cases in + * which we don't do locking. One is when we're building the btree. + * Since the creating transaction has not committed, no one can see + * the index, and there's no reason to share locks. The second case + * is when we're just starting up the database system. We use some + * special-purpose initialization code in the relation cache manager + * (see utils/cache/relcache.c) to allow us to do indexed scans on + * the system catalogs before we'd normally be able to. This happens + * before the lock table is fully initialized, so we can't use it. + * Strictly speaking, this violates 2pl, but we don't do 2pl on the + * system catalogs anyway, so I declare this to be okay. */ -#define USELOCKING (!BuildingBtree && !IsInitProcessingMode()) +#define USELOCKING (!BuildingBtree && !IsInitProcessingMode()) /* - * _bt_metapinit() -- Initialize the metadata page of a btree. + * _bt_metapinit() -- Initialize the metadata page of a btree. */ void _bt_metapinit(Relation rel) { - Buffer buf; - Page pg; - int nblocks; - BTMetaPageData metad; - BTPageOpaque op; - - /* can't be sharing this with anyone, now... */ - if (USELOCKING) - RelationSetLockForWrite(rel); - - if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0) { - elog(WARN, "Cannot initialize non-empty btree %s", - RelationGetRelationName(rel)); - } - - buf = ReadBuffer(rel, P_NEW); - pg = BufferGetPage(buf); - _bt_pageinit(pg, BufferGetPageSize(buf)); - - metad.btm_magic = BTREE_MAGIC; - metad.btm_version = BTREE_VERSION; - metad.btm_root = P_NONE; + Buffer buf; + Page pg; + int nblocks; + BTMetaPageData metad; + BTPageOpaque op; + + /* can't be sharing this with anyone, now... */ + if (USELOCKING) + RelationSetLockForWrite(rel); + + if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0) + { + elog(WARN, "Cannot initialize non-empty btree %s", + RelationGetRelationName(rel)); + } + + buf = ReadBuffer(rel, P_NEW); + pg = BufferGetPage(buf); + _bt_pageinit(pg, BufferGetPageSize(buf)); + + metad.btm_magic = BTREE_MAGIC; + metad.btm_version = BTREE_VERSION; + metad.btm_root = P_NONE; #ifdef BTREE_VERSION_1 - metad.btm_level = 0; + metad.btm_level = 0; #endif - memmove((char *) BTPageGetMeta(pg), (char *) &metad, sizeof(metad)); - - op = (BTPageOpaque) PageGetSpecialPointer(pg); - op->btpo_flags = BTP_META; - - WriteBuffer(buf); - - /* all done */ - if (USELOCKING) - RelationUnsetLockForWrite(rel); + memmove((char *) BTPageGetMeta(pg), (char *) &metad, sizeof(metad)); + + op = (BTPageOpaque) PageGetSpecialPointer(pg); + op->btpo_flags = BTP_META; + + WriteBuffer(buf); + + /* all done */ + if (USELOCKING) + RelationUnsetLockForWrite(rel); } #ifdef NOT_USED /* - * _bt_checkmeta() -- Verify that the metadata stored in a btree are - * reasonable. + * _bt_checkmeta() -- Verify that the metadata stored in a btree are + * reasonable. */ void _bt_checkmeta(Relation rel) { - Buffer metabuf; - Page metap; - BTMetaPageData *metad; - BTPageOpaque op; - int nblocks; - - /* if the relation is empty, this is init time; don't complain */ - if ((nblocks = RelationGetNumberOfBlocks(rel)) == 0) - return; - - metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ); - metap = BufferGetPage(metabuf); - op = (BTPageOpaque) PageGetSpecialPointer(metap); - if (!(op->btpo_flags & BTP_META)) { - elog(WARN, "Invalid metapage for index %s", - RelationGetRelationName(rel)); - } - metad = BTPageGetMeta(metap); - - if (metad->btm_magic != BTREE_MAGIC) { - elog(WARN, "Index %s is not a btree", - RelationGetRelationName(rel)); - } - - if (metad->btm_version != BTREE_VERSION) { - elog(WARN, "Version mismatch on %s: version %d file, version %d code", - RelationGetRelationName(rel), - metad->btm_version, BTREE_VERSION); - } - - _bt_relbuf(rel, metabuf, BT_READ); + Buffer metabuf; + Page metap; + BTMetaPageData *metad; + BTPageOpaque op; + int nblocks; + + /* if the relation is empty, this is init time; don't complain */ + if ((nblocks = RelationGetNumberOfBlocks(rel)) == 0) + return; + + metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ); + metap = BufferGetPage(metabuf); + op = (BTPageOpaque) PageGetSpecialPointer(metap); + if (!(op->btpo_flags & BTP_META)) + { + elog(WARN, "Invalid metapage for index %s", + RelationGetRelationName(rel)); + } + metad = BTPageGetMeta(metap); + + if (metad->btm_magic != BTREE_MAGIC) + { + elog(WARN, "Index %s is not a btree", + RelationGetRelationName(rel)); + } + + if (metad->btm_version != BTREE_VERSION) + { + elog(WARN, "Version mismatch on %s: version %d file, version %d code", + RelationGetRelationName(rel), + metad->btm_version, BTREE_VERSION); + } + + _bt_relbuf(rel, metabuf, BT_READ); } + #endif /* - * _bt_getroot() -- Get the root page of the btree. + * _bt_getroot() -- Get the root page of the btree. * - * Since the root page can move around the btree file, we have to read - * its location from the metadata page, and then read the root page - * itself. If no root page exists yet, we have to create one. The - * standard class of race conditions exists here; I think I covered - * them all in the Hopi Indian rain dance of lock requests below. + * Since the root page can move around the btree file, we have to read + * its location from the metadata page, and then read the root page + * itself. If no root page exists yet, we have to create one. The + * standard class of race conditions exists here; I think I covered + * them all in the Hopi Indian rain dance of lock requests below. * - * We pass in the access type (BT_READ or BT_WRITE), and return the - * root page's buffer with the appropriate lock type set. Reference - * count on the root page gets bumped by ReadBuffer. The metadata - * page is unlocked and unreferenced by this process when this routine - * returns. + * We pass in the access type (BT_READ or BT_WRITE), and return the + * root page's buffer with the appropriate lock type set. Reference + * count on the root page gets bumped by ReadBuffer. The metadata + * page is unlocked and unreferenced by this process when this routine + * returns. */ Buffer _bt_getroot(Relation rel, int access) { - Buffer metabuf; - Page metapg; - BTPageOpaque metaopaque; - Buffer rootbuf; - Page rootpg; - BTPageOpaque rootopaque; - BlockNumber rootblkno; - BTMetaPageData *metad; - - metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ); - metapg = BufferGetPage(metabuf); - metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg); - Assert(metaopaque->btpo_flags & BTP_META); - metad = BTPageGetMeta(metapg); - - if (metad->btm_magic != BTREE_MAGIC) { - elog(WARN, "Index %s is not a btree", - RelationGetRelationName(rel)); - } - - if (metad->btm_version != BTREE_VERSION) { - elog(WARN, "Version mismatch on %s: version %d file, version %d code", - RelationGetRelationName(rel), - metad->btm_version, BTREE_VERSION); - } - - /* if no root page initialized yet, do it */ - if (metad->btm_root == P_NONE) { - - /* turn our read lock in for a write lock */ - _bt_relbuf(rel, metabuf, BT_READ); - metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE); + Buffer metabuf; + Page metapg; + BTPageOpaque metaopaque; + Buffer rootbuf; + Page rootpg; + BTPageOpaque rootopaque; + BlockNumber rootblkno; + BTMetaPageData *metad; + + metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ); metapg = BufferGetPage(metabuf); metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg); Assert(metaopaque->btpo_flags & BTP_META); metad = BTPageGetMeta(metapg); - - /* - * Race condition: if someone else initialized the metadata between - * the time we released the read lock and acquired the write lock, - * above, we want to avoid doing it again. - */ - - if (metad->btm_root == P_NONE) { - - /* - * Get, initialize, write, and leave a lock of the appropriate - * type on the new root page. Since this is the first page in - * the tree, it's a leaf. - */ - - rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); - rootblkno = BufferGetBlockNumber(rootbuf); - rootpg = BufferGetPage(rootbuf); - metad->btm_root = rootblkno; + + if (metad->btm_magic != BTREE_MAGIC) + { + elog(WARN, "Index %s is not a btree", + RelationGetRelationName(rel)); + } + + if (metad->btm_version != BTREE_VERSION) + { + elog(WARN, "Version mismatch on %s: version %d file, version %d code", + RelationGetRelationName(rel), + metad->btm_version, BTREE_VERSION); + } + + /* if no root page initialized yet, do it */ + if (metad->btm_root == P_NONE) + { + + /* turn our read lock in for a write lock */ + _bt_relbuf(rel, metabuf, BT_READ); + metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE); + metapg = BufferGetPage(metabuf); + metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg); + Assert(metaopaque->btpo_flags & BTP_META); + metad = BTPageGetMeta(metapg); + + /* + * Race condition: if someone else initialized the metadata + * between the time we released the read lock and acquired the + * write lock, above, we want to avoid doing it again. + */ + + if (metad->btm_root == P_NONE) + { + + /* + * Get, initialize, write, and leave a lock of the appropriate + * type on the new root page. Since this is the first page in + * the tree, it's a leaf. + */ + + rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE); + rootblkno = BufferGetBlockNumber(rootbuf); + rootpg = BufferGetPage(rootbuf); + metad->btm_root = rootblkno; #ifdef BTREE_VERSION_1 - metad->btm_level = 1; + metad->btm_level = 1; #endif - _bt_pageinit(rootpg, BufferGetPageSize(rootbuf)); - rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg); - rootopaque->btpo_flags |= (BTP_LEAF | BTP_ROOT); - _bt_wrtnorelbuf(rel, rootbuf); - - /* swap write lock for read lock, if appropriate */ - if (access != BT_WRITE) { - _bt_setpagelock(rel, rootblkno, BT_READ); - _bt_unsetpagelock(rel, rootblkno, BT_WRITE); - } - - /* okay, metadata is correct */ - _bt_wrtbuf(rel, metabuf); - } else { - - /* - * Metadata initialized by someone else. In order to guarantee - * no deadlocks, we have to release the metadata page and start - * all over again. - */ - - _bt_relbuf(rel, metabuf, BT_WRITE); - return (_bt_getroot(rel, access)); + _bt_pageinit(rootpg, BufferGetPageSize(rootbuf)); + rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg); + rootopaque->btpo_flags |= (BTP_LEAF | BTP_ROOT); + _bt_wrtnorelbuf(rel, rootbuf); + + /* swap write lock for read lock, if appropriate */ + if (access != BT_WRITE) + { + _bt_setpagelock(rel, rootblkno, BT_READ); + _bt_unsetpagelock(rel, rootblkno, BT_WRITE); + } + + /* okay, metadata is correct */ + _bt_wrtbuf(rel, metabuf); + } + else + { + + /* + * Metadata initialized by someone else. In order to + * guarantee no deadlocks, we have to release the metadata + * page and start all over again. + */ + + _bt_relbuf(rel, metabuf, BT_WRITE); + return (_bt_getroot(rel, access)); + } } - } else { - rootbuf = _bt_getbuf(rel, metad->btm_root, access); - - /* done with the meta page */ - _bt_relbuf(rel, metabuf, BT_READ); - } - - /* - * Race condition: If the root page split between the time we looked - * at the metadata page and got the root buffer, then we got the wrong - * buffer. - */ - - rootpg = BufferGetPage(rootbuf); - rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg); - if (!(rootopaque->btpo_flags & BTP_ROOT)) { - - /* it happened, try again */ - _bt_relbuf(rel, rootbuf, access); - return (_bt_getroot(rel, access)); - } - - /* - * By here, we have a correct lock on the root block, its reference - * count is correct, and we have no lock set on the metadata page. - * Return the root block. - */ - - return (rootbuf); + else + { + rootbuf = _bt_getbuf(rel, metad->btm_root, access); + + /* done with the meta page */ + _bt_relbuf(rel, metabuf, BT_READ); + } + + /* + * Race condition: If the root page split between the time we looked + * at the metadata page and got the root buffer, then we got the wrong + * buffer. + */ + + rootpg = BufferGetPage(rootbuf); + rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg); + if (!(rootopaque->btpo_flags & BTP_ROOT)) + { + + /* it happened, try again */ + _bt_relbuf(rel, rootbuf, access); + return (_bt_getroot(rel, access)); + } + + /* + * By here, we have a correct lock on the root block, its reference + * count is correct, and we have no lock set on the metadata page. + * Return the root block. + */ + + return (rootbuf); } /* - * _bt_getbuf() -- Get a buffer by block number for read or write. + * _bt_getbuf() -- Get a buffer by block number for read or write. * - * When this routine returns, the appropriate lock is set on the - * requested buffer its reference count is correct. + * When this routine returns, the appropriate lock is set on the + * requested buffer its reference count is correct. */ Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access) { - Buffer buf; - Page page; - - /* - * If we want a new block, we can't set a lock of the appropriate type - * until we've instantiated the buffer. - */ - - if (blkno != P_NEW) { - if (access == BT_WRITE) - _bt_setpagelock(rel, blkno, BT_WRITE); - else - _bt_setpagelock(rel, blkno, BT_READ); - - buf = ReadBuffer(rel, blkno); - } else { - buf = ReadBuffer(rel, blkno); - blkno = BufferGetBlockNumber(buf); - page = BufferGetPage(buf); - _bt_pageinit(page, BufferGetPageSize(buf)); - - if (access == BT_WRITE) - _bt_setpagelock(rel, blkno, BT_WRITE); + Buffer buf; + Page page; + + /* + * If we want a new block, we can't set a lock of the appropriate type + * until we've instantiated the buffer. + */ + + if (blkno != P_NEW) + { + if (access == BT_WRITE) + _bt_setpagelock(rel, blkno, BT_WRITE); + else + _bt_setpagelock(rel, blkno, BT_READ); + + buf = ReadBuffer(rel, blkno); + } else - _bt_setpagelock(rel, blkno, BT_READ); - } - - /* ref count and lock type are correct */ - return (buf); + { + buf = ReadBuffer(rel, blkno); + blkno = BufferGetBlockNumber(buf); + page = BufferGetPage(buf); + _bt_pageinit(page, BufferGetPageSize(buf)); + + if (access == BT_WRITE) + _bt_setpagelock(rel, blkno, BT_WRITE); + else + _bt_setpagelock(rel, blkno, BT_READ); + } + + /* ref count and lock type are correct */ + return (buf); } /* - * _bt_relbuf() -- release a locked buffer. + * _bt_relbuf() -- release a locked buffer. */ void _bt_relbuf(Relation rel, Buffer buf, int access) { - BlockNumber blkno; - - blkno = BufferGetBlockNumber(buf); - - /* access had better be one of read or write */ - if (access == BT_WRITE) - _bt_unsetpagelock(rel, blkno, BT_WRITE); - else - _bt_unsetpagelock(rel, blkno, BT_READ); - - ReleaseBuffer(buf); + BlockNumber blkno; + + blkno = BufferGetBlockNumber(buf); + + /* access had better be one of read or write */ + if (access == BT_WRITE) + _bt_unsetpagelock(rel, blkno, BT_WRITE); + else + _bt_unsetpagelock(rel, blkno, BT_READ); + + ReleaseBuffer(buf); } /* - * _bt_wrtbuf() -- write a btree page to disk. + * _bt_wrtbuf() -- write a btree page to disk. * - * This routine releases the lock held on the buffer and our reference - * to it. It is an error to call _bt_wrtbuf() without a write lock - * or a reference to the buffer. + * This routine releases the lock held on the buffer and our reference + * to it. It is an error to call _bt_wrtbuf() without a write lock + * or a reference to the buffer. */ void _bt_wrtbuf(Relation rel, Buffer buf) { - BlockNumber blkno; - - blkno = BufferGetBlockNumber(buf); - WriteBuffer(buf); - _bt_unsetpagelock(rel, blkno, BT_WRITE); + BlockNumber blkno; + + blkno = BufferGetBlockNumber(buf); + WriteBuffer(buf); + _bt_unsetpagelock(rel, blkno, BT_WRITE); } /* - * _bt_wrtnorelbuf() -- write a btree page to disk, but do not release - * our reference or lock. + * _bt_wrtnorelbuf() -- write a btree page to disk, but do not release + * our reference or lock. * - * It is an error to call _bt_wrtnorelbuf() without a write lock - * or a reference to the buffer. + * It is an error to call _bt_wrtnorelbuf() without a write lock + * or a reference to the buffer. */ void _bt_wrtnorelbuf(Relation rel, Buffer buf) { - BlockNumber blkno; - - blkno = BufferGetBlockNumber(buf); - WriteNoReleaseBuffer(buf); + BlockNumber blkno; + + blkno = BufferGetBlockNumber(buf); + WriteNoReleaseBuffer(buf); } /* - * _bt_pageinit() -- Initialize a new page. + * _bt_pageinit() -- Initialize a new page. */ void _bt_pageinit(Page page, Size size) { - /* - * Cargo-cult programming -- don't really need this to be zero, but - * creating new pages is an infrequent occurrence and it makes me feel - * good when I know they're empty. - */ - - memset(page, 0, size); - - PageInit(page, size, sizeof(BTPageOpaqueData)); + + /* + * Cargo-cult programming -- don't really need this to be zero, but + * creating new pages is an infrequent occurrence and it makes me feel + * good when I know they're empty. + */ + + memset(page, 0, size); + + PageInit(page, size, sizeof(BTPageOpaqueData)); } /* - * _bt_metaproot() -- Change the root page of the btree. + * _bt_metaproot() -- Change the root page of the btree. * - * Lehman and Yao require that the root page move around in order to - * guarantee deadlock-free short-term, fine-granularity locking. When - * we split the root page, we record the new parent in the metadata page - * for the relation. This routine does the work. + * Lehman and Yao require that the root page move around in order to + * guarantee deadlock-free short-term, fine-granularity locking. When + * we split the root page, we record the new parent in the metadata page + * for the relation. This routine does the work. * - * No direct preconditions, but if you don't have the a write lock on - * at least the old root page when you call this, you're making a big - * mistake. On exit, metapage data is correct and we no longer have - * a reference to or lock on the metapage. + * No direct preconditions, but if you don't have the a write lock on + * at least the old root page when you call this, you're making a big + * mistake. On exit, metapage data is correct and we no longer have + * a reference to or lock on the metapage. */ void _bt_metaproot(Relation rel, BlockNumber rootbknum, int level) { - Buffer metabuf; - Page metap; - BTPageOpaque metaopaque; - BTMetaPageData *metad; - - metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE); - metap = BufferGetPage(metabuf); - metaopaque = (BTPageOpaque) PageGetSpecialPointer(metap); - Assert(metaopaque->btpo_flags & BTP_META); - metad = BTPageGetMeta(metap); - metad->btm_root = rootbknum; + Buffer metabuf; + Page metap; + BTPageOpaque metaopaque; + BTMetaPageData *metad; + + metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE); + metap = BufferGetPage(metabuf); + metaopaque = (BTPageOpaque) PageGetSpecialPointer(metap); + Assert(metaopaque->btpo_flags & BTP_META); + metad = BTPageGetMeta(metap); + metad->btm_root = rootbknum; #ifdef BTREE_VERSION_1 - if ( level == 0 ) /* called from _do_insert */ - metad->btm_level += 1; - else - metad->btm_level = level; /* called from btsort */ + if (level == 0) /* called from _do_insert */ + metad->btm_level += 1; + else + metad->btm_level = level; /* called from btsort */ #endif - _bt_wrtbuf(rel, metabuf); + _bt_wrtbuf(rel, metabuf); } /* - * _bt_getstackbuf() -- Walk back up the tree one step, and find the item - * we last looked at in the parent. + * _bt_getstackbuf() -- Walk back up the tree one step, and find the item + * we last looked at in the parent. * - * This is possible because we save a bit image of the last item - * we looked at in the parent, and the update algorithm guarantees - * that if items above us in the tree move, they only move right. + * This is possible because we save a bit image of the last item + * we looked at in the parent, and the update algorithm guarantees + * that if items above us in the tree move, they only move right. * - * Also, re-set bts_blkno & bts_offset if changed and - * bts_btitem (it may be changed - see _bt_insertonpg). + * Also, re-set bts_blkno & bts_offset if changed and + * bts_btitem (it may be changed - see _bt_insertonpg). */ Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access) { - Buffer buf; - BlockNumber blkno; - OffsetNumber start, offnum, maxoff; - OffsetNumber i; - Page page; - ItemId itemid; - BTItem item; - BTPageOpaque opaque; - BTItem item_save; - int item_nbytes; - - blkno = stack->bts_blkno; - buf = _bt_getbuf(rel, blkno, access); - page = BufferGetPage(buf); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - maxoff = PageGetMaxOffsetNumber(page); - - if (maxoff >= stack->bts_offset) { - itemid = PageGetItemId(page, stack->bts_offset); - item = (BTItem) PageGetItem(page, itemid); - - /* if the item is where we left it, we're done */ - if ( BTItemSame (item, stack->bts_btitem) ) - { - pfree(stack->bts_btitem); - item_nbytes = ItemIdGetLength(itemid); - item_save = (BTItem) palloc(item_nbytes); - memmove((char *) item_save, (char *) item, item_nbytes); - stack->bts_btitem = item_save; - return (buf); - } - - /* if the item has just moved right on this page, we're done */ - for (i = OffsetNumberNext(stack->bts_offset); - i <= maxoff; - i = OffsetNumberNext(i)) { - itemid = PageGetItemId(page, i); - item = (BTItem) PageGetItem(page, itemid); - - /* if the item is where we left it, we're done */ - if ( BTItemSame (item, stack->bts_btitem) ) - { - stack->bts_offset = i; - pfree(stack->bts_btitem); - item_nbytes = ItemIdGetLength(itemid); - item_save = (BTItem) palloc(item_nbytes); - memmove((char *) item_save, (char *) item, item_nbytes); - stack->bts_btitem = item_save; - return (buf); - } - } - } - - /* by here, the item we're looking for moved right at least one page */ - for (;;) { - blkno = opaque->btpo_next; - if (P_RIGHTMOST(opaque)) - elog(FATAL, "my bits moved right off the end of the world!"); - - _bt_relbuf(rel, buf, access); + Buffer buf; + BlockNumber blkno; + OffsetNumber start, + offnum, + maxoff; + OffsetNumber i; + Page page; + ItemId itemid; + BTItem item; + BTPageOpaque opaque; + BTItem item_save; + int item_nbytes; + + blkno = stack->bts_blkno; buf = _bt_getbuf(rel, blkno, access); page = BufferGetPage(buf); - maxoff = PageGetMaxOffsetNumber(page); opaque = (BTPageOpaque) PageGetSpecialPointer(page); - - /* if we have a right sibling, step over the high key */ - start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - - /* see if it's on this page */ - for (offnum = start; - offnum <= maxoff; - offnum = OffsetNumberNext(offnum)) { - itemid = PageGetItemId(page, offnum); - item = (BTItem) PageGetItem(page, itemid); - if ( BTItemSame (item, stack->bts_btitem) ) - { - stack->bts_offset = offnum; - stack->bts_blkno = blkno; - pfree(stack->bts_btitem); - item_nbytes = ItemIdGetLength(itemid); - item_save = (BTItem) palloc(item_nbytes); - memmove((char *) item_save, (char *) item, item_nbytes); - stack->bts_btitem = item_save; - return (buf); - } + maxoff = PageGetMaxOffsetNumber(page); + + if (maxoff >= stack->bts_offset) + { + itemid = PageGetItemId(page, stack->bts_offset); + item = (BTItem) PageGetItem(page, itemid); + + /* if the item is where we left it, we're done */ + if (BTItemSame(item, stack->bts_btitem)) + { + pfree(stack->bts_btitem); + item_nbytes = ItemIdGetLength(itemid); + item_save = (BTItem) palloc(item_nbytes); + memmove((char *) item_save, (char *) item, item_nbytes); + stack->bts_btitem = item_save; + return (buf); + } + + /* if the item has just moved right on this page, we're done */ + for (i = OffsetNumberNext(stack->bts_offset); + i <= maxoff; + i = OffsetNumberNext(i)) + { + itemid = PageGetItemId(page, i); + item = (BTItem) PageGetItem(page, itemid); + + /* if the item is where we left it, we're done */ + if (BTItemSame(item, stack->bts_btitem)) + { + stack->bts_offset = i; + pfree(stack->bts_btitem); + item_nbytes = ItemIdGetLength(itemid); + item_save = (BTItem) palloc(item_nbytes); + memmove((char *) item_save, (char *) item, item_nbytes); + stack->bts_btitem = item_save; + return (buf); + } + } + } + + /* by here, the item we're looking for moved right at least one page */ + for (;;) + { + blkno = opaque->btpo_next; + if (P_RIGHTMOST(opaque)) + elog(FATAL, "my bits moved right off the end of the world!"); + + _bt_relbuf(rel, buf, access); + buf = _bt_getbuf(rel, blkno, access); + page = BufferGetPage(buf); + maxoff = PageGetMaxOffsetNumber(page); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + /* if we have a right sibling, step over the high key */ + start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + /* see if it's on this page */ + for (offnum = start; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) + { + itemid = PageGetItemId(page, offnum); + item = (BTItem) PageGetItem(page, itemid); + if (BTItemSame(item, stack->bts_btitem)) + { + stack->bts_offset = offnum; + stack->bts_blkno = blkno; + pfree(stack->bts_btitem); + item_nbytes = ItemIdGetLength(itemid); + item_save = (BTItem) palloc(item_nbytes); + memmove((char *) item_save, (char *) item, item_nbytes); + stack->bts_btitem = item_save; + return (buf); + } + } } - } } static void _bt_setpagelock(Relation rel, BlockNumber blkno, int access) { - ItemPointerData iptr; - - if (USELOCKING) { - ItemPointerSet(&iptr, blkno, P_HIKEY); - - if (access == BT_WRITE) - RelationSetSingleWLockPage(rel, &iptr); - else - RelationSetSingleRLockPage(rel, &iptr); - } + ItemPointerData iptr; + + if (USELOCKING) + { + ItemPointerSet(&iptr, blkno, P_HIKEY); + + if (access == BT_WRITE) + RelationSetSingleWLockPage(rel, &iptr); + else + RelationSetSingleRLockPage(rel, &iptr); + } } static void _bt_unsetpagelock(Relation rel, BlockNumber blkno, int access) { - ItemPointerData iptr; - - if (USELOCKING) { - ItemPointerSet(&iptr, blkno, P_HIKEY); - - if (access == BT_WRITE) - RelationUnsetSingleWLockPage(rel, &iptr); - else - RelationUnsetSingleRLockPage(rel, &iptr); - } + ItemPointerData iptr; + + if (USELOCKING) + { + ItemPointerSet(&iptr, blkno, P_HIKEY); + + if (access == BT_WRITE) + RelationUnsetSingleWLockPage(rel, &iptr); + else + RelationUnsetSingleRLockPage(rel, &iptr); + } } void _bt_pagedel(Relation rel, ItemPointer tid) { - Buffer buf; - Page page; - BlockNumber blkno; - OffsetNumber offno; - - blkno = ItemPointerGetBlockNumber(tid); - offno = ItemPointerGetOffsetNumber(tid); - - buf = _bt_getbuf(rel, blkno, BT_WRITE); - page = BufferGetPage(buf); - - PageIndexTupleDelete(page, offno); - - /* write the buffer and release the lock */ - _bt_wrtbuf(rel, buf); + Buffer buf; + Page page; + BlockNumber blkno; + OffsetNumber offno; + + blkno = ItemPointerGetBlockNumber(tid); + offno = ItemPointerGetOffsetNumber(tid); + + buf = _bt_getbuf(rel, blkno, BT_WRITE); + page = BufferGetPage(buf); + + PageIndexTupleDelete(page, offno); + + /* write the buffer and release the lock */ + _bt_wrtbuf(rel, buf); } diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index b672901f8db..dccbd77b355 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * btree.c-- - * Implementation of Lehman and Yao's btree management algorithm for - * Postgres. + * Implementation of Lehman and Yao's btree management algorithm for + * Postgres. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.19 1997/05/05 03:41:17 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.20 1997/09/07 04:38:54 momjian Exp $ * * NOTES - * This file contains only the public interface routines. + * This file contains only the public interface routines. * *------------------------------------------------------------------------- */ @@ -28,546 +28,579 @@ #include <miscadmin.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif #ifdef BTREE_BUILD_STATS #include <tcop/tcopprot.h> -extern int ShowExecutorStats; +extern int ShowExecutorStats; + #endif -bool BuildingBtree = false; /* see comment in btbuild() */ -bool FastBuild = true; /* use sort/build instead of insertion build */ +bool BuildingBtree = false; /* see comment in btbuild() */ +bool FastBuild = true; /* use sort/build instead of + * insertion build */ /* - * btbuild() -- build a new btree index. + * btbuild() -- build a new btree 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 btbuild(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 = 0; - int nhtups, nitups; - int i; - BTItem btitem; + HeapScanDesc hscan; + Buffer buffer; + HeapTuple htup; + IndexTuple itup; + TupleDesc htupdesc, + itupdesc; + Datum *attdata; + bool *nulls; + InsertIndexResult res = 0; + int nhtups, + nitups; + int i; + BTItem btitem; + #ifndef OMIT_PARTIAL_INDEX - ExprContext *econtext = (ExprContext *) NULL; - TupleTable tupleTable = (TupleTable) NULL; - TupleTableSlot *slot = (TupleTableSlot *) NULL; -#endif - Oid hrelid, irelid; - Node *pred, *oldPred; - void *spool = (void *) NULL; - bool isunique; - bool usefast; - - /* note that this is a new btree */ - BuildingBtree = true; - - pred = predInfo->pred; - oldPred = predInfo->oldPred; - - /* - * bootstrap processing does something strange, so don't use - * sort/build for initial catalog indices. at some point i need - * to look harder at this. (there is some kind of incremental - * processing going on there.) -- pma 08/29/95 - */ - usefast = (FastBuild && IsNormalProcessingMode()); + ExprContext *econtext = (ExprContext *) NULL; + TupleTable tupleTable = (TupleTable) NULL; + TupleTableSlot *slot = (TupleTableSlot *) NULL; -#ifdef BTREE_BUILD_STATS - if ( ShowExecutorStats ) - ResetUsage (); #endif + Oid hrelid, + irelid; + Node *pred, + *oldPred; + void *spool = (void *) NULL; + bool isunique; + bool usefast; - /* see if index is unique */ - isunique = IndexIsUniqueNoCache(RelationGetRelationId(index)); - - /* initialize the btree index metadata page (if this is a new index) */ - if (oldPred == NULL) - _bt_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, InvalidBuffer); + /* note that this is a new btree */ + BuildingBtree = true; + + pred = predInfo->pred; + oldPred = predInfo->oldPred; /* - * we never want to use sort/build if we are extending an - * existing partial index -- it works by inserting the - * newly-qualifying tuples into the existing index. - * (sort/build would overwrite the existing index with one - * consisting of the newly-qualifying tuples.) + * bootstrap processing does something strange, so don't use + * sort/build for initial catalog indices. at some point i need to + * look harder at this. (there is some kind of incremental processing + * going on there.) -- pma 08/29/95 */ - usefast = false; - } -#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; - - if (usefast) { - spool = _bt_spoolinit(index, 7, isunique); - res = (InsertIndexResult) NULL; - } - - for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer)) { - - nhtups++; - + usefast = (FastBuild && IsNormalProcessingMode()); + +#ifdef BTREE_BUILD_STATS + if (ShowExecutorStats) + ResetUsage(); +#endif + + /* see if index is unique */ + isunique = IndexIsUniqueNoCache(RelationGetRelationId(index)); + + /* initialize the btree index metadata page (if this is a new index) */ + if (oldPred == NULL) + _bt_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 oldPred != NULL, this is an EXTEND INDEX command, so skip - * this tuple if it was already in the existing partial index + * 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 */ - if (oldPred != NULL) { +#ifndef OMIT_PARTIAL_INDEX + if (pred != NULL || oldPred != NULL) + { + tupleTable = ExecCreateTupleTable(1); + slot = ExecAllocTableSlot(tupleTable); + econtext = makeNode(ExprContext); + FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer); + + /* + * we never want to use sort/build if we are extending an existing + * partial index -- it works by inserting the newly-qualifying + * tuples into the existing index. (sort/build would overwrite the + * existing index with one consisting of the newly-qualifying + * tuples.) + */ + usefast = false; + } +#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; + + if (usefast) + { + spool = _bt_spoolinit(index, 7, isunique); + res = (InsertIndexResult) NULL; + } + + 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) + { #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) + { + 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 = AttrNumberGetAttrOffset(i); + 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. Btrees support scans on <, <=, =, >=, and >. Relational + * algebra says that A op B (where op is one of the operators + * above) 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. + */ + + /* + * New comments: NULLs handling. While we can't do NULL + * comparison, we can follow simple rule for ordering items on + * btree pages - NULLs greater NOT_NULLs and NULL = NULL is TRUE. + * Sure, it's just rule for placing/finding items and no more - + * keytest'll return FALSE for a = 5 for items having 'a' isNULL. + * Look at _bt_skeycmp, _bt_compare and _bt_itemcmp for how it + * works. - vadim 03/23/97 + * + * if (itup->t_info & INDEX_NULL_MASK) { pfree(itup); continue; } + */ + + itup->t_tid = htup->t_ctid; + btitem = _bt_formitem(itup); + + /* + * if we are doing bottom-up btree build, we insert the index into + * a spool page for subsequent processing. otherwise, we insert + * into the btree. + */ + if (usefast) + { + _bt_spool(index, btitem, spool); + } + else + { + res = _bt_doinsert(index, btitem, isunique, heap); + } + + pfree(btitem); + pfree(itup); + if (res) + { + 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 */ + ExecDestroyTupleTable(tupleTable, true); + pfree(econtext); +#endif /* OMIT_PARTIAL_INDEX */ } - - nitups++; - + /* - * For the current heap tuple, extract all the attributes - * we use in this index, and note which are null. + * if we are doing bottom-up btree build, we now have a bunch of + * sorted runs in the spool pages. finish the build by (1) merging + * the runs, (2) inserting the sorted tuples into btree pages and (3) + * building the upper levels. */ - - 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); - attdata[attoff] = GetIndexValue(htup, - htupdesc, - attoff, - attnum, - finfo, - &attnull, - buffer); - nulls[attoff] = (attnull ? 'n' : ' '); + if (usefast) + { + _bt_spool(index, (BTItem) NULL, spool); /* flush the spool */ + _bt_leafbuild(index, spool); + _bt_spooldestroy(spool); } - - /* 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. Btrees support scans on <, <=, =, >=, and >. - * Relational algebra says that A op B (where op is one of the - * operators above) 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. - */ - /* - * New comments: NULLs handling. - * While we can't do NULL comparison, we can follow simple - * rule for ordering items on btree pages - NULLs greater - * NOT_NULLs and NULL = NULL is TRUE. Sure, it's just rule - * for placing/finding items and no more - keytest'll return - * FALSE for a = 5 for items having 'a' isNULL. - * Look at _bt_skeycmp, _bt_compare and _bt_itemcmp for - * how it works. - vadim 03/23/97 - - if (itup->t_info & INDEX_NULL_MASK) { - pfree(itup); - continue; + +#ifdef BTREE_BUILD_STATS + if (ShowExecutorStats) + { + fprintf(stderr, "! BtreeBuild Stats:\n"); + ShowUsage(); + ResetUsage(); } - */ - - itup->t_tid = htup->t_ctid; - btitem = _bt_formitem(itup); +#endif /* - * if we are doing bottom-up btree build, we insert the index - * into a spool page for subsequent processing. otherwise, we - * insert into the btree. + * 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 (usefast) { - _bt_spool(index, btitem, spool); - } else { - res = _bt_doinsert(index, btitem, isunique, heap); + 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); + } } - pfree(btitem); - pfree(itup); - if (res) { - 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 */ - } - - /* - * if we are doing bottom-up btree build, we now have a bunch of - * sorted runs in the spool pages. finish the build by (1) - * merging the runs, (2) inserting the sorted tuples into btree - * pages and (3) building the upper levels. - */ - if (usefast) { - _bt_spool(index, (BTItem) NULL, spool); /* flush the spool */ - _bt_leafbuild(index, spool); - _bt_spooldestroy(spool); - } + pfree(nulls); + pfree(attdata); -#ifdef BTREE_BUILD_STATS - if ( ShowExecutorStats ) - { - fprintf(stderr, "! BtreeBuild Stats:\n"); - ShowUsage (); - ResetUsage (); - } -#endif - - /* - * 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()) - { - 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); - } - } - - pfree(nulls); - pfree(attdata); - - /* all done */ - BuildingBtree = false; + /* all done */ + BuildingBtree = false; } /* - * btinsert() -- insert an index tuple into a btree. + * btinsert() -- insert an index tuple into a btree. * - * Descend the tree recursively, find the appropriate location for our - * new tuple, put it there, set its unique OID as appropriate, and - * return an InsertIndexResult to the caller. + * Descend the tree recursively, find the appropriate location for our + * new tuple, put it there, set its unique OID as appropriate, and + * return an InsertIndexResult to the caller. */ InsertIndexResult -btinsert(Relation rel, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) +btinsert(Relation rel, Datum * datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) { - BTItem btitem; - IndexTuple itup; - InsertIndexResult res; - - /* generate an index tuple */ - itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls); - itup->t_tid = *ht_ctid; - - /* - * See comments in btbuild. - - if (itup->t_info & INDEX_NULL_MASK) - return ((InsertIndexResult) NULL); - */ - - btitem = _bt_formitem(itup); - - res = _bt_doinsert(rel, btitem, - IndexIsUnique(RelationGetRelationId(rel)), heapRel); - - pfree(btitem); - pfree(itup); - - /* adjust any active scans that will be affected by this insertion */ - _bt_adjscans(rel, &(res->pointerData), BT_INSERT); - - return (res); + BTItem btitem; + IndexTuple itup; + InsertIndexResult res; + + /* generate an index tuple */ + itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls); + itup->t_tid = *ht_ctid; + + /* + * See comments in btbuild. + * + * if (itup->t_info & INDEX_NULL_MASK) return ((InsertIndexResult) NULL); + */ + + btitem = _bt_formitem(itup); + + res = _bt_doinsert(rel, btitem, + IndexIsUnique(RelationGetRelationId(rel)), heapRel); + + pfree(btitem); + pfree(itup); + + /* adjust any active scans that will be affected by this insertion */ + _bt_adjscans(rel, &(res->pointerData), BT_INSERT); + + return (res); } /* - * btgettuple() -- Get the next tuple in the scan. + * btgettuple() -- Get the next tuple in the scan. */ -char * +char * btgettuple(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 = _bt_next(scan, dir); - else - res = _bt_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 = _bt_next(scan, dir); + else + res = _bt_first(scan, dir); + + return ((char *) res); } /* - * btbeginscan() -- start a scan on a btree index + * btbeginscan() -- start a scan on a btree index */ -char * +char * btbeginscan(Relation rel, bool fromEnd, uint16 keysz, ScanKey scankey) { - IndexScanDesc scan; - - /* get the scan */ - scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey); - - /* register scan in case we change pages it's using */ - _bt_regscan(scan); - - return ((char *) scan); + IndexScanDesc scan; + + /* get the scan */ + scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey); + + /* register scan in case we change pages it's using */ + _bt_regscan(scan); + + return ((char *) scan); } /* - * btrescan() -- rescan an index relation + * btrescan() -- rescan an index relation */ void btrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey) { - ItemPointer iptr; - BTScanOpaque so; - - so = (BTScanOpaque) scan->opaque; - - /* we hold a read lock on the current page in the scan */ - if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { - _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); - so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - /* and we hold a read lock on the last marked item in the scan */ - if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { - _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ); - so->btso_mrkbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - if ( so == NULL ) /* if called from btbeginscan */ - { - so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData)); - so->btso_curbuf = so->btso_mrkbuf = InvalidBuffer; - so->keyData = (ScanKey) NULL; - if ( scan->numberOfKeys > 0) - so->keyData = (ScanKey) palloc (scan->numberOfKeys * sizeof(ScanKeyData)); - scan->opaque = so; - scan->flags = 0x0; - } - - /* - * Reset the scan keys. Note that keys ordering stuff - * moved to _bt_first. - vadim 05/05/97 - */ - so->numberOfKeys = scan->numberOfKeys; - if (scan->numberOfKeys > 0) { - memmove(scan->keyData, - scankey, - scan->numberOfKeys * sizeof(ScanKeyData)); - memmove(so->keyData, - scankey, - so->numberOfKeys * sizeof(ScanKeyData)); - } + ItemPointer iptr; + BTScanOpaque so; + + so = (BTScanOpaque) scan->opaque; + + /* we hold a read lock on the current page in the scan */ + if (ItemPointerIsValid(iptr = &(scan->currentItemData))) + { + _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); + so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + /* and we hold a read lock on the last marked item in the scan */ + if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) + { + _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ); + so->btso_mrkbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + if (so == NULL) /* if called from btbeginscan */ + { + so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData)); + so->btso_curbuf = so->btso_mrkbuf = InvalidBuffer; + so->keyData = (ScanKey) NULL; + if (scan->numberOfKeys > 0) + so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData)); + scan->opaque = so; + scan->flags = 0x0; + } + + /* + * Reset the scan keys. Note that keys ordering stuff moved to + * _bt_first. - vadim 05/05/97 + */ + so->numberOfKeys = scan->numberOfKeys; + if (scan->numberOfKeys > 0) + { + memmove(scan->keyData, + scankey, + scan->numberOfKeys * sizeof(ScanKeyData)); + memmove(so->keyData, + scankey, + so->numberOfKeys * sizeof(ScanKeyData)); + } } void btmovescan(IndexScanDesc scan, Datum v) { - ItemPointer iptr; - BTScanOpaque so; - - so = (BTScanOpaque) scan->opaque; - - /* release any locks we still hold */ - if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { - _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); - so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - -/* scan->keyData[0].sk_argument = v; */ - so->keyData[0].sk_argument = v; + ItemPointer iptr; + BTScanOpaque so; + + so = (BTScanOpaque) scan->opaque; + + /* release any locks we still hold */ + if (ItemPointerIsValid(iptr = &(scan->currentItemData))) + { + _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); + so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + +/* scan->keyData[0].sk_argument = v; */ + so->keyData[0].sk_argument = v; } /* - * btendscan() -- close down a scan + * btendscan() -- close down a scan */ void btendscan(IndexScanDesc scan) { - ItemPointer iptr; - BTScanOpaque so; - - so = (BTScanOpaque) scan->opaque; - - /* release any locks we still hold */ - if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { - if (BufferIsValid(so->btso_curbuf)) - _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); - so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { - if (BufferIsValid(so->btso_mrkbuf)) - _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ); - so->btso_mrkbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - if ( so->keyData != (ScanKey) NULL ) - pfree (so->keyData); - pfree (so); - - _bt_dropscan(scan); + ItemPointer iptr; + BTScanOpaque so; + + so = (BTScanOpaque) scan->opaque; + + /* release any locks we still hold */ + if (ItemPointerIsValid(iptr = &(scan->currentItemData))) + { + if (BufferIsValid(so->btso_curbuf)) + _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); + so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) + { + if (BufferIsValid(so->btso_mrkbuf)) + _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ); + so->btso_mrkbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + if (so->keyData != (ScanKey) NULL) + pfree(so->keyData); + pfree(so); + + _bt_dropscan(scan); } /* - * btmarkpos() -- save current scan position + * btmarkpos() -- save current scan position */ void btmarkpos(IndexScanDesc scan) { - ItemPointer iptr; - BTScanOpaque so; - - so = (BTScanOpaque) scan->opaque; - - /* release lock on old marked data, if any */ - if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) { - _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ); - so->btso_mrkbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - /* bump lock on currentItemData and copy to currentMarkData */ - if (ItemPointerIsValid(&(scan->currentItemData))) { - so->btso_mrkbuf = _bt_getbuf(scan->relation, - BufferGetBlockNumber(so->btso_curbuf), - BT_READ); - scan->currentMarkData = scan->currentItemData; - } + ItemPointer iptr; + BTScanOpaque so; + + so = (BTScanOpaque) scan->opaque; + + /* release lock on old marked data, if any */ + if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) + { + _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ); + so->btso_mrkbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + /* bump lock on currentItemData and copy to currentMarkData */ + if (ItemPointerIsValid(&(scan->currentItemData))) + { + so->btso_mrkbuf = _bt_getbuf(scan->relation, + BufferGetBlockNumber(so->btso_curbuf), + BT_READ); + scan->currentMarkData = scan->currentItemData; + } } /* - * btrestrpos() -- restore scan to last saved position + * btrestrpos() -- restore scan to last saved position */ void btrestrpos(IndexScanDesc scan) { - ItemPointer iptr; - BTScanOpaque so; - - so = (BTScanOpaque) scan->opaque; - - /* release lock on current data, if any */ - if (ItemPointerIsValid(iptr = &(scan->currentItemData))) { - _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); - so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(iptr); - } - - /* bump lock on currentMarkData and copy to currentItemData */ - if (ItemPointerIsValid(&(scan->currentMarkData))) { - so->btso_curbuf = _bt_getbuf(scan->relation, - BufferGetBlockNumber(so->btso_mrkbuf), - BT_READ); - - scan->currentItemData = scan->currentMarkData; - } + ItemPointer iptr; + BTScanOpaque so; + + so = (BTScanOpaque) scan->opaque; + + /* release lock on current data, if any */ + if (ItemPointerIsValid(iptr = &(scan->currentItemData))) + { + _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ); + so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(iptr); + } + + /* bump lock on currentMarkData and copy to currentItemData */ + if (ItemPointerIsValid(&(scan->currentMarkData))) + { + so->btso_curbuf = _bt_getbuf(scan->relation, + BufferGetBlockNumber(so->btso_mrkbuf), + BT_READ); + + scan->currentItemData = scan->currentMarkData; + } } /* stubs */ void btdelete(Relation rel, ItemPointer tid) { - /* adjust any active scans that will be affected by this deletion */ - _bt_adjscans(rel, tid, BT_DELETE); - - /* delete the data from the page */ - _bt_pagedel(rel, tid); + /* adjust any active scans that will be affected by this deletion */ + _bt_adjscans(rel, tid, BT_DELETE); + + /* delete the data from the page */ + _bt_pagedel(rel, tid); } diff --git a/src/backend/access/nbtree/nbtscan.c b/src/backend/access/nbtree/nbtscan.c index 5e23fe13d7b..8a2042403ad 100644 --- a/src/backend/access/nbtree/nbtscan.c +++ b/src/backend/access/nbtree/nbtscan.c @@ -1,28 +1,28 @@ /*------------------------------------------------------------------------- * * btscan.c-- - * manage scans on btrees. + * manage scans on btrees. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.7 1997/02/18 17:13:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.8 1997/09/07 04:38:57 momjian Exp $ * * * NOTES - * Because we can be doing an index scan on a relation while we update - * it, we need to avoid missing data that moves around in the index. - * The routines and global variables in this file guarantee that all - * scans in the local address space stay correctly positioned. This - * is all we need to worry about, since write locking guarantees that - * no one else will be on the same page at the same time as we are. + * Because we can be doing an index scan on a relation while we update + * it, we need to avoid missing data that moves around in the index. + * The routines and global variables in this file guarantee that all + * scans in the local address space stay correctly positioned. This + * is all we need to worry about, since write locking guarantees that + * no one else will be on the same page at the same time as we are. * - * The scheme is to manage a list of active scans in the current backend. - * Whenever we add or remove records from an index, or whenever we - * split a leaf page, we check the list of active scans to see if any - * has been affected. A scan is affected only if it is on the same - * relation, and the same page, as the update. + * The scheme is to manage a list of active scans in the current backend. + * Whenever we add or remove records from an index, or whenever we + * split a leaf page, we check the list of active scans to see if any + * has been affected. A scan is affected only if it is on the same + * relation, and the same page, as the update. * *------------------------------------------------------------------------- */ @@ -32,83 +32,87 @@ #include <storage/bufpage.h> #include <access/nbtree.h> -typedef struct BTScanListData { - IndexScanDesc btsl_scan; - struct BTScanListData *btsl_next; -} BTScanListData; +typedef struct BTScanListData +{ + IndexScanDesc btsl_scan; + struct BTScanListData *btsl_next; +} BTScanListData; -typedef BTScanListData *BTScanList; +typedef BTScanListData *BTScanList; -static BTScanList BTScans = (BTScanList) NULL; +static BTScanList BTScans = (BTScanList) NULL; -static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno); -static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); +static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno); +static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno); /* - * _bt_regscan() -- register a new scan. + * _bt_regscan() -- register a new scan. */ void _bt_regscan(IndexScanDesc scan) { - BTScanList new_el; - - new_el = (BTScanList) palloc(sizeof(BTScanListData)); - new_el->btsl_scan = scan; - new_el->btsl_next = BTScans; - BTScans = new_el; + BTScanList new_el; + + new_el = (BTScanList) palloc(sizeof(BTScanListData)); + new_el->btsl_scan = scan; + new_el->btsl_next = BTScans; + BTScans = new_el; } /* - * _bt_dropscan() -- drop a scan from the scan list + * _bt_dropscan() -- drop a scan from the scan list */ void _bt_dropscan(IndexScanDesc scan) { - BTScanList chk, last; - - last = (BTScanList) NULL; - for (chk = BTScans; - chk != (BTScanList) NULL && chk->btsl_scan != scan; - chk = chk->btsl_next) { - last = chk; - } - - if (chk == (BTScanList) NULL) - elog(WARN, "btree scan list trashed; can't find 0x%lx", scan); - - if (last == (BTScanList) NULL) - BTScans = chk->btsl_next; - else - last->btsl_next = chk->btsl_next; - - pfree (chk); + BTScanList chk, + last; + + last = (BTScanList) NULL; + for (chk = BTScans; + chk != (BTScanList) NULL && chk->btsl_scan != scan; + chk = chk->btsl_next) + { + last = chk; + } + + if (chk == (BTScanList) NULL) + elog(WARN, "btree scan list trashed; can't find 0x%lx", scan); + + if (last == (BTScanList) NULL) + BTScans = chk->btsl_next; + else + last->btsl_next = chk->btsl_next; + + pfree(chk); } /* - * _bt_adjscans() -- adjust all scans in the scan list to compensate - * for a given deletion or insertion + * _bt_adjscans() -- adjust all scans in the scan list to compensate + * for a given deletion or insertion */ void _bt_adjscans(Relation rel, ItemPointer tid, int op) { - BTScanList l; - Oid relid; - - relid = rel->rd_id; - for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next) { - if (relid == l->btsl_scan->relation->rd_id) - _bt_scandel(l->btsl_scan, op, - ItemPointerGetBlockNumber(tid), - ItemPointerGetOffsetNumber(tid)); - } + BTScanList l; + Oid relid; + + relid = rel->rd_id; + for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next) + { + if (relid == l->btsl_scan->relation->rd_id) + _bt_scandel(l->btsl_scan, op, + ItemPointerGetBlockNumber(tid), + ItemPointerGetOffsetNumber(tid)); + } } /* - * _bt_scandel() -- adjust a single scan + * _bt_scandel() -- adjust a single scan * * because each index page is always maintained as an ordered array of * index tuples, the index tuples on a given page shift beneath any - * given scan. an index modification "behind" a scan position (i.e., + * given scan. an index modification "behind" a scan position (i.e., * same page, lower or equal offset number) will therefore force us to * adjust the scan in the following ways: * @@ -126,80 +130,85 @@ _bt_adjscans(Relation rel, ItemPointer tid, int op) static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno) { - ItemPointer current; - Buffer buf; - BTScanOpaque so; - - if (!_bt_scantouched(scan, blkno, offno)) - return; - - so = (BTScanOpaque) scan->opaque; - buf = so->btso_curbuf; - - current = &(scan->currentItemData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) { - switch (op) { - case BT_INSERT: - _bt_step(scan, &buf, ForwardScanDirection); - break; - case BT_DELETE: - _bt_step(scan, &buf, BackwardScanDirection); - break; - default: - elog(WARN, "_bt_scandel: bad operation '%d'", op); - /*NOTREACHED*/ + ItemPointer current; + Buffer buf; + BTScanOpaque so; + + if (!_bt_scantouched(scan, blkno, offno)) + return; + + so = (BTScanOpaque) scan->opaque; + buf = so->btso_curbuf; + + current = &(scan->currentItemData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + { + switch (op) + { + case BT_INSERT: + _bt_step(scan, &buf, ForwardScanDirection); + break; + case BT_DELETE: + _bt_step(scan, &buf, BackwardScanDirection); + break; + default: + elog(WARN, "_bt_scandel: bad operation '%d'", op); + /* NOTREACHED */ + } + so->btso_curbuf = buf; } - so->btso_curbuf = buf; - } - - current = &(scan->currentMarkData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) { - ItemPointerData tmp; - tmp = *current; - *current = scan->currentItemData; - scan->currentItemData = tmp; - switch (op) { - case BT_INSERT: - _bt_step(scan, &buf, ForwardScanDirection); - break; - case BT_DELETE: - _bt_step(scan, &buf, BackwardScanDirection); - break; - default: - elog(WARN, "_bt_scandel: bad operation '%d'", op); - /*NOTREACHED*/ + + current = &(scan->currentMarkData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + { + ItemPointerData tmp; + + tmp = *current; + *current = scan->currentItemData; + scan->currentItemData = tmp; + switch (op) + { + case BT_INSERT: + _bt_step(scan, &buf, ForwardScanDirection); + break; + case BT_DELETE: + _bt_step(scan, &buf, BackwardScanDirection); + break; + default: + elog(WARN, "_bt_scandel: bad operation '%d'", op); + /* NOTREACHED */ + } + so->btso_mrkbuf = buf; + tmp = *current; + *current = scan->currentItemData; + scan->currentItemData = tmp; } - so->btso_mrkbuf = buf; - tmp = *current; - *current = scan->currentItemData; - scan->currentItemData = tmp; - } } /* - * _bt_scantouched() -- check to see if a scan is affected by a given - * change to the index + * _bt_scantouched() -- check to see if a scan is affected by a given + * change to the index */ -static bool +static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno) { - ItemPointer current; - - current = &(scan->currentItemData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) - return (true); - - current = &(scan->currentMarkData); - if (ItemPointerIsValid(current) - && ItemPointerGetBlockNumber(current) == blkno - && ItemPointerGetOffsetNumber(current) >= offno) - return (true); - - return (false); + ItemPointer current; + + current = &(scan->currentItemData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + return (true); + + current = &(scan->currentMarkData); + if (ItemPointerIsValid(current) + && ItemPointerGetBlockNumber(current) == blkno + && ItemPointerGetOffsetNumber(current) >= offno) + return (true); + + return (false); } diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index 1d1c8072b93..8b1f75b7533 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * btsearch.c-- - * search code for postgres btrees. + * search code for postgres btrees. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.23 1997/08/19 21:29:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.24 1997/09/07 04:38:58 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,1435 +22,1516 @@ #include <catalog/pg_proc.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static BTStack -_bt_searchr(Relation rel, int keysz, ScanKey scankey, - Buffer *bufP, BTStack stack_in); -static OffsetNumber -_bt_firsteq(Relation rel, TupleDesc itupdesc, Page page, - Size keysz, ScanKey scankey, OffsetNumber offnum); -static int -_bt_compare(Relation rel, TupleDesc itupdesc, Page page, - int keysz, ScanKey scankey, OffsetNumber offnum); -static bool -_bt_twostep(IndexScanDesc scan, Buffer *bufP, ScanDirection dir); -static RetrieveIndexResult -_bt_endpoint(IndexScanDesc scan, ScanDirection dir); +static BTStack +_bt_searchr(Relation rel, int keysz, ScanKey scankey, + Buffer * bufP, BTStack stack_in); +static OffsetNumber +_bt_firsteq(Relation rel, TupleDesc itupdesc, Page page, + Size keysz, ScanKey scankey, OffsetNumber offnum); +static int +_bt_compare(Relation rel, TupleDesc itupdesc, Page page, + int keysz, ScanKey scankey, OffsetNumber offnum); +static bool + _bt_twostep(IndexScanDesc scan, Buffer * bufP, ScanDirection dir); +static RetrieveIndexResult + _bt_endpoint(IndexScanDesc scan, ScanDirection dir); /* - * _bt_search() -- Search for a scan key in the index. + * _bt_search() -- Search for a scan key in the index. * - * This routine is actually just a helper that sets things up and - * calls a recursive-descent search routine on the tree. + * This routine is actually just a helper that sets things up and + * calls a recursive-descent search routine on the tree. */ BTStack -_bt_search(Relation rel, int keysz, ScanKey scankey, Buffer *bufP) +_bt_search(Relation rel, int keysz, ScanKey scankey, Buffer * bufP) { - *bufP = _bt_getroot(rel, BT_READ); - return (_bt_searchr(rel, keysz, scankey, bufP, (BTStack) NULL)); + *bufP = _bt_getroot(rel, BT_READ); + return (_bt_searchr(rel, keysz, scankey, bufP, (BTStack) NULL)); } /* - * _bt_searchr() -- Search the tree recursively for a particular scankey. + * _bt_searchr() -- Search the tree recursively for a particular scankey. */ -static BTStack +static BTStack _bt_searchr(Relation rel, - int keysz, - ScanKey scankey, - Buffer *bufP, - BTStack stack_in) + int keysz, + ScanKey scankey, + Buffer * bufP, + BTStack stack_in) { - BTStack stack; - OffsetNumber offnum; - Page page; - BTPageOpaque opaque; - BlockNumber par_blkno; - BlockNumber blkno; - ItemId itemid; - BTItem btitem; - BTItem item_save; - int item_nbytes; - IndexTuple itup; - - /* if this is a leaf page, we're done */ - page = BufferGetPage(*bufP); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - if (opaque->btpo_flags & BTP_LEAF) - return (stack_in); - - /* - * Find the appropriate item on the internal page, and get the child - * page that it points to. - */ - - par_blkno = BufferGetBlockNumber(*bufP); - offnum = _bt_binsrch(rel, *bufP, keysz, scankey, BT_DESCENT); - itemid = PageGetItemId(page, offnum); - btitem = (BTItem) PageGetItem(page, itemid); - itup = &(btitem->bti_itup); - blkno = ItemPointerGetBlockNumber(&(itup->t_tid)); - - /* - * We need to save the bit image of the index entry we chose in the - * parent page on a stack. In case we split the tree, we'll use this - * bit image to figure out what our real parent page is, in case the - * parent splits while we're working lower in the tree. See the paper - * by Lehman and Yao for how this is detected and handled. (We use - * unique OIDs to disambiguate duplicate keys in the index -- Lehman - * and Yao disallow duplicate keys). - */ - - item_nbytes = ItemIdGetLength(itemid); - item_save = (BTItem) palloc(item_nbytes); - memmove((char *) item_save, (char *) btitem, item_nbytes); - stack = (BTStack) palloc(sizeof(BTStackData)); - stack->bts_blkno = par_blkno; - stack->bts_offset = offnum; - stack->bts_btitem = item_save; - stack->bts_parent = stack_in; - - /* drop the read lock on the parent page and acquire one on the child */ - _bt_relbuf(rel, *bufP, BT_READ); - *bufP = _bt_getbuf(rel, blkno, BT_READ); - - /* - * Race -- the page we just grabbed may have split since we read its - * pointer in the parent. If it has, we may need to move right to its - * new sibling. Do that. - */ - - *bufP = _bt_moveright(rel, *bufP, keysz, scankey, BT_READ); - - /* okay, all set to move down a level */ - return (_bt_searchr(rel, keysz, scankey, bufP, stack)); + BTStack stack; + OffsetNumber offnum; + Page page; + BTPageOpaque opaque; + BlockNumber par_blkno; + BlockNumber blkno; + ItemId itemid; + BTItem btitem; + BTItem item_save; + int item_nbytes; + IndexTuple itup; + + /* if this is a leaf page, we're done */ + page = BufferGetPage(*bufP); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + if (opaque->btpo_flags & BTP_LEAF) + return (stack_in); + + /* + * Find the appropriate item on the internal page, and get the child + * page that it points to. + */ + + par_blkno = BufferGetBlockNumber(*bufP); + offnum = _bt_binsrch(rel, *bufP, keysz, scankey, BT_DESCENT); + itemid = PageGetItemId(page, offnum); + btitem = (BTItem) PageGetItem(page, itemid); + itup = &(btitem->bti_itup); + blkno = ItemPointerGetBlockNumber(&(itup->t_tid)); + + /* + * We need to save the bit image of the index entry we chose in the + * parent page on a stack. In case we split the tree, we'll use this + * bit image to figure out what our real parent page is, in case the + * parent splits while we're working lower in the tree. See the paper + * by Lehman and Yao for how this is detected and handled. (We use + * unique OIDs to disambiguate duplicate keys in the index -- Lehman + * and Yao disallow duplicate keys). + */ + + item_nbytes = ItemIdGetLength(itemid); + item_save = (BTItem) palloc(item_nbytes); + memmove((char *) item_save, (char *) btitem, item_nbytes); + stack = (BTStack) palloc(sizeof(BTStackData)); + stack->bts_blkno = par_blkno; + stack->bts_offset = offnum; + stack->bts_btitem = item_save; + stack->bts_parent = stack_in; + + /* drop the read lock on the parent page and acquire one on the child */ + _bt_relbuf(rel, *bufP, BT_READ); + *bufP = _bt_getbuf(rel, blkno, BT_READ); + + /* + * Race -- the page we just grabbed may have split since we read its + * pointer in the parent. If it has, we may need to move right to its + * new sibling. Do that. + */ + + *bufP = _bt_moveright(rel, *bufP, keysz, scankey, BT_READ); + + /* okay, all set to move down a level */ + return (_bt_searchr(rel, keysz, scankey, bufP, stack)); } /* - * _bt_moveright() -- move right in the btree if necessary. + * _bt_moveright() -- move right in the btree if necessary. * - * When we drop and reacquire a pointer to a page, it is possible that - * the page has changed in the meanwhile. If this happens, we're - * guaranteed that the page has "split right" -- that is, that any - * data that appeared on the page originally is either on the page - * or strictly to the right of it. + * When we drop and reacquire a pointer to a page, it is possible that + * the page has changed in the meanwhile. If this happens, we're + * guaranteed that the page has "split right" -- that is, that any + * data that appeared on the page originally is either on the page + * or strictly to the right of it. * - * This routine decides whether or not we need to move right in the - * tree by examining the high key entry on the page. If that entry - * is strictly less than one we expect to be on the page, then our - * picture of the page is incorrect and we need to move right. + * This routine decides whether or not we need to move right in the + * tree by examining the high key entry on the page. If that entry + * is strictly less than one we expect to be on the page, then our + * picture of the page is incorrect and we need to move right. * - * On entry, we have the buffer pinned and a lock of the proper type. - * If we move right, we release the buffer and lock and acquire the - * same on the right sibling. + * On entry, we have the buffer pinned and a lock of the proper type. + * If we move right, we release the buffer and lock and acquire the + * same on the right sibling. */ Buffer _bt_moveright(Relation rel, - Buffer buf, - int keysz, - ScanKey scankey, - int access) + Buffer buf, + int keysz, + ScanKey scankey, + int access) { - Page page; - BTPageOpaque opaque; - ItemId hikey; - BlockNumber rblkno; - int natts = rel->rd_rel->relnatts; - - page = BufferGetPage(buf); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - - /* if we're on a rightmost page, we don't need to move right */ - if (P_RIGHTMOST(opaque)) - return (buf); - - /* by convention, item 0 on non-rightmost pages is the high key */ - hikey = PageGetItemId(page, P_HIKEY); - - /* - * If the scan key that brought us to this page is >= the high key - * stored on the page, then the page has split and we need to move - * right. - */ - - if (_bt_skeycmp(rel, keysz, scankey, page, hikey, - BTGreaterEqualStrategyNumber)) - { - /* move right as long as we need to */ - do + Page page; + BTPageOpaque opaque; + ItemId hikey; + BlockNumber rblkno; + int natts = rel->rd_rel->relnatts; + + page = BufferGetPage(buf); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + /* if we're on a rightmost page, we don't need to move right */ + if (P_RIGHTMOST(opaque)) + return (buf); + + /* by convention, item 0 on non-rightmost pages is the high key */ + hikey = PageGetItemId(page, P_HIKEY); + + /* + * If the scan key that brought us to this page is >= the high key + * stored on the page, then the page has split and we need to move + * right. + */ + + if (_bt_skeycmp(rel, keysz, scankey, page, hikey, + BTGreaterEqualStrategyNumber)) { - OffsetNumber offmax = PageGetMaxOffsetNumber(page); - /* - * If this page consists of all duplicate keys (hikey and first - * key on the page have the same value), then we don't need to - * step right. - * - * NOTE for multi-column indices: we may do scan using - * keys not for all attrs. But we handle duplicates - * using all attrs in _bt_insert/_bt_spool code. - * And so we've to compare scankey with _last_ item - * on this page to do not lose "good" tuples if number - * of attrs > keysize. Example: (2,0) - last items on - * this page, (2,1) - first item on next page (hikey), - * our scankey is x = 2. Scankey == (2,1) because of - * we compare first attrs only, but we shouldn't to move - * right of here. - vadim 04/15/97 - */ - - if ( _bt_skeycmp (rel, keysz, scankey, page, hikey, - BTEqualStrategyNumber) ) - { - if ( opaque->btpo_flags & BTP_CHAIN ) - { - Assert ( ( opaque->btpo_flags & BTP_LEAF ) || offmax > P_HIKEY ); - break; - } - if ( offmax > P_HIKEY ) - { - if ( natts == keysz ) /* sanity checks */ - { - if ( _bt_skeycmp (rel, keysz, scankey, page, - PageGetItemId (page, P_FIRSTKEY), - BTEqualStrategyNumber) ) - elog (FATAL, "btree: BTP_CHAIN flag was expected"); - if ( _bt_skeycmp (rel, keysz, scankey, page, - PageGetItemId (page, offmax), - BTEqualStrategyNumber) ) - elog (FATAL, "btree: unexpected equal last item"); - if ( _bt_skeycmp (rel, keysz, scankey, page, - PageGetItemId (page, offmax), - BTLessStrategyNumber) ) - elog (FATAL, "btree: unexpected greater last item"); - /* move right */ - } - else if ( _bt_skeycmp (rel, keysz, scankey, page, - PageGetItemId (page, offmax), - BTLessEqualStrategyNumber) ) - break; - } - } - - /* step right one page */ - rblkno = opaque->btpo_next; - _bt_relbuf(rel, buf, access); - buf = _bt_getbuf(rel, rblkno, access); - page = BufferGetPage(buf); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - hikey = PageGetItemId(page, P_HIKEY); - - } while (! P_RIGHTMOST(opaque) - && _bt_skeycmp(rel, keysz, scankey, page, hikey, - BTGreaterEqualStrategyNumber)); - } - return (buf); + /* move right as long as we need to */ + do + { + OffsetNumber offmax = PageGetMaxOffsetNumber(page); + + /* + * If this page consists of all duplicate keys (hikey and + * first key on the page have the same value), then we don't + * need to step right. + * + * NOTE for multi-column indices: we may do scan using keys not + * for all attrs. But we handle duplicates using all attrs in + * _bt_insert/_bt_spool code. And so we've to compare scankey + * with _last_ item on this page to do not lose "good" tuples + * if number of attrs > keysize. Example: (2,0) - last items + * on this page, (2,1) - first item on next page (hikey), our + * scankey is x = 2. Scankey == (2,1) because of we compare + * first attrs only, but we shouldn't to move right of here. + * - vadim 04/15/97 + */ + + if (_bt_skeycmp(rel, keysz, scankey, page, hikey, + BTEqualStrategyNumber)) + { + if (opaque->btpo_flags & BTP_CHAIN) + { + Assert((opaque->btpo_flags & BTP_LEAF) || offmax > P_HIKEY); + break; + } + if (offmax > P_HIKEY) + { + if (natts == keysz) /* sanity checks */ + { + if (_bt_skeycmp(rel, keysz, scankey, page, + PageGetItemId(page, P_FIRSTKEY), + BTEqualStrategyNumber)) + elog(FATAL, "btree: BTP_CHAIN flag was expected"); + if (_bt_skeycmp(rel, keysz, scankey, page, + PageGetItemId(page, offmax), + BTEqualStrategyNumber)) + elog(FATAL, "btree: unexpected equal last item"); + if (_bt_skeycmp(rel, keysz, scankey, page, + PageGetItemId(page, offmax), + BTLessStrategyNumber)) + elog(FATAL, "btree: unexpected greater last item"); + /* move right */ + } + else if (_bt_skeycmp(rel, keysz, scankey, page, + PageGetItemId(page, offmax), + BTLessEqualStrategyNumber)) + break; + } + } + + /* step right one page */ + rblkno = opaque->btpo_next; + _bt_relbuf(rel, buf, access); + buf = _bt_getbuf(rel, rblkno, access); + page = BufferGetPage(buf); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + hikey = PageGetItemId(page, P_HIKEY); + + } while (!P_RIGHTMOST(opaque) + && _bt_skeycmp(rel, keysz, scankey, page, hikey, + BTGreaterEqualStrategyNumber)); + } + return (buf); } /* - * _bt_skeycmp() -- compare a scan key to a particular item on a page using - * a requested strategy (<, <=, =, >=, >). + * _bt_skeycmp() -- compare a scan key to a particular item on a page using + * a requested strategy (<, <=, =, >=, >). * - * We ignore the unique OIDs stored in the btree item here. Those - * numbers are intended for use internally only, in repositioning a - * scan after a page split. They do not impose any meaningful ordering. + * We ignore the unique OIDs stored in the btree item here. Those + * numbers are intended for use internally only, in repositioning a + * scan after a page split. They do not impose any meaningful ordering. * - * The comparison is A <op> B, where A is the scan key and B is the - * tuple pointed at by itemid on page. + * The comparison is A <op> B, where A is the scan key and B is the + * tuple pointed at by itemid on page. */ bool _bt_skeycmp(Relation rel, - Size keysz, - ScanKey scankey, - Page page, - ItemId itemid, - StrategyNumber strat) + Size keysz, + ScanKey scankey, + Page page, + ItemId itemid, + StrategyNumber strat) { - BTItem item; - IndexTuple indexTuple; - TupleDesc tupDes; - ScanKey entry; - int i; - Datum attrDatum; - Datum keyDatum; - bool compare; - bool isNull; - bool useEqual = false; - bool keyNull; - - if ( strat == BTLessEqualStrategyNumber ) - { - useEqual = true; - strat = BTLessStrategyNumber; - } - else if ( strat == BTGreaterEqualStrategyNumber ) - { - useEqual = true; - strat = BTGreaterStrategyNumber; - } - - item = (BTItem) PageGetItem(page, itemid); - indexTuple = &(item->bti_itup); - - tupDes = RelationGetTupleDescriptor(rel); - - /* see if the comparison is true for all of the key attributes */ - for (i=1; i <= keysz; i++) { - - entry = &scankey[i-1]; - Assert ( entry->sk_attno == i ); - attrDatum = index_getattr(indexTuple, - entry->sk_attno, - tupDes, - &isNull); - keyDatum = entry->sk_argument; - - /* see comments about NULLs handling in btbuild */ - if ( entry->sk_flags & SK_ISNULL ) /* key is NULL */ + BTItem item; + IndexTuple indexTuple; + TupleDesc tupDes; + ScanKey entry; + int i; + Datum attrDatum; + Datum keyDatum; + bool compare; + bool isNull; + bool useEqual = false; + bool keyNull; + + if (strat == BTLessEqualStrategyNumber) { - Assert ( entry->sk_procedure == NullValueRegProcedure ); - keyNull = true; - if ( isNull ) - compare = ( strat == BTEqualStrategyNumber ) ? true : false; - else - compare = ( strat == BTGreaterStrategyNumber ) ? true : false; - } - else if ( isNull ) /* key is NOT_NULL and item is NULL */ - { - keyNull = false; - compare = ( strat == BTLessStrategyNumber ) ? true : false; - } - else - { - keyNull = false; - compare = _bt_invokestrat(rel, i, strat, keyDatum, attrDatum); + useEqual = true; + strat = BTLessStrategyNumber; } - - if ( compare ) /* true for one of ">, <, =" */ + else if (strat == BTGreaterEqualStrategyNumber) { - if ( strat != BTEqualStrategyNumber ) - return (true); + useEqual = true; + strat = BTGreaterStrategyNumber; } - else /* false for one of ">, <, =" */ + + item = (BTItem) PageGetItem(page, itemid); + indexTuple = &(item->bti_itup); + + tupDes = RelationGetTupleDescriptor(rel); + + /* see if the comparison is true for all of the key attributes */ + for (i = 1; i <= keysz; i++) { - if ( strat == BTEqualStrategyNumber ) - return (false); - /* - * if original strat was "<=, >=" OR - * "<, >" but some attribute(s) left - * - need to test for Equality - */ - if ( useEqual || i < keysz ) - { - if ( keyNull || isNull ) - compare = ( keyNull && isNull ) ? true : false; - else - compare = _bt_invokestrat(rel, i, BTEqualStrategyNumber, - keyDatum, attrDatum); - if ( compare ) /* key' and item' attributes are equal */ - continue; /* - try to compare next attributes */ - } - return (false); + + entry = &scankey[i - 1]; + Assert(entry->sk_attno == i); + attrDatum = index_getattr(indexTuple, + entry->sk_attno, + tupDes, + &isNull); + keyDatum = entry->sk_argument; + + /* see comments about NULLs handling in btbuild */ + if (entry->sk_flags & SK_ISNULL) /* key is NULL */ + { + Assert(entry->sk_procedure == NullValueRegProcedure); + keyNull = true; + if (isNull) + compare = (strat == BTEqualStrategyNumber) ? true : false; + else + compare = (strat == BTGreaterStrategyNumber) ? true : false; + } + else if (isNull) /* key is NOT_NULL and item is NULL */ + { + keyNull = false; + compare = (strat == BTLessStrategyNumber) ? true : false; + } + else + { + keyNull = false; + compare = _bt_invokestrat(rel, i, strat, keyDatum, attrDatum); + } + + if (compare) /* true for one of ">, <, =" */ + { + if (strat != BTEqualStrategyNumber) + return (true); + } + else +/* false for one of ">, <, =" */ + { + if (strat == BTEqualStrategyNumber) + return (false); + + /* + * if original strat was "<=, >=" OR "<, >" but some + * attribute(s) left - need to test for Equality + */ + if (useEqual || i < keysz) + { + if (keyNull || isNull) + compare = (keyNull && isNull) ? true : false; + else + compare = _bt_invokestrat(rel, i, BTEqualStrategyNumber, + keyDatum, attrDatum); + if (compare) /* key' and item' attributes are equal */ + continue; /* - try to compare next attributes */ + } + return (false); + } } - } - - return (true); + + return (true); } /* - * _bt_binsrch() -- Do a binary search for a key on a particular page. + * _bt_binsrch() -- Do a binary search for a key on a particular page. * - * The scankey we get has the compare function stored in the procedure - * entry of each data struct. We invoke this regproc to do the - * comparison for every key in the scankey. _bt_binsrch() returns - * the OffsetNumber of the first matching key on the page, or the - * OffsetNumber at which the matching key would appear if it were - * on this page. + * The scankey we get has the compare function stored in the procedure + * entry of each data struct. We invoke this regproc to do the + * comparison for every key in the scankey. _bt_binsrch() returns + * the OffsetNumber of the first matching key on the page, or the + * OffsetNumber at which the matching key would appear if it were + * on this page. * - * By the time this procedure is called, we're sure we're looking - * at the right page -- don't need to walk right. _bt_binsrch() has - * no lock or refcount side effects on the buffer. + * By the time this procedure is called, we're sure we're looking + * at the right page -- don't need to walk right. _bt_binsrch() has + * no lock or refcount side effects on the buffer. */ OffsetNumber _bt_binsrch(Relation rel, - Buffer buf, - int keysz, - ScanKey scankey, - int srchtype) + Buffer buf, + int keysz, + ScanKey scankey, + int srchtype) { - TupleDesc itupdesc; - Page page; - BTPageOpaque opaque; - OffsetNumber low, mid, high; - int natts = rel->rd_rel->relnatts; - int result; - - itupdesc = RelationGetTupleDescriptor(rel); - page = BufferGetPage(buf); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - - /* by convention, item 1 on any non-rightmost page is the high key */ - low = mid = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - - high = PageGetMaxOffsetNumber(page); - - /* - * Since for non-rightmost pages, the first item on the page is the - * high key, there are two notions of emptiness. One is if nothing - * appears on the page. The other is if nothing but the high key does. - * The reason we test high <= low, rather than high == low, is that - * after vacuuming there may be nothing *but* the high key on a page. - * In that case, given the scheme above, low = 2 and high = 1. - */ - - if ( PageIsEmpty (page) ) - return (low); - if ( (! P_RIGHTMOST(opaque) && high <= low)) - { - if ( high < low || - (srchtype == BT_DESCENT && !(opaque->btpo_flags & BTP_LEAF)) ) - return (low); - /* It's insertion and high == low == 2 */ - result = _bt_compare(rel, itupdesc, page, keysz, scankey, low); - if ( result > 0 ) - return ( OffsetNumberNext (low) ); - return (low); - } - - while ((high - low) > 1) { - mid = low + ((high - low) / 2); - result = _bt_compare(rel, itupdesc, page, keysz, scankey, mid); - - if (result > 0) - low = mid; - else if (result < 0) - high = mid - 1; - else + TupleDesc itupdesc; + Page page; + BTPageOpaque opaque; + OffsetNumber low, + mid, + high; + int natts = rel->rd_rel->relnatts; + int result; + + itupdesc = RelationGetTupleDescriptor(rel); + page = BufferGetPage(buf); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + /* by convention, item 1 on any non-rightmost page is the high key */ + low = mid = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + high = PageGetMaxOffsetNumber(page); + + /* + * Since for non-rightmost pages, the first item on the page is the + * high key, there are two notions of emptiness. One is if nothing + * appears on the page. The other is if nothing but the high key + * does. The reason we test high <= low, rather than high == low, is + * that after vacuuming there may be nothing *but* the high key on a + * page. In that case, given the scheme above, low = 2 and high = 1. + */ + + if (PageIsEmpty(page)) + return (low); + if ((!P_RIGHTMOST(opaque) && high <= low)) { - mid = _bt_firsteq(rel, itupdesc, page, keysz, scankey, mid); - /* - * NOTE for multi-column indices: we may do scan using - * keys not for all attrs. But we handle duplicates using - * all attrs in _bt_insert/_bt_spool code. And so while - * searching on internal pages having number of attrs > keysize - * we want to point at the last item < the scankey, not at the - * first item = the scankey (!!!), and let _bt_moveright - * decide later whether to move right or not (see comments and - * example there). Note also that INSERTions are not affected - * by this code (natts == keysz). - vadim 04/15/97 - */ - if ( natts == keysz || opaque->btpo_flags & BTP_LEAF ) - return (mid); - low = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - if ( mid == low ) - return (mid); - return (OffsetNumberPrev(mid)); + if (high < low || + (srchtype == BT_DESCENT && !(opaque->btpo_flags & BTP_LEAF))) + return (low); + /* It's insertion and high == low == 2 */ + result = _bt_compare(rel, itupdesc, page, keysz, scankey, low); + if (result > 0) + return (OffsetNumberNext(low)); + return (low); } - } - - /* - * We terminated because the endpoints got too close together. There - * are two cases to take care of. - * - * For non-insertion searches on internal pages, we want to point at - * the last key <, or first key =, the scankey on the page. This - * guarantees that we'll descend the tree correctly. - * (NOTE comments above for multi-column indices). - * - * For all other cases, we want to point at the first key >= - * the scankey on the page. This guarantees that scans and - * insertions will happen correctly. - */ - - if (!(opaque->btpo_flags & BTP_LEAF) && srchtype == BT_DESCENT) - { /* - * We want the last key <, or first key ==, the scan key. - */ - result = _bt_compare(rel, itupdesc, page, keysz, scankey, high); - - if (result == 0) + + while ((high - low) > 1) { - mid = _bt_firsteq(rel, itupdesc, page, keysz, scankey, high); - /* - * If natts > keysz we want last item < the scan key. - * See comments above for multi-column indices. - */ - if ( natts == keysz ) - return (mid); - low = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - if ( mid == low ) - return (mid); - return (OffsetNumberPrev(mid)); + mid = low + ((high - low) / 2); + result = _bt_compare(rel, itupdesc, page, keysz, scankey, mid); + + if (result > 0) + low = mid; + else if (result < 0) + high = mid - 1; + else + { + mid = _bt_firsteq(rel, itupdesc, page, keysz, scankey, mid); + + /* + * NOTE for multi-column indices: we may do scan using keys + * not for all attrs. But we handle duplicates using all attrs + * in _bt_insert/_bt_spool code. And so while searching on + * internal pages having number of attrs > keysize we want to + * point at the last item < the scankey, not at the first item + * = the scankey (!!!), and let _bt_moveright decide later + * whether to move right or not (see comments and example + * there). Note also that INSERTions are not affected by this + * code (natts == keysz). - vadim 04/15/97 + */ + if (natts == keysz || opaque->btpo_flags & BTP_LEAF) + return (mid); + low = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + if (mid == low) + return (mid); + return (OffsetNumberPrev(mid)); + } + } + + /* + * We terminated because the endpoints got too close together. There + * are two cases to take care of. + * + * For non-insertion searches on internal pages, we want to point at the + * last key <, or first key =, the scankey on the page. This + * guarantees that we'll descend the tree correctly. (NOTE comments + * above for multi-column indices). + * + * For all other cases, we want to point at the first key >= the scankey + * on the page. This guarantees that scans and insertions will happen + * correctly. + */ + + if (!(opaque->btpo_flags & BTP_LEAF) && srchtype == BT_DESCENT) + { /* We want the last key <, or first key + * ==, the scan key. */ + result = _bt_compare(rel, itupdesc, page, keysz, scankey, high); + + if (result == 0) + { + mid = _bt_firsteq(rel, itupdesc, page, keysz, scankey, high); + + /* + * If natts > keysz we want last item < the scan key. See + * comments above for multi-column indices. + */ + if (natts == keysz) + return (mid); + low = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + if (mid == low) + return (mid); + return (OffsetNumberPrev(mid)); + } + else if (result > 0) + return (high); + else + return (low); } - else if (result > 0) - return (high); - else - return (low); - } - else /* we want the first key >= the scan key */ - { - result = _bt_compare(rel, itupdesc, page, keysz, scankey, low); - if (result <= 0) - return (low); else +/* we want the first key >= the scan key */ { - if (low == high) - return (OffsetNumberNext(low)); - - result = _bt_compare(rel, itupdesc, page, keysz, scankey, high); - if (result <= 0) - return (high); - else - return (OffsetNumberNext(high)); + result = _bt_compare(rel, itupdesc, page, keysz, scankey, low); + if (result <= 0) + return (low); + else + { + if (low == high) + return (OffsetNumberNext(low)); + + result = _bt_compare(rel, itupdesc, page, keysz, scankey, high); + if (result <= 0) + return (high); + else + return (OffsetNumberNext(high)); + } } - } } -static OffsetNumber +static OffsetNumber _bt_firsteq(Relation rel, - TupleDesc itupdesc, - Page page, - Size keysz, - ScanKey scankey, - OffsetNumber offnum) + TupleDesc itupdesc, + Page page, + Size keysz, + ScanKey scankey, + OffsetNumber offnum) { - BTPageOpaque opaque; - OffsetNumber limit; - - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - - /* skip the high key, if any */ - limit = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - - /* walk backwards looking for the first key in the chain of duplicates */ - while (offnum > limit - && _bt_compare(rel, itupdesc, page, - keysz, scankey, OffsetNumberPrev(offnum)) == 0) { - offnum = OffsetNumberPrev(offnum); - } - - return (offnum); + BTPageOpaque opaque; + OffsetNumber limit; + + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + /* skip the high key, if any */ + limit = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + /* walk backwards looking for the first key in the chain of duplicates */ + while (offnum > limit + && _bt_compare(rel, itupdesc, page, + keysz, scankey, OffsetNumberPrev(offnum)) == 0) + { + offnum = OffsetNumberPrev(offnum); + } + + return (offnum); } /* - * _bt_compare() -- Compare scankey to a particular tuple on the page. + * _bt_compare() -- Compare scankey to a particular tuple on the page. * - * This routine returns: - * -1 if scankey < tuple at offnum; - * 0 if scankey == tuple at offnum; - * +1 if scankey > tuple at offnum. + * This routine returns: + * -1 if scankey < tuple at offnum; + * 0 if scankey == tuple at offnum; + * +1 if scankey > tuple at offnum. * - * -- Old comments: - * In order to avoid having to propagate changes up the tree any time - * a new minimal key is inserted, the leftmost entry on the leftmost - * page is less than all possible keys, by definition. + * -- Old comments: + * In order to avoid having to propagate changes up the tree any time + * a new minimal key is inserted, the leftmost entry on the leftmost + * page is less than all possible keys, by definition. * - * -- New ones: - * New insertion code (fix against updating _in_place_ if new minimal - * key has bigger size than old one) may delete P_HIKEY entry on the - * root page in order to insert new minimal key - and so this definition - * does not work properly in this case and breaks key' order on root - * page. BTW, this propagation occures only while page' splitting, - * but not "any time a new min key is inserted" (see _bt_insertonpg). - * - vadim 12/05/96 + * -- New ones: + * New insertion code (fix against updating _in_place_ if new minimal + * key has bigger size than old one) may delete P_HIKEY entry on the + * root page in order to insert new minimal key - and so this definition + * does not work properly in this case and breaks key' order on root + * page. BTW, this propagation occures only while page' splitting, + * but not "any time a new min key is inserted" (see _bt_insertonpg). + * - vadim 12/05/96 */ static int _bt_compare(Relation rel, - TupleDesc itupdesc, - Page page, - int keysz, - ScanKey scankey, - OffsetNumber offnum) + TupleDesc itupdesc, + Page page, + int keysz, + ScanKey scankey, + OffsetNumber offnum) { - Datum datum; - BTItem btitem; - ItemId itemid; - IndexTuple itup; - BTPageOpaque opaque; - ScanKey entry; - AttrNumber attno; - int result; - int i; - bool null; - - /* - * If this is a leftmost internal page, and if our comparison is - * with the first key on the page, then the item at that position is - * by definition less than the scan key. - * - * - see new comments above... - */ - - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - if (!(opaque->btpo_flags & BTP_LEAF) - && P_LEFTMOST(opaque) - && offnum == P_HIKEY) { - itemid = PageGetItemId(page, offnum); - + Datum datum; + BTItem btitem; + ItemId itemid; + IndexTuple itup; + BTPageOpaque opaque; + ScanKey entry; + AttrNumber attno; + int result; + int i; + bool null; + /* - * we just have to believe that this will only be called with - * offnum == P_HIKEY when P_HIKEY is the OffsetNumber of the - * first actual data key (i.e., this is also a rightmost - * page). there doesn't seem to be any code that implies - * that the leftmost page is normally missing a high key as - * well as the rightmost page. but that implies that this - * code path only applies to the root -- which seems - * unlikely.. + * If this is a leftmost internal page, and if our comparison is with + * the first key on the page, then the item at that position is by + * definition less than the scan key. * - * - see new comments above... + * - see new comments above... */ - if (! P_RIGHTMOST(opaque)) { - elog(WARN, "_bt_compare: invalid comparison to high key"); - } + + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + if (!(opaque->btpo_flags & BTP_LEAF) + && P_LEFTMOST(opaque) + && offnum == P_HIKEY) + { + itemid = PageGetItemId(page, offnum); + + /* + * we just have to believe that this will only be called with + * offnum == P_HIKEY when P_HIKEY is the OffsetNumber of the first + * actual data key (i.e., this is also a rightmost page). there + * doesn't seem to be any code that implies that the leftmost page + * is normally missing a high key as well as the rightmost page. + * but that implies that this code path only applies to the root + * -- which seems unlikely.. + * + * - see new comments above... + */ + if (!P_RIGHTMOST(opaque)) + { + elog(WARN, "_bt_compare: invalid comparison to high key"); + } #if 0 + + /* + * We just have to belive that right answer will not break + * anything. I've checked code and all seems to be ok. See new + * comments above... + * + * -- Old comments If the item on the page is equal to the scankey, + * that's okay to admit. We just can't claim that the first key + * on the page is greater than anything. + */ + + if (_bt_skeycmp(rel, keysz, scankey, page, itemid, + BTEqualStrategyNumber)) + { + return (0); + } + return (1); +#endif + } + + btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &(btitem->bti_itup); + /* - * We just have to belive that right answer will not - * break anything. I've checked code and all seems to be ok. - * See new comments above... + * The scan key is set up with the attribute number associated with + * each term in the key. It is important that, if the index is + * multi-key, the scan contain the first k key attributes, and that + * they be in order. If you think about how multi-key ordering works, + * you'll understand why this is. * - * -- Old comments - * If the item on the page is equal to the scankey, that's - * okay to admit. We just can't claim that the first key on - * the page is greater than anything. + * We don't test for violation of this condition here. */ - - if (_bt_skeycmp(rel, keysz, scankey, page, itemid, - BTEqualStrategyNumber)) { - return (0); - } - return (1); -#endif - } - - btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &(btitem->bti_itup); - - /* - * The scan key is set up with the attribute number associated with each - * term in the key. It is important that, if the index is multi-key, - * the scan contain the first k key attributes, and that they be in - * order. If you think about how multi-key ordering works, you'll - * understand why this is. - * - * We don't test for violation of this condition here. - */ - - for (i = 1; i <= keysz; i++) { - long tmpres; - - entry = &scankey[i - 1]; - attno = entry->sk_attno; - datum = index_getattr(itup, attno, itupdesc, &null); - - /* see comments about NULLs handling in btbuild */ - if ( entry->sk_flags & SK_ISNULL ) /* key is NULL */ + + for (i = 1; i <= keysz; i++) { - Assert ( entry->sk_procedure == NullValueRegProcedure ); - if ( null ) - tmpres = (long) 0; /* NULL "=" NULL */ - else - tmpres = (long) 1; /* NULL ">" NOT_NULL */ - } - else if ( null ) /* key is NOT_NULL and item is NULL */ - { - tmpres = (long) -1; /* NOT_NULL "<" NULL */ - } - else - { - tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, - entry->sk_argument, datum); + long tmpres; + + entry = &scankey[i - 1]; + attno = entry->sk_attno; + datum = index_getattr(itup, attno, itupdesc, &null); + + /* see comments about NULLs handling in btbuild */ + if (entry->sk_flags & SK_ISNULL) /* key is NULL */ + { + Assert(entry->sk_procedure == NullValueRegProcedure); + if (null) + tmpres = (long) 0; /* NULL "=" NULL */ + else + tmpres = (long) 1; /* NULL ">" NOT_NULL */ + } + else if (null) /* key is NOT_NULL and item is NULL */ + { + tmpres = (long) -1; /* NOT_NULL "<" NULL */ + } + else + { + tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure, + entry->sk_argument, datum); + } + result = tmpres; + + /* if the keys are unequal, return the difference */ + if (result != 0) + return (result); } - result = tmpres; - - /* if the keys are unequal, return the difference */ - if (result != 0) - return (result); - } - - /* by here, the keys are equal */ - return (0); + + /* by here, the keys are equal */ + return (0); } /* - * _bt_next() -- Get the next item in a scan. + * _bt_next() -- Get the next item in a scan. * - * On entry, we have a valid currentItemData in the scan, and a - * read lock on the page that contains that item. We do not have - * the page pinned. We return the next item in the scan. On - * exit, we have the page containing the next item locked but not - * pinned. + * On entry, we have a valid currentItemData in the scan, and a + * read lock on the page that contains that item. We do not have + * the page pinned. We return the next item in the scan. On + * exit, we have the page containing the next item locked but not + * pinned. */ RetrieveIndexResult _bt_next(IndexScanDesc scan, ScanDirection dir) { - Relation rel; - Buffer buf; - Page page; - OffsetNumber offnum; - RetrieveIndexResult res; - ItemPointer current; - BTItem btitem; - IndexTuple itup; - BTScanOpaque so; - Size keysok; - - rel = scan->relation; - so = (BTScanOpaque) scan->opaque; - current = &(scan->currentItemData); - - /* - * XXX 10 may 91: somewhere there's a bug in our management of the - * cached buffer for this scan. wei discovered it. the following - * is a workaround so he can work until i figure out what's going on. - */ - - if (!BufferIsValid(so->btso_curbuf)) - so->btso_curbuf = _bt_getbuf(rel, ItemPointerGetBlockNumber(current), - BT_READ); - - /* we still have the buffer pinned and locked */ - buf = so->btso_curbuf; - - do - { - /* step one tuple in the appropriate direction */ - if (!_bt_step(scan, &buf, dir)) - return ((RetrieveIndexResult) NULL); - - /* by here, current is the tuple we want to return */ - offnum = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &btitem->bti_itup; - - if ( _bt_checkkeys (scan, itup, &keysok) ) - { - Assert (keysok == so->numberOfKeys); - res = FormRetrieveIndexResult(current, &(itup->t_tid)); - - /* remember which buffer we have pinned and locked */ - so->btso_curbuf = buf; - return (res); - } + Relation rel; + Buffer buf; + Page page; + OffsetNumber offnum; + RetrieveIndexResult res; + ItemPointer current; + BTItem btitem; + IndexTuple itup; + BTScanOpaque so; + Size keysok; + + rel = scan->relation; + so = (BTScanOpaque) scan->opaque; + current = &(scan->currentItemData); + + /* + * XXX 10 may 91: somewhere there's a bug in our management of the + * cached buffer for this scan. wei discovered it. the following is + * a workaround so he can work until i figure out what's going on. + */ + + if (!BufferIsValid(so->btso_curbuf)) + so->btso_curbuf = _bt_getbuf(rel, ItemPointerGetBlockNumber(current), + BT_READ); + + /* we still have the buffer pinned and locked */ + buf = so->btso_curbuf; + + do + { + /* step one tuple in the appropriate direction */ + if (!_bt_step(scan, &buf, dir)) + return ((RetrieveIndexResult) NULL); - } while ( keysok >= so->numberOfFirstKeys ); + /* by here, current is the tuple we want to return */ + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &btitem->bti_itup; + + if (_bt_checkkeys(scan, itup, &keysok)) + { + Assert(keysok == so->numberOfKeys); + res = FormRetrieveIndexResult(current, &(itup->t_tid)); + + /* remember which buffer we have pinned and locked */ + so->btso_curbuf = buf; + return (res); + } + + } while (keysok >= so->numberOfFirstKeys); - ItemPointerSetInvalid(current); - so->btso_curbuf = InvalidBuffer; - _bt_relbuf(rel, buf, BT_READ); - - return ((RetrieveIndexResult) NULL); + ItemPointerSetInvalid(current); + so->btso_curbuf = InvalidBuffer; + _bt_relbuf(rel, buf, BT_READ); + + return ((RetrieveIndexResult) NULL); } /* - * _bt_first() -- Find the first item in a scan. + * _bt_first() -- Find the first item in a scan. * - * We need to be clever about the type of scan, the operation it's - * performing, and the tree ordering. We return the RetrieveIndexResult - * of the first item in the tree that satisfies the qualification - * associated with the scan descriptor. On exit, the page containing - * the current index tuple is read locked and pinned, and the scan's - * opaque data entry is updated to include the buffer. + * We need to be clever about the type of scan, the operation it's + * performing, and the tree ordering. We return the RetrieveIndexResult + * of the first item in the tree that satisfies the qualification + * associated with the scan descriptor. On exit, the page containing + * the current index tuple is read locked and pinned, and the scan's + * opaque data entry is updated to include the buffer. */ RetrieveIndexResult _bt_first(IndexScanDesc scan, ScanDirection dir) { - Relation rel; - TupleDesc itupdesc; - Buffer buf; - Page page; - BTPageOpaque pop; - BTStack stack; - OffsetNumber offnum, maxoff; - bool offGmax = false; - BTItem btitem; - IndexTuple itup; - ItemPointer current; - BlockNumber blkno; - StrategyNumber strat; - RetrieveIndexResult res; - RegProcedure proc; - int result; - BTScanOpaque so; - ScanKeyData skdata; - Size keysok; - - rel = scan->relation; - so = (BTScanOpaque) scan->opaque; - - /* - * Order the keys in the qualification and be sure - * that the scan exploits the tree order. - */ - so->numberOfFirstKeys = 0; /* may be changed by _bt_orderkeys */ - so->qual_ok = 1; /* may be changed by _bt_orderkeys */ - scan->scanFromEnd = false; - if ( so->numberOfKeys > 0 ) - { - _bt_orderkeys(rel, so); - - strat = _bt_getstrat(rel, 1, so->keyData[0].sk_procedure); + Relation rel; + TupleDesc itupdesc; + Buffer buf; + Page page; + BTPageOpaque pop; + BTStack stack; + OffsetNumber offnum, + maxoff; + bool offGmax = false; + BTItem btitem; + IndexTuple itup; + ItemPointer current; + BlockNumber blkno; + StrategyNumber strat; + RetrieveIndexResult res; + RegProcedure proc; + int result; + BTScanOpaque so; + ScanKeyData skdata; + Size keysok; - /* NOTE: it assumes ForwardScanDirection */ - if ( strat == BTLessStrategyNumber || - strat == BTLessEqualStrategyNumber ) - scan->scanFromEnd = true; - } - else - scan->scanFromEnd = true; - - if ( so->qual_ok == 0 ) - return ((RetrieveIndexResult) NULL); - - /* if we just need to walk down one edge of the tree, do that */ - if (scan->scanFromEnd) - return (_bt_endpoint(scan, dir)); - - itupdesc = RelationGetTupleDescriptor(rel); - current = &(scan->currentItemData); - - /* - * Okay, we want something more complicated. What we'll do is use - * the first item in the scan key passed in (which has been correctly - * ordered to take advantage of index ordering) to position ourselves - * at the right place in the scan. - */ - /* _bt_orderkeys disallows it, but it's place to add some code latter */ - if ( so->keyData[0].sk_flags & SK_ISNULL ) - { - elog (WARN, "_bt_first: btree doesn't support is(not)null, yet"); - return ((RetrieveIndexResult) NULL); - } - proc = index_getprocid(rel, 1, BTORDER_PROC); - ScanKeyEntryInitialize(&skdata, so->keyData[0].sk_flags, 1, proc, - so->keyData[0].sk_argument); - - stack = _bt_search(rel, 1, &skdata, &buf); - _bt_freestack(stack); - - blkno = BufferGetBlockNumber(buf); - page = BufferGetPage(buf); - - /* - * This will happen if the tree we're searching is entirely empty, - * or if we're doing a search for a key that would appear on an - * entirely empty internal page. In either case, there are no - * matching tuples in the index. - */ - - if (PageIsEmpty(page)) { - ItemPointerSetInvalid(current); - so->btso_curbuf = InvalidBuffer; - _bt_relbuf(rel, buf, BT_READ); - return ((RetrieveIndexResult) NULL); - } - maxoff = PageGetMaxOffsetNumber(page); - pop = (BTPageOpaque) PageGetSpecialPointer(page); - - /* - * Now _bt_moveright doesn't move from non-rightmost leaf page - * if scankey == hikey and there is only hikey there. It's - * good for insertion, but we need to do work for scan here. - * - vadim 05/27/97 - */ - - while ( maxoff == P_HIKEY && !P_RIGHTMOST(pop) && - _bt_skeycmp(rel, 1, &skdata, page, - PageGetItemId(page, P_HIKEY), - BTGreaterEqualStrategyNumber) ) - { - /* step right one page */ - blkno = pop->btpo_next; - _bt_relbuf(rel, buf, BT_READ); - buf = _bt_getbuf(rel, blkno, BT_READ); + rel = scan->relation; + so = (BTScanOpaque) scan->opaque; + + /* + * Order the keys in the qualification and be sure that the scan + * exploits the tree order. + */ + so->numberOfFirstKeys = 0; /* may be changed by _bt_orderkeys */ + so->qual_ok = 1; /* may be changed by _bt_orderkeys */ + scan->scanFromEnd = false; + if (so->numberOfKeys > 0) + { + _bt_orderkeys(rel, so); + + strat = _bt_getstrat(rel, 1, so->keyData[0].sk_procedure); + + /* NOTE: it assumes ForwardScanDirection */ + if (strat == BTLessStrategyNumber || + strat == BTLessEqualStrategyNumber) + scan->scanFromEnd = true; + } + else + scan->scanFromEnd = true; + + if (so->qual_ok == 0) + return ((RetrieveIndexResult) NULL); + + /* if we just need to walk down one edge of the tree, do that */ + if (scan->scanFromEnd) + return (_bt_endpoint(scan, dir)); + + itupdesc = RelationGetTupleDescriptor(rel); + current = &(scan->currentItemData); + + /* + * Okay, we want something more complicated. What we'll do is use the + * first item in the scan key passed in (which has been correctly + * ordered to take advantage of index ordering) to position ourselves + * at the right place in the scan. + */ + /* _bt_orderkeys disallows it, but it's place to add some code latter */ + if (so->keyData[0].sk_flags & SK_ISNULL) + { + elog(WARN, "_bt_first: btree doesn't support is(not)null, yet"); + return ((RetrieveIndexResult) NULL); + } + proc = index_getprocid(rel, 1, BTORDER_PROC); + ScanKeyEntryInitialize(&skdata, so->keyData[0].sk_flags, 1, proc, + so->keyData[0].sk_argument); + + stack = _bt_search(rel, 1, &skdata, &buf); + _bt_freestack(stack); + + blkno = BufferGetBlockNumber(buf); page = BufferGetPage(buf); - if (PageIsEmpty(page)) { - ItemPointerSetInvalid(current); - so->btso_curbuf = InvalidBuffer; - _bt_relbuf(rel, buf, BT_READ); - return ((RetrieveIndexResult) NULL); + + /* + * This will happen if the tree we're searching is entirely empty, or + * if we're doing a search for a key that would appear on an entirely + * empty internal page. In either case, there are no matching tuples + * in the index. + */ + + if (PageIsEmpty(page)) + { + ItemPointerSetInvalid(current); + so->btso_curbuf = InvalidBuffer; + _bt_relbuf(rel, buf, BT_READ); + return ((RetrieveIndexResult) NULL); } - maxoff = PageGetMaxOffsetNumber(page); + maxoff = PageGetMaxOffsetNumber(page); pop = (BTPageOpaque) PageGetSpecialPointer(page); - } - - - /* find the nearest match to the manufactured scan key on the page */ - offnum = _bt_binsrch(rel, buf, 1, &skdata, BT_DESCENT); - - if (offnum > maxoff) - { - offnum = maxoff; - offGmax = true; - } - - ItemPointerSet(current, blkno, offnum); - - /* - * Now find the right place to start the scan. Result is the - * value we're looking for minus the value we're looking at - * in the index. - */ - - result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); - - /* it's yet other place to add some code latter for is(not)null */ - - strat = _bt_getstrat(rel, 1, so->keyData[0].sk_procedure); - - switch (strat) { - case BTLessStrategyNumber: - if (result <= 0) { - do { - if (!_bt_twostep(scan, &buf, BackwardScanDirection)) - break; - - offnum = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); - } while (result <= 0); - - /* if this is true, the key we just looked at is gone */ - if (result > 0) - _bt_twostep(scan, &buf, ForwardScanDirection); - } - break; - - case BTLessEqualStrategyNumber: - if (result >= 0) { - do { - if (!_bt_twostep(scan, &buf, ForwardScanDirection)) - break; - - offnum = ItemPointerGetOffsetNumber(current); + + /* + * Now _bt_moveright doesn't move from non-rightmost leaf page if + * scankey == hikey and there is only hikey there. It's good for + * insertion, but we need to do work for scan here. - vadim 05/27/97 + */ + + while (maxoff == P_HIKEY && !P_RIGHTMOST(pop) && + _bt_skeycmp(rel, 1, &skdata, page, + PageGetItemId(page, P_HIKEY), + BTGreaterEqualStrategyNumber)) + { + /* step right one page */ + blkno = pop->btpo_next; + _bt_relbuf(rel, buf, BT_READ); + buf = _bt_getbuf(rel, blkno, BT_READ); page = BufferGetPage(buf); - result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); - } while (result >= 0); - - if (result < 0) - _bt_twostep(scan, &buf, BackwardScanDirection); + if (PageIsEmpty(page)) + { + ItemPointerSetInvalid(current); + so->btso_curbuf = InvalidBuffer; + _bt_relbuf(rel, buf, BT_READ); + return ((RetrieveIndexResult) NULL); + } + maxoff = PageGetMaxOffsetNumber(page); + pop = (BTPageOpaque) PageGetSpecialPointer(page); } - break; - - case BTEqualStrategyNumber: - if (result != 0) { - _bt_relbuf(scan->relation, buf, BT_READ); - so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(&(scan->currentItemData)); - return ((RetrieveIndexResult) NULL); + + + /* find the nearest match to the manufactured scan key on the page */ + offnum = _bt_binsrch(rel, buf, 1, &skdata, BT_DESCENT); + + if (offnum > maxoff) + { + offnum = maxoff; + offGmax = true; } - break; - - case BTGreaterEqualStrategyNumber: - if ( offGmax ) + + ItemPointerSet(current, blkno, offnum); + + /* + * Now find the right place to start the scan. Result is the value + * we're looking for minus the value we're looking at in the index. + */ + + result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); + + /* it's yet other place to add some code latter for is(not)null */ + + strat = _bt_getstrat(rel, 1, so->keyData[0].sk_procedure); + + switch (strat) { - if (result < 0) - { - Assert ( !P_RIGHTMOST(pop) && maxoff == P_HIKEY ); - if ( !_bt_step(scan, &buf, ForwardScanDirection) ) - { - _bt_relbuf(scan->relation, buf, BT_READ); - so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(&(scan->currentItemData)); - return ((RetrieveIndexResult) NULL); + case BTLessStrategyNumber: + if (result <= 0) + { + do + { + if (!_bt_twostep(scan, &buf, BackwardScanDirection)) + break; + + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); + } while (result <= 0); + + /* if this is true, the key we just looked at is gone */ + if (result > 0) + _bt_twostep(scan, &buf, ForwardScanDirection); } - } - else if (result > 0) - { /* - * Just remember: _bt_binsrch() returns the OffsetNumber of - * the first matching key on the page, or the OffsetNumber at - * which the matching key WOULD APPEAR IF IT WERE on this page. - * No key on this page, but offnum from _bt_binsrch() greater - * maxoff - have to move right. - vadim 12/06/96 - */ - _bt_twostep(scan, &buf, ForwardScanDirection); - } + break; + + case BTLessEqualStrategyNumber: + if (result >= 0) + { + do + { + if (!_bt_twostep(scan, &buf, ForwardScanDirection)) + break; + + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); + } while (result >= 0); + + if (result < 0) + _bt_twostep(scan, &buf, BackwardScanDirection); + } + break; + + case BTEqualStrategyNumber: + if (result != 0) + { + _bt_relbuf(scan->relation, buf, BT_READ); + so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(&(scan->currentItemData)); + return ((RetrieveIndexResult) NULL); + } + break; + + case BTGreaterEqualStrategyNumber: + if (offGmax) + { + if (result < 0) + { + Assert(!P_RIGHTMOST(pop) && maxoff == P_HIKEY); + if (!_bt_step(scan, &buf, ForwardScanDirection)) + { + _bt_relbuf(scan->relation, buf, BT_READ); + so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(&(scan->currentItemData)); + return ((RetrieveIndexResult) NULL); + } + } + else if (result > 0) + { /* Just remember: _bt_binsrch() returns + * the OffsetNumber of the first matching + * key on the page, or the OffsetNumber at + * which the matching key WOULD APPEAR IF + * IT WERE on this page. No key on this + * page, but offnum from _bt_binsrch() + * greater maxoff - have to move right. - + * vadim 12/06/96 */ + _bt_twostep(scan, &buf, ForwardScanDirection); + } + } + else if (result < 0) + { + do + { + if (!_bt_twostep(scan, &buf, BackwardScanDirection)) + break; + + page = BufferGetPage(buf); + offnum = ItemPointerGetOffsetNumber(current); + result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); + } while (result < 0); + + if (result > 0) + _bt_twostep(scan, &buf, ForwardScanDirection); + } + break; + + case BTGreaterStrategyNumber: + /* offGmax helps as above */ + if (result >= 0 || offGmax) + { + do + { + if (!_bt_twostep(scan, &buf, ForwardScanDirection)) + break; + + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); + } while (result >= 0); + } + break; } - else if (result < 0) + + /* okay, current item pointer for the scan is right */ + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &btitem->bti_itup; + + if (_bt_checkkeys(scan, itup, &keysok)) { - do { - if (!_bt_twostep(scan, &buf, BackwardScanDirection)) - break; - - page = BufferGetPage(buf); - offnum = ItemPointerGetOffsetNumber(current); - result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); - } while (result < 0); - - if (result > 0) - _bt_twostep(scan, &buf, ForwardScanDirection); + res = FormRetrieveIndexResult(current, &(itup->t_tid)); + + /* remember which buffer we have pinned */ + so->btso_curbuf = buf; } - break; - - case BTGreaterStrategyNumber: - /* offGmax helps as above */ - if (result >= 0 || offGmax) { - do { - if (!_bt_twostep(scan, &buf, ForwardScanDirection)) - break; - - offnum = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum); - } while (result >= 0); + else if (keysok >= so->numberOfFirstKeys) + { + so->btso_curbuf = buf; + return (_bt_next(scan, dir)); } - break; - } - - /* okay, current item pointer for the scan is right */ - offnum = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &btitem->bti_itup; - - if ( _bt_checkkeys (scan, itup, &keysok) ) - { - res = FormRetrieveIndexResult(current, &(itup->t_tid)); - - /* remember which buffer we have pinned */ - so->btso_curbuf = buf; - } - else if ( keysok >= so->numberOfFirstKeys ) - { - so->btso_curbuf = buf; - return (_bt_next (scan, dir)); - } - else - { - ItemPointerSetInvalid(current); - so->btso_curbuf = InvalidBuffer; - _bt_relbuf(rel, buf, BT_READ); - res = (RetrieveIndexResult) NULL; - } - - return (res); + else + { + ItemPointerSetInvalid(current); + so->btso_curbuf = InvalidBuffer; + _bt_relbuf(rel, buf, BT_READ); + res = (RetrieveIndexResult) NULL; + } + + return (res); } /* - * _bt_step() -- Step one item in the requested direction in a scan on - * the tree. + * _bt_step() -- Step one item in the requested direction in a scan on + * the tree. * - * If no adjacent record exists in the requested direction, return - * false. Else, return true and set the currentItemData for the - * scan to the right thing. + * If no adjacent record exists in the requested direction, return + * false. Else, return true and set the currentItemData for the + * scan to the right thing. */ bool -_bt_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir) +_bt_step(IndexScanDesc scan, Buffer * bufP, ScanDirection dir) { - Page page; - BTPageOpaque opaque; - OffsetNumber offnum, maxoff; - OffsetNumber start; - BlockNumber blkno; - BlockNumber obknum; - BTScanOpaque so; - ItemPointer current; - Relation rel; - - rel = scan->relation; - current = &(scan->currentItemData); - offnum = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(*bufP); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - so = (BTScanOpaque) scan->opaque; - maxoff = PageGetMaxOffsetNumber(page); - - /* get the next tuple */ - if (ScanDirectionIsForward(dir)) { - if (!PageIsEmpty(page) && offnum < maxoff) { - offnum = OffsetNumberNext(offnum); - } else { - - /* if we're at end of scan, release the buffer and return */ - blkno = opaque->btpo_next; - if (P_RIGHTMOST(opaque)) { - _bt_relbuf(rel, *bufP, BT_READ); - ItemPointerSetInvalid(current); - *bufP = so->btso_curbuf = InvalidBuffer; - return (false); - } else { - - /* walk right to the next page with data */ - _bt_relbuf(rel, *bufP, BT_READ); - for (;;) { - *bufP = _bt_getbuf(rel, blkno, BT_READ); - page = BufferGetPage(*bufP); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - maxoff = PageGetMaxOffsetNumber(page); - start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - - if (!PageIsEmpty(page) && start <= maxoff) { - break; - } else { + Page page; + BTPageOpaque opaque; + OffsetNumber offnum, + maxoff; + OffsetNumber start; + BlockNumber blkno; + BlockNumber obknum; + BTScanOpaque so; + ItemPointer current; + Relation rel; + + rel = scan->relation; + current = &(scan->currentItemData); + offnum = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(*bufP); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + so = (BTScanOpaque) scan->opaque; + maxoff = PageGetMaxOffsetNumber(page); + + /* get the next tuple */ + if (ScanDirectionIsForward(dir)) + { + if (!PageIsEmpty(page) && offnum < maxoff) + { + offnum = OffsetNumberNext(offnum); + } + else + { + + /* if we're at end of scan, release the buffer and return */ blkno = opaque->btpo_next; - _bt_relbuf(rel, *bufP, BT_READ); - if (blkno == P_NONE) { - *bufP = so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(current); - return (false); + if (P_RIGHTMOST(opaque)) + { + _bt_relbuf(rel, *bufP, BT_READ); + ItemPointerSetInvalid(current); + *bufP = so->btso_curbuf = InvalidBuffer; + return (false); + } + else + { + + /* walk right to the next page with data */ + _bt_relbuf(rel, *bufP, BT_READ); + for (;;) + { + *bufP = _bt_getbuf(rel, blkno, BT_READ); + page = BufferGetPage(*bufP); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + maxoff = PageGetMaxOffsetNumber(page); + start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + if (!PageIsEmpty(page) && start <= maxoff) + { + break; + } + else + { + blkno = opaque->btpo_next; + _bt_relbuf(rel, *bufP, BT_READ); + if (blkno == P_NONE) + { + *bufP = so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(current); + return (false); + } + } + } + offnum = start; } - } } - offnum = start; - } } - } else if (ScanDirectionIsBackward(dir)) { - - /* remember that high key is item zero on non-rightmost pages */ - start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + else if (ScanDirectionIsBackward(dir)) + { - if (offnum > start) { - offnum = OffsetNumberPrev(offnum); - } else { - - /* if we're at end of scan, release the buffer and return */ - blkno = opaque->btpo_prev; - if (P_LEFTMOST(opaque)) { - _bt_relbuf(rel, *bufP, BT_READ); - *bufP = so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(current); - return (false); - } else { - - obknum = BufferGetBlockNumber(*bufP); - - /* walk right to the next page with data */ - _bt_relbuf(rel, *bufP, BT_READ); - for (;;) { - *bufP = _bt_getbuf(rel, blkno, BT_READ); - page = BufferGetPage(*bufP); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - maxoff = PageGetMaxOffsetNumber(page); - - /* - * If the adjacent page just split, then we may have the - * wrong block. Handle this case. Because pages only - * split right, we don't have to worry about this failing - * to terminate. - */ - - while (opaque->btpo_next != obknum) { - blkno = opaque->btpo_next; - _bt_relbuf(rel, *bufP, BT_READ); - *bufP = _bt_getbuf(rel, blkno, BT_READ); - page = BufferGetPage(*bufP); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - maxoff = PageGetMaxOffsetNumber(page); - } - - /* don't consider the high key */ - start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - - /* anything to look at here? */ - if (!PageIsEmpty(page) && maxoff >= start) { - break; - } else { + /* remember that high key is item zero on non-rightmost pages */ + start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + if (offnum > start) + { + offnum = OffsetNumberPrev(offnum); + } + else + { + + /* if we're at end of scan, release the buffer and return */ blkno = opaque->btpo_prev; - obknum = BufferGetBlockNumber(*bufP); - _bt_relbuf(rel, *bufP, BT_READ); - if (blkno == P_NONE) { - *bufP = so->btso_curbuf = InvalidBuffer; - ItemPointerSetInvalid(current); - return (false); + if (P_LEFTMOST(opaque)) + { + _bt_relbuf(rel, *bufP, BT_READ); + *bufP = so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(current); + return (false); + } + else + { + + obknum = BufferGetBlockNumber(*bufP); + + /* walk right to the next page with data */ + _bt_relbuf(rel, *bufP, BT_READ); + for (;;) + { + *bufP = _bt_getbuf(rel, blkno, BT_READ); + page = BufferGetPage(*bufP); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + maxoff = PageGetMaxOffsetNumber(page); + + /* + * If the adjacent page just split, then we may have + * the wrong block. Handle this case. Because pages + * only split right, we don't have to worry about this + * failing to terminate. + */ + + while (opaque->btpo_next != obknum) + { + blkno = opaque->btpo_next; + _bt_relbuf(rel, *bufP, BT_READ); + *bufP = _bt_getbuf(rel, blkno, BT_READ); + page = BufferGetPage(*bufP); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + maxoff = PageGetMaxOffsetNumber(page); + } + + /* don't consider the high key */ + start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + /* anything to look at here? */ + if (!PageIsEmpty(page) && maxoff >= start) + { + break; + } + else + { + blkno = opaque->btpo_prev; + obknum = BufferGetBlockNumber(*bufP); + _bt_relbuf(rel, *bufP, BT_READ); + if (blkno == P_NONE) + { + *bufP = so->btso_curbuf = InvalidBuffer; + ItemPointerSetInvalid(current); + return (false); + } + } + } + offnum = maxoff;/* XXX PageIsEmpty? */ } - } } - offnum = maxoff; /* XXX PageIsEmpty? */ - } } - } - blkno = BufferGetBlockNumber(*bufP); - so->btso_curbuf = *bufP; - ItemPointerSet(current, blkno, offnum); - - return (true); + blkno = BufferGetBlockNumber(*bufP); + so->btso_curbuf = *bufP; + ItemPointerSet(current, blkno, offnum); + + return (true); } /* - * _bt_twostep() -- Move to an adjacent record in a scan on the tree, - * if an adjacent record exists. + * _bt_twostep() -- Move to an adjacent record in a scan on the tree, + * if an adjacent record exists. * - * This is like _bt_step, except that if no adjacent record exists - * it restores us to where we were before trying the step. This is - * only hairy when you cross page boundaries, since the page you cross - * from could have records inserted or deleted, or could even split. - * This is unlikely, but we try to handle it correctly here anyway. + * This is like _bt_step, except that if no adjacent record exists + * it restores us to where we were before trying the step. This is + * only hairy when you cross page boundaries, since the page you cross + * from could have records inserted or deleted, or could even split. + * This is unlikely, but we try to handle it correctly here anyway. * - * This routine contains the only case in which our changes to Lehman - * and Yao's algorithm. + * This routine contains the only case in which our changes to Lehman + * and Yao's algorithm. * - * Like step, this routine leaves the scan's currentItemData in the - * proper state and acquires a lock and pin on *bufP. If the twostep - * succeeded, we return true; otherwise, we return false. + * Like step, this routine leaves the scan's currentItemData in the + * proper state and acquires a lock and pin on *bufP. If the twostep + * succeeded, we return true; otherwise, we return false. */ -static bool -_bt_twostep(IndexScanDesc scan, Buffer *bufP, ScanDirection dir) +static bool +_bt_twostep(IndexScanDesc scan, Buffer * bufP, ScanDirection dir) { - Page page; - BTPageOpaque opaque; - OffsetNumber offnum, maxoff; - OffsetNumber start; - ItemPointer current; - ItemId itemid; - int itemsz; - BTItem btitem; - BTItem svitem; - BlockNumber blkno; - - blkno = BufferGetBlockNumber(*bufP); - page = BufferGetPage(*bufP); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - maxoff = PageGetMaxOffsetNumber(page); - current = &(scan->currentItemData); - offnum = ItemPointerGetOffsetNumber(current); - - start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - - /* if we're safe, just do it */ - if (ScanDirectionIsForward(dir) && offnum < maxoff) { /* XXX PageIsEmpty? */ - ItemPointerSet(current, blkno, OffsetNumberNext(offnum)); - return (true); - } else if (ScanDirectionIsBackward(dir) && offnum > start) { - ItemPointerSet(current, blkno, OffsetNumberPrev(offnum)); - return (true); - } - - /* if we've hit end of scan we don't have to do any work */ - if (ScanDirectionIsForward(dir) && P_RIGHTMOST(opaque)) { - return (false); - } else if (ScanDirectionIsBackward(dir) && P_LEFTMOST(opaque)) { - return (false); - } - - /* - * Okay, it's off the page; let _bt_step() do the hard work, and we'll - * try to remember where we were. This is not guaranteed to work; this - * is the only place in the code where concurrency can screw us up, - * and it's because we want to be able to move in two directions in - * the scan. - */ - - itemid = PageGetItemId(page, offnum); - itemsz = ItemIdGetLength(itemid); - btitem = (BTItem) PageGetItem(page, itemid); - svitem = (BTItem) palloc(itemsz); - memmove((char *) svitem, (char *) btitem, itemsz); - - if (_bt_step(scan, bufP, dir)) { - pfree(svitem); - return (true); - } - - /* try to find our place again */ - *bufP = _bt_getbuf(scan->relation, blkno, BT_READ); - page = BufferGetPage(*bufP); - maxoff = PageGetMaxOffsetNumber(page); - - while (offnum <= maxoff) { + Page page; + BTPageOpaque opaque; + OffsetNumber offnum, + maxoff; + OffsetNumber start; + ItemPointer current; + ItemId itemid; + int itemsz; + BTItem btitem; + BTItem svitem; + BlockNumber blkno; + + blkno = BufferGetBlockNumber(*bufP); + page = BufferGetPage(*bufP); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + maxoff = PageGetMaxOffsetNumber(page); + current = &(scan->currentItemData); + offnum = ItemPointerGetOffsetNumber(current); + + start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + /* if we're safe, just do it */ + if (ScanDirectionIsForward(dir) && offnum < maxoff) + { /* XXX PageIsEmpty? */ + ItemPointerSet(current, blkno, OffsetNumberNext(offnum)); + return (true); + } + else if (ScanDirectionIsBackward(dir) && offnum > start) + { + ItemPointerSet(current, blkno, OffsetNumberPrev(offnum)); + return (true); + } + + /* if we've hit end of scan we don't have to do any work */ + if (ScanDirectionIsForward(dir) && P_RIGHTMOST(opaque)) + { + return (false); + } + else if (ScanDirectionIsBackward(dir) && P_LEFTMOST(opaque)) + { + return (false); + } + + /* + * Okay, it's off the page; let _bt_step() do the hard work, and we'll + * try to remember where we were. This is not guaranteed to work; + * this is the only place in the code where concurrency can screw us + * up, and it's because we want to be able to move in two directions + * in the scan. + */ + itemid = PageGetItemId(page, offnum); + itemsz = ItemIdGetLength(itemid); btitem = (BTItem) PageGetItem(page, itemid); - if ( BTItemSame (btitem, svitem) ) { - pfree(svitem); - ItemPointerSet(current, blkno, offnum); - return (false); + svitem = (BTItem) palloc(itemsz); + memmove((char *) svitem, (char *) btitem, itemsz); + + if (_bt_step(scan, bufP, dir)) + { + pfree(svitem); + return (true); + } + + /* try to find our place again */ + *bufP = _bt_getbuf(scan->relation, blkno, BT_READ); + page = BufferGetPage(*bufP); + maxoff = PageGetMaxOffsetNumber(page); + + while (offnum <= maxoff) + { + itemid = PageGetItemId(page, offnum); + btitem = (BTItem) PageGetItem(page, itemid); + if (BTItemSame(btitem, svitem)) + { + pfree(svitem); + ItemPointerSet(current, blkno, offnum); + return (false); + } } - } - - /* - * XXX crash and burn -- can't find our place. We can be a little - * smarter -- walk to the next page to the right, for example, since - * that's the only direction that splits happen in. Deletions screw - * us up less often since they're only done by the vacuum daemon. - */ - - elog(WARN, "btree synchronization error: concurrent update botched scan"); - - return (false); + + /* + * XXX crash and burn -- can't find our place. We can be a little + * smarter -- walk to the next page to the right, for example, since + * that's the only direction that splits happen in. Deletions screw + * us up less often since they're only done by the vacuum daemon. + */ + + elog(WARN, "btree synchronization error: concurrent update botched scan"); + + return (false); } /* - * _bt_endpoint() -- Find the first or last key in the index. + * _bt_endpoint() -- Find the first or last key in the index. */ -static RetrieveIndexResult +static RetrieveIndexResult _bt_endpoint(IndexScanDesc scan, ScanDirection dir) { - Relation rel; - Buffer buf; - Page page; - BTPageOpaque opaque; - ItemPointer current; - OffsetNumber offnum, maxoff; - OffsetNumber start = 0; - BlockNumber blkno; - BTItem btitem; - IndexTuple itup; - BTScanOpaque so; - RetrieveIndexResult res; - Size keysok; - - rel = scan->relation; - current = &(scan->currentItemData); - so = (BTScanOpaque) scan->opaque; - - buf = _bt_getroot(rel, BT_READ); - blkno = BufferGetBlockNumber(buf); - page = BufferGetPage(buf); - opaque = (BTPageOpaque) PageGetSpecialPointer(page); - - for (;;) { - if (opaque->btpo_flags & BTP_LEAF) - break; - - if (ScanDirectionIsForward(dir)) { - offnum = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - } else { - offnum = PageGetMaxOffsetNumber(page); - } - - btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); - itup = &(btitem->bti_itup); - - blkno = ItemPointerGetBlockNumber(&(itup->t_tid)); - - _bt_relbuf(rel, buf, BT_READ); - buf = _bt_getbuf(rel, blkno, BT_READ); + Relation rel; + Buffer buf; + Page page; + BTPageOpaque opaque; + ItemPointer current; + OffsetNumber offnum, + maxoff; + OffsetNumber start = 0; + BlockNumber blkno; + BTItem btitem; + IndexTuple itup; + BTScanOpaque so; + RetrieveIndexResult res; + Size keysok; + + rel = scan->relation; + current = &(scan->currentItemData); + so = (BTScanOpaque) scan->opaque; + + buf = _bt_getroot(rel, BT_READ); + blkno = BufferGetBlockNumber(buf); page = BufferGetPage(buf); opaque = (BTPageOpaque) PageGetSpecialPointer(page); - - /* - * Race condition: If the child page we just stepped onto is - * in the process of being split, we need to make sure we're - * all the way at the right edge of the tree. See the paper - * by Lehman and Yao. - */ - - if (ScanDirectionIsBackward(dir) && ! P_RIGHTMOST(opaque)) { - do { - blkno = opaque->btpo_next; + + for (;;) + { + if (opaque->btpo_flags & BTP_LEAF) + break; + + if (ScanDirectionIsForward(dir)) + { + offnum = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + } + else + { + offnum = PageGetMaxOffsetNumber(page); + } + + btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); + itup = &(btitem->bti_itup); + + blkno = ItemPointerGetBlockNumber(&(itup->t_tid)); + _bt_relbuf(rel, buf, BT_READ); buf = _bt_getbuf(rel, blkno, BT_READ); page = BufferGetPage(buf); opaque = (BTPageOpaque) PageGetSpecialPointer(page); - } while (! P_RIGHTMOST(opaque)); + + /* + * Race condition: If the child page we just stepped onto is in + * the process of being split, we need to make sure we're all the + * way at the right edge of the tree. See the paper by Lehman and + * Yao. + */ + + if (ScanDirectionIsBackward(dir) && !P_RIGHTMOST(opaque)) + { + do + { + blkno = opaque->btpo_next; + _bt_relbuf(rel, buf, BT_READ); + buf = _bt_getbuf(rel, blkno, BT_READ); + page = BufferGetPage(buf); + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + } while (!P_RIGHTMOST(opaque)); + } } - } - - /* okay, we've got the {left,right}-most page in the tree */ - maxoff = PageGetMaxOffsetNumber(page); - - if (ScanDirectionIsForward(dir)) { - if ( !P_LEFTMOST(opaque) ) /* non-leftmost page ? */ - elog (WARN, "_bt_endpoint: leftmost page (%u) has not leftmost flag", blkno); - start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; - /* - * I don't understand this stuff! It doesn't work for non-rightmost - * pages with only one element (P_HIKEY) which we have after - * deletion itups by vacuum (it's case of start > maxoff). - * Scanning in BackwardScanDirection is not understandable at all. - * Well - new stuff. - vadim 12/06/96 - */ + + /* okay, we've got the {left,right}-most page in the tree */ + maxoff = PageGetMaxOffsetNumber(page); + + if (ScanDirectionIsForward(dir)) + { + if (!P_LEFTMOST(opaque))/* non-leftmost page ? */ + elog(WARN, "_bt_endpoint: leftmost page (%u) has not leftmost flag", blkno); + start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY; + + /* + * I don't understand this stuff! It doesn't work for + * non-rightmost pages with only one element (P_HIKEY) which we + * have after deletion itups by vacuum (it's case of start > + * maxoff). Scanning in BackwardScanDirection is not + * understandable at all. Well - new stuff. - vadim 12/06/96 + */ #if 0 - if (PageIsEmpty(page) || start > maxoff) { - ItemPointerSet(current, blkno, maxoff); - if (!_bt_step(scan, &buf, BackwardScanDirection)) - return ((RetrieveIndexResult) NULL); - - start = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - } + if (PageIsEmpty(page) || start > maxoff) + { + ItemPointerSet(current, blkno, maxoff); + if (!_bt_step(scan, &buf, BackwardScanDirection)) + return ((RetrieveIndexResult) NULL); + + start = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + } #endif - if ( PageIsEmpty (page) ) + if (PageIsEmpty(page)) + { + if (start != P_HIKEY) /* non-rightmost page */ + elog(WARN, "_bt_endpoint: non-rightmost page (%u) is empty", blkno); + + /* + * It's left- & right- most page - root page, - and it's + * empty... + */ + _bt_relbuf(rel, buf, BT_READ); + ItemPointerSetInvalid(current); + so->btso_curbuf = InvalidBuffer; + return ((RetrieveIndexResult) NULL); + } + if (start > maxoff) /* start == 2 && maxoff == 1 */ + { + ItemPointerSet(current, blkno, maxoff); + if (!_bt_step(scan, &buf, ForwardScanDirection)) + return ((RetrieveIndexResult) NULL); + + start = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + } + /* new stuff ends here */ + else + { + ItemPointerSet(current, blkno, start); + } + } + else if (ScanDirectionIsBackward(dir)) { - if ( start != P_HIKEY ) /* non-rightmost page */ - elog (WARN, "_bt_endpoint: non-rightmost page (%u) is empty", blkno); - /* It's left- & right- most page - root page, - and it's empty... */ - _bt_relbuf(rel, buf, BT_READ); - ItemPointerSetInvalid(current); - so->btso_curbuf = InvalidBuffer; - return ((RetrieveIndexResult) NULL); + + /* + * I don't understand this stuff too! If RIGHT-most leaf page is + * empty why do scanning in ForwardScanDirection ??? Well - new + * stuff. - vadim 12/06/96 + */ +#if 0 + if (PageIsEmpty(page)) + { + ItemPointerSet(current, blkno, FirstOffsetNumber); + if (!_bt_step(scan, &buf, ForwardScanDirection)) + return ((RetrieveIndexResult) NULL); + + start = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + } +#endif + if (PageIsEmpty(page)) + { + /* If it's leftmost page too - it's empty root page... */ + if (P_LEFTMOST(opaque)) + { + _bt_relbuf(rel, buf, BT_READ); + ItemPointerSetInvalid(current); + so->btso_curbuf = InvalidBuffer; + return ((RetrieveIndexResult) NULL); + } + /* Go back ! */ + ItemPointerSet(current, blkno, FirstOffsetNumber); + if (!_bt_step(scan, &buf, BackwardScanDirection)) + return ((RetrieveIndexResult) NULL); + + start = ItemPointerGetOffsetNumber(current); + page = BufferGetPage(buf); + } + /* new stuff ends here */ + else + { + start = PageGetMaxOffsetNumber(page); + ItemPointerSet(current, blkno, start); + } } - if ( start > maxoff ) /* start == 2 && maxoff == 1 */ + else { - ItemPointerSet(current, blkno, maxoff); - if (!_bt_step(scan, &buf, ForwardScanDirection)) - return ((RetrieveIndexResult) NULL); - - start = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); + elog(WARN, "Illegal scan direction %d", dir); } - /* new stuff ends here */ - else { - ItemPointerSet(current, blkno, start); + + btitem = (BTItem) PageGetItem(page, PageGetItemId(page, start)); + itup = &(btitem->bti_itup); + + /* see if we picked a winner */ + if (_bt_checkkeys(scan, itup, &keysok)) + { + res = FormRetrieveIndexResult(current, &(itup->t_tid)); + + /* remember which buffer we have pinned */ + so->btso_curbuf = buf; } - } else if (ScanDirectionIsBackward(dir)) { - /* - * I don't understand this stuff too! If RIGHT-most leaf page is - * empty why do scanning in ForwardScanDirection ??? - * Well - new stuff. - vadim 12/06/96 - */ -#if 0 - if (PageIsEmpty(page)) { - ItemPointerSet(current, blkno, FirstOffsetNumber); - if (!_bt_step(scan, &buf, ForwardScanDirection)) - return ((RetrieveIndexResult) NULL); - - start = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); + else if (keysok >= so->numberOfFirstKeys) + { + so->btso_curbuf = buf; + return (_bt_next(scan, dir)); } -#endif - if (PageIsEmpty(page)) + else { - /* If it's leftmost page too - it's empty root page... */ - if ( P_LEFTMOST(opaque) ) - { - _bt_relbuf(rel, buf, BT_READ); ItemPointerSetInvalid(current); so->btso_curbuf = InvalidBuffer; - return ((RetrieveIndexResult) NULL); - } - /* Go back ! */ - ItemPointerSet(current, blkno, FirstOffsetNumber); - if (!_bt_step(scan, &buf, BackwardScanDirection)) - return ((RetrieveIndexResult) NULL); - - start = ItemPointerGetOffsetNumber(current); - page = BufferGetPage(buf); - } - /* new stuff ends here */ - else { - start = PageGetMaxOffsetNumber(page); - ItemPointerSet(current, blkno, start); + _bt_relbuf(rel, buf, BT_READ); + res = (RetrieveIndexResult) NULL; } - } else { - elog(WARN, "Illegal scan direction %d", dir); - } - - btitem = (BTItem) PageGetItem(page, PageGetItemId(page, start)); - itup = &(btitem->bti_itup); - - /* see if we picked a winner */ - if ( _bt_checkkeys (scan, itup, &keysok) ) - { - res = FormRetrieveIndexResult(current, &(itup->t_tid)); - - /* remember which buffer we have pinned */ - so->btso_curbuf = buf; - } - else if ( keysok >= so->numberOfFirstKeys ) - { - so->btso_curbuf = buf; - return (_bt_next (scan, dir)); - } - else - { - ItemPointerSetInvalid(current); - so->btso_curbuf = InvalidBuffer; - _bt_relbuf(rel, buf, BT_READ); - res = (RetrieveIndexResult) NULL; - } - - return (res); + + return (res); } diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c index 8e054d24abf..09cb43769f2 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -5,30 +5,30 @@ * * * IDENTIFICATION - * $Id: nbtsort.c,v 1.19 1997/08/19 21:29:46 momjian Exp $ + * $Id: nbtsort.c,v 1.20 1997/09/07 04:39:02 momjian Exp $ * * NOTES * * what we do is: * - generate a set of initial one-block runs, distributed round-robin - * between the output tapes. + * between the output tapes. * - for each pass, - * - swap input and output tape sets, rewinding both and truncating - * the output tapes. - * - merge the current run in each input tape to the current output - * tape. - * - when each input run has been exhausted, switch to another output - * tape and start processing another run. + * - swap input and output tape sets, rewinding both and truncating + * the output tapes. + * - merge the current run in each input tape to the current output + * tape. + * - when each input run has been exhausted, switch to another output + * tape and start processing another run. * - when we have fewer runs than tapes, we know we are ready to start - * merging into the btree leaf pages. (i.e., we do not have to wait - * until we have exactly one tape.) + * merging into the btree leaf pages. (i.e., we do not have to wait + * until we have exactly one tape.) * - as we extract tuples from the final runs, we build the pages for - * each level. when we have only one page on a level, it must be the - * root -- it can be attached to the btree metapage and we are done. + * each level. when we have only one page on a level, it must be the + * root -- it can be attached to the btree metapage and we are done. * * conventions: * - external interface routines take in and return "void *" for their - * opaque handles. this is for modularity reasons. + * opaque handles. this is for modularity reasons. * * this code is moderately slow (~10% slower) compared to the regular * btree (insertion) build code on sorted or well-clustered data. on @@ -58,20 +58,21 @@ #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif #ifdef BTREE_BUILD_STATS #include <tcop/tcopprot.h> -extern int ShowExecutorStats; +extern int ShowExecutorStats; + #endif -static BTItem _bt_buildadd(Relation index, void *pstate, BTItem bti, int flags); -static BTItem _bt_minitem(Page opage, BlockNumber oblkno, int atend); -static void *_bt_pagestate(Relation index, int flags, int level, bool doupper); -static void _bt_uppershutdown(Relation index, BTPageState *state); +static BTItem _bt_buildadd(Relation index, void *pstate, BTItem bti, int flags); +static BTItem _bt_minitem(Page opage, BlockNumber oblkno, int atend); +static void *_bt_pagestate(Relation index, int flags, int level, bool doupper); +static void _bt_uppershutdown(Relation index, BTPageState * state); /* * turn on debugging output. @@ -83,18 +84,18 @@ static void _bt_uppershutdown(Relation index, BTPageState *state); #define FASTBUILD_SPOOL #define FASTBUILD_MERGE -#define MAXTAPES (7) -#define TAPEBLCKSZ (MAXBLCKSZ << 2) -#define TAPETEMP "pg_btsortXXXXXX" +#define MAXTAPES (7) +#define TAPEBLCKSZ (MAXBLCKSZ << 2) +#define TAPETEMP "pg_btsortXXXXXX" -extern int NDirectFileRead; -extern int NDirectFileWrite; -extern char *mktemp(char *template); +extern int NDirectFileRead; +extern int NDirectFileWrite; +extern char *mktemp(char *template); /* - * this is what we use to shovel BTItems in and out of memory. it's + * this is what we use to shovel BTItems in and out of memory. it's * bigger than a standard block because we are doing a lot of strictly - * sequential i/o. this is obviously something of a tradeoff since we + * sequential i/o. this is obviously something of a tradeoff since we * are potentially reading a bunch of zeroes off of disk in many * cases. * @@ -104,14 +105,15 @@ extern char *mktemp(char *template); * the only thing like that so i'm not going to worry about wasting a * few bytes. */ -typedef struct { - int bttb_magic; /* magic number */ - int bttb_fd; /* file descriptor */ - int bttb_top; /* top of free space within bttb_data */ - short bttb_ntup; /* number of tuples in this block */ - short bttb_eor; /* End-Of-Run marker */ - char bttb_data[TAPEBLCKSZ - 2 * sizeof(double)]; -} BTTapeBlock; +typedef struct +{ + int bttb_magic; /* magic number */ + int bttb_fd; /* file descriptor */ + int bttb_top; /* top of free space within bttb_data */ + short bttb_ntup; /* number of tuples in this block */ + short bttb_eor; /* End-Of-Run marker */ + char bttb_data[TAPEBLCKSZ - 2 * sizeof(double)]; +} BTTapeBlock; /* * this structure holds the bookkeeping for a simple balanced multiway @@ -120,13 +122,14 @@ typedef struct { * right now. though if psort was in a condition that i could hack it * to do this, you bet i would.) */ -typedef struct { - int bts_ntapes; - int bts_tape; - BTTapeBlock **bts_itape; /* input tape blocks */ - BTTapeBlock **bts_otape; /* output tape blocks */ - bool isunique; -} BTSpool; +typedef struct +{ + int bts_ntapes; + int bts_tape; + BTTapeBlock **bts_itape; /* input tape blocks */ + BTTapeBlock **bts_otape; /* output tape blocks */ + bool isunique; +} BTSpool; /*------------------------------------------------------------------------- * sorting comparison routine - returns {-1,0,1} depending on whether @@ -146,101 +149,102 @@ typedef struct { * what the heck. * *------------------------------------------------------------------------- */ -typedef struct { - Datum *btsk_datum; - char *btsk_nulls; - BTItem btsk_item; -} BTSortKey; +typedef struct +{ + Datum *btsk_datum; + char *btsk_nulls; + BTItem btsk_item; +} BTSortKey; static Relation _bt_sortrel; -static int _bt_nattr; -static BTSpool * _bt_inspool; +static int _bt_nattr; +static BTSpool *_bt_inspool; static void -_bt_isortcmpinit(Relation index, BTSpool *spool) +_bt_isortcmpinit(Relation index, BTSpool * spool) { - _bt_sortrel = index; - _bt_inspool = spool; - _bt_nattr = index->rd_att->natts; + _bt_sortrel = index; + _bt_inspool = spool; + _bt_nattr = index->rd_att->natts; } static int -_bt_isortcmp(BTSortKey *k1, BTSortKey *k2) +_bt_isortcmp(BTSortKey * k1, BTSortKey * k2) { - Datum *k1_datum = k1->btsk_datum; - Datum *k2_datum = k2->btsk_datum; - char *k1_nulls = k1->btsk_nulls; - char *k2_nulls = k2->btsk_nulls; - bool equal_isnull = false; - int i; - - if (k1->btsk_item == (BTItem) NULL) - { - if (k2->btsk_item == (BTItem) NULL) - return(0); /* 1 = 2 */ - return(1); /* 1 > 2 */ - } - else if (k2->btsk_item == (BTItem) NULL) - return(-1); /* 1 < 2 */ - - for (i = 0; i < _bt_nattr; i++) - { - if ( k1_nulls[i] != ' ' ) /* k1 attr is NULL */ + Datum *k1_datum = k1->btsk_datum; + Datum *k2_datum = k2->btsk_datum; + char *k1_nulls = k1->btsk_nulls; + char *k2_nulls = k2->btsk_nulls; + bool equal_isnull = false; + int i; + + if (k1->btsk_item == (BTItem) NULL) { - if ( k2_nulls[i] != ' ' ) /* the same for k2 */ - { - equal_isnull = true; - continue; - } - return (1); /* NULL ">" NOT_NULL */ + if (k2->btsk_item == (BTItem) NULL) + return (0); /* 1 = 2 */ + return (1); /* 1 > 2 */ } - else if ( k2_nulls[i] != ' ' ) /* k2 attr is NULL */ - return (-1); /* NOT_NULL "<" NULL */ - - if (_bt_invokestrat(_bt_sortrel, i+1, BTGreaterStrategyNumber, - k1_datum[i], k2_datum[i])) - return(1); /* 1 > 2 */ - else if (_bt_invokestrat(_bt_sortrel, i+1, BTGreaterStrategyNumber, - k2_datum[i], k1_datum[i])) - return(-1); /* 1 < 2 */ - } - - if ( _bt_inspool->isunique && !equal_isnull ) - { - _bt_spooldestroy ((void*)_bt_inspool); - elog (WARN, "Cannot create unique index. Table contains non-unique values"); - } - return(0); /* 1 = 2 */ + else if (k2->btsk_item == (BTItem) NULL) + return (-1); /* 1 < 2 */ + + for (i = 0; i < _bt_nattr; i++) + { + if (k1_nulls[i] != ' ') /* k1 attr is NULL */ + { + if (k2_nulls[i] != ' ') /* the same for k2 */ + { + equal_isnull = true; + continue; + } + return (1); /* NULL ">" NOT_NULL */ + } + else if (k2_nulls[i] != ' ') /* k2 attr is NULL */ + return (-1); /* NOT_NULL "<" NULL */ + + if (_bt_invokestrat(_bt_sortrel, i + 1, BTGreaterStrategyNumber, + k1_datum[i], k2_datum[i])) + return (1); /* 1 > 2 */ + else if (_bt_invokestrat(_bt_sortrel, i + 1, BTGreaterStrategyNumber, + k2_datum[i], k1_datum[i])) + return (-1); /* 1 < 2 */ + } + + if (_bt_inspool->isunique && !equal_isnull) + { + _bt_spooldestroy((void *) _bt_inspool); + elog(WARN, "Cannot create unique index. Table contains non-unique values"); + } + return (0); /* 1 = 2 */ } static void -_bt_setsortkey(Relation index, BTItem bti, BTSortKey *sk) +_bt_setsortkey(Relation index, BTItem bti, BTSortKey * sk) { - sk->btsk_item = (BTItem) NULL; - sk->btsk_datum = (Datum*) NULL; - sk->btsk_nulls = (char*) NULL; - - if (bti != (BTItem) NULL) - { - IndexTuple it = &(bti->bti_itup); - TupleDesc itdesc = index->rd_att; - Datum *dp = (Datum*) palloc (_bt_nattr * sizeof (Datum)); - char *np = (char*) palloc (_bt_nattr * sizeof (char)); - bool isnull; - int i; - - for (i = 0; i < _bt_nattr; i++) - { - dp[i] = index_getattr(it, i+1, itdesc, &isnull); - if ( isnull ) - np[i] = 'n'; - else - np[i] = ' '; + sk->btsk_item = (BTItem) NULL; + sk->btsk_datum = (Datum *) NULL; + sk->btsk_nulls = (char *) NULL; + + if (bti != (BTItem) NULL) + { + IndexTuple it = &(bti->bti_itup); + TupleDesc itdesc = index->rd_att; + Datum *dp = (Datum *) palloc(_bt_nattr * sizeof(Datum)); + char *np = (char *) palloc(_bt_nattr * sizeof(char)); + bool isnull; + int i; + + for (i = 0; i < _bt_nattr; i++) + { + dp[i] = index_getattr(it, i + 1, itdesc, &isnull); + if (isnull) + np[i] = 'n'; + else + np[i] = ' '; + } + sk->btsk_item = bti; + sk->btsk_datum = dp; + sk->btsk_nulls = np; } - sk->btsk_item = bti; - sk->btsk_datum = dp; - sk->btsk_nulls = np; - } } /*------------------------------------------------------------------------- @@ -254,84 +258,100 @@ _bt_setsortkey(Relation index, BTItem bti, BTSortKey *sk) * XXX these probably ought to be generic library functions. *------------------------------------------------------------------------- */ -typedef struct { - int btpqe_tape; /* tape identifier */ - BTSortKey btpqe_item; /* pointer to BTItem in tape buffer */ -} BTPriQueueElem; - -#define MAXELEM MAXTAPES -typedef struct { - int btpq_nelem; - BTPriQueueElem btpq_queue[MAXELEM]; - Relation btpq_rel; -} BTPriQueue; +typedef struct +{ + int btpqe_tape; /* tape identifier */ + BTSortKey btpqe_item; /* pointer to BTItem in tape buffer */ +} BTPriQueueElem; + +#define MAXELEM MAXTAPES +typedef struct +{ + int btpq_nelem; + BTPriQueueElem btpq_queue[MAXELEM]; + Relation btpq_rel; +} BTPriQueue; /* be sure to call _bt_isortcmpinit first */ #define GREATER(a, b) \ - (_bt_isortcmp(&((a)->btpqe_item), &((b)->btpqe_item)) > 0) + (_bt_isortcmp(&((a)->btpqe_item), &((b)->btpqe_item)) > 0) static void -_bt_pqsift(BTPriQueue *q, int parent) +_bt_pqsift(BTPriQueue * q, int parent) { - int child; - BTPriQueueElem e; - - for (child = parent * 2 + 1; - child < q->btpq_nelem; - child = parent * 2 + 1) { - if (child < q->btpq_nelem - 1) { - if (GREATER(&(q->btpq_queue[child]), &(q->btpq_queue[child+1]))) { - ++child; - } - } - if (GREATER(&(q->btpq_queue[parent]), &(q->btpq_queue[child]))) { - e = q->btpq_queue[child]; /* struct = */ - q->btpq_queue[child] = q->btpq_queue[parent]; /* struct = */ - q->btpq_queue[parent] = e; /* struct = */ - parent = child; - } else { - parent = child + 1; + int child; + BTPriQueueElem e; + + for (child = parent * 2 + 1; + child < q->btpq_nelem; + child = parent * 2 + 1) + { + if (child < q->btpq_nelem - 1) + { + if (GREATER(&(q->btpq_queue[child]), &(q->btpq_queue[child + 1]))) + { + ++child; + } + } + if (GREATER(&(q->btpq_queue[parent]), &(q->btpq_queue[child]))) + { + e = q->btpq_queue[child]; /* struct = */ + q->btpq_queue[child] = q->btpq_queue[parent]; /* struct = */ + q->btpq_queue[parent] = e; /* struct = */ + parent = child; + } + else + { + parent = child + 1; + } } - } } static int -_bt_pqnext(BTPriQueue *q, BTPriQueueElem *e) +_bt_pqnext(BTPriQueue * q, BTPriQueueElem * e) { - if (q->btpq_nelem < 1) { /* already empty */ - return(-1); - } - *e = q->btpq_queue[0]; /* struct = */ - - if (--q->btpq_nelem < 1) { /* now empty, don't sift */ - return(0); - } - q->btpq_queue[0] = q->btpq_queue[q->btpq_nelem]; /* struct = */ - _bt_pqsift(q, 0); - return(0); + if (q->btpq_nelem < 1) + { /* already empty */ + return (-1); + } + *e = q->btpq_queue[0]; /* struct = */ + + if (--q->btpq_nelem < 1) + { /* now empty, don't sift */ + return (0); + } + q->btpq_queue[0] = q->btpq_queue[q->btpq_nelem]; /* struct = */ + _bt_pqsift(q, 0); + return (0); } static void -_bt_pqadd(BTPriQueue *q, BTPriQueueElem *e) +_bt_pqadd(BTPriQueue * q, BTPriQueueElem * e) { - int child, parent; - - if (q->btpq_nelem >= MAXELEM) { - elog(WARN, "_bt_pqadd: queue overflow"); - } - - child = q->btpq_nelem++; - while (child > 0) { - parent = child / 2; - if (GREATER(e, &(q->btpq_queue[parent]))) { - break; - } else { - q->btpq_queue[child] = q->btpq_queue[parent]; /* struct = */ - child = parent; + int child, + parent; + + if (q->btpq_nelem >= MAXELEM) + { + elog(WARN, "_bt_pqadd: queue overflow"); + } + + child = q->btpq_nelem++; + while (child > 0) + { + parent = child / 2; + if (GREATER(e, &(q->btpq_queue[parent]))) + { + break; + } + else + { + q->btpq_queue[child] = q->btpq_queue[parent]; /* struct = */ + child = parent; + } } - } - q->btpq_queue[child] = *e; /* struct = */ + q->btpq_queue[child] = *e; /* struct = */ } /*------------------------------------------------------------------------- @@ -339,37 +359,37 @@ _bt_pqadd(BTPriQueue *q, BTPriQueueElem *e) *------------------------------------------------------------------------- */ -#define BTITEMSZ(btitem) \ - ((btitem) ? \ - (IndexTupleDSize((btitem)->bti_itup) + \ - (sizeof(BTItemData) - sizeof(IndexTupleData))) : \ - 0) -#define SPCLEFT(tape) \ - (sizeof((tape)->bttb_data) - (tape)->bttb_top) -#define EMPTYTAPE(tape) \ - ((tape)->bttb_ntup <= 0) -#define BTTAPEMAGIC 0x19660226 +#define BTITEMSZ(btitem) \ + ((btitem) ? \ + (IndexTupleDSize((btitem)->bti_itup) + \ + (sizeof(BTItemData) - sizeof(IndexTupleData))) : \ + 0) +#define SPCLEFT(tape) \ + (sizeof((tape)->bttb_data) - (tape)->bttb_top) +#define EMPTYTAPE(tape) \ + ((tape)->bttb_ntup <= 0) +#define BTTAPEMAGIC 0x19660226 /* * reset the tape header for its next use without doing anything to - * the physical tape file. (setting bttb_top to 0 makes the block + * the physical tape file. (setting bttb_top to 0 makes the block * empty.) */ static void -_bt_tapereset(BTTapeBlock *tape) +_bt_tapereset(BTTapeBlock * tape) { - tape->bttb_eor = 0; - tape->bttb_top = 0; - tape->bttb_ntup = 0; + tape->bttb_eor = 0; + tape->bttb_top = 0; + tape->bttb_ntup = 0; } /* * rewind the physical tape file. */ static void -_bt_taperewind(BTTapeBlock *tape) +_bt_taperewind(BTTapeBlock * tape) { - FileSeek(tape->bttb_fd, 0, SEEK_SET); + FileSeek(tape->bttb_fd, 0, SEEK_SET); } /* @@ -382,17 +402,17 @@ _bt_taperewind(BTTapeBlock *tape) * least you don't have to delete and reinsert the directory entries. */ static void -_bt_tapeclear(BTTapeBlock *tape) +_bt_tapeclear(BTTapeBlock * tape) { - /* blow away the contents of the old file */ - _bt_taperewind(tape); + /* blow away the contents of the old file */ + _bt_taperewind(tape); #if 0 - FileSync(tape->bttb_fd); + FileSync(tape->bttb_fd); #endif - FileTruncate(tape->bttb_fd, 0); + FileTruncate(tape->bttb_fd, 0); - /* reset the buffer */ - _bt_tapereset(tape); + /* reset the buffer */ + _bt_tapereset(tape); } /* @@ -402,43 +422,44 @@ _bt_tapeclear(BTTapeBlock *tape) static BTTapeBlock * _bt_tapecreate(char *fname) { - BTTapeBlock *tape = (BTTapeBlock *) palloc(sizeof(BTTapeBlock)); + BTTapeBlock *tape = (BTTapeBlock *) palloc(sizeof(BTTapeBlock)); - if (tape == (BTTapeBlock *) NULL) { - elog(WARN, "_bt_tapecreate: out of memory"); - } + if (tape == (BTTapeBlock *) NULL) + { + elog(WARN, "_bt_tapecreate: out of memory"); + } - tape->bttb_magic = BTTAPEMAGIC; + tape->bttb_magic = BTTAPEMAGIC; - tape->bttb_fd = FileNameOpenFile(fname, O_RDWR|O_CREAT|O_TRUNC, 0600); - Assert(tape->bttb_fd >= 0); + tape->bttb_fd = FileNameOpenFile(fname, O_RDWR | O_CREAT | O_TRUNC, 0600); + Assert(tape->bttb_fd >= 0); - /* initialize the buffer */ - _bt_tapereset(tape); + /* initialize the buffer */ + _bt_tapereset(tape); - return(tape); + return (tape); } /* * destroy the BTTapeBlock structure and its physical tape file. */ static void -_bt_tapedestroy(BTTapeBlock *tape) +_bt_tapedestroy(BTTapeBlock * tape) { - FileUnlink(tape->bttb_fd); - pfree((void *) tape); + FileUnlink(tape->bttb_fd); + pfree((void *) tape); } /* * flush the tape block to the file, marking End-Of-Run if requested. */ static void -_bt_tapewrite(BTTapeBlock *tape, int eor) +_bt_tapewrite(BTTapeBlock * tape, int eor) { - tape->bttb_eor = eor; - FileWrite(tape->bttb_fd, (char *) tape, TAPEBLCKSZ); - NDirectFileWrite += TAPEBLCKSZ/MAXBLCKSZ; - _bt_tapereset(tape); + tape->bttb_eor = eor; + FileWrite(tape->bttb_fd, (char *) tape, TAPEBLCKSZ); + NDirectFileWrite += TAPEBLCKSZ / MAXBLCKSZ; + _bt_tapereset(tape); } /* @@ -447,34 +468,36 @@ _bt_tapewrite(BTTapeBlock *tape, int eor) * * returns: * - 0 if there are no more blocks in the tape or in this run (call - * _bt_tapereset to clear the End-Of-Run marker) + * _bt_tapereset to clear the End-Of-Run marker) * - 1 if a valid block was read */ static int -_bt_taperead(BTTapeBlock *tape) +_bt_taperead(BTTapeBlock * tape) { - int fd; - int nread; - - if (tape->bttb_eor) { - return(0); /* we are already at End-Of-Run */ - } - - /* - * we're clobbering the old tape block, but we do need to save the - * VFD (the one in the block we're reading is bogus). - */ - fd = tape->bttb_fd; - nread = FileRead(fd, (char *) tape, TAPEBLCKSZ); - tape->bttb_fd = fd; - - if (nread != TAPEBLCKSZ) { - Assert(nread == 0); /* we are at EOF */ - return(0); - } - Assert(tape->bttb_magic == BTTAPEMAGIC); - NDirectFileRead += TAPEBLCKSZ/MAXBLCKSZ; - return(1); + int fd; + int nread; + + if (tape->bttb_eor) + { + return (0); /* we are already at End-Of-Run */ + } + + /* + * we're clobbering the old tape block, but we do need to save the VFD + * (the one in the block we're reading is bogus). + */ + fd = tape->bttb_fd; + nread = FileRead(fd, (char *) tape, TAPEBLCKSZ); + tape->bttb_fd = fd; + + if (nread != TAPEBLCKSZ) + { + Assert(nread == 0); /* we are at EOF */ + return (0); + } + Assert(tape->bttb_magic == BTTAPEMAGIC); + NDirectFileRead += TAPEBLCKSZ / MAXBLCKSZ; + return (1); } /* @@ -487,19 +510,20 @@ _bt_taperead(BTTapeBlock *tape) * side effects: * - sets 'pos' to the current position within the block. */ -static BTItem -_bt_tapenext(BTTapeBlock *tape, char **pos) +static BTItem +_bt_tapenext(BTTapeBlock * tape, char **pos) { - Size itemsz; - BTItem bti; - - if (*pos >= tape->bttb_data + tape->bttb_top) { - return((BTItem) NULL); - } - bti = (BTItem) *pos; - itemsz = BTITEMSZ(bti); - *pos += DOUBLEALIGN(itemsz); - return(bti); + Size itemsz; + BTItem bti; + + if (*pos >= tape->bttb_data + tape->bttb_top) + { + return ((BTItem) NULL); + } + bti = (BTItem) * pos; + itemsz = BTITEMSZ(bti); + *pos += DOUBLEALIGN(itemsz); + return (bti); } /* @@ -514,11 +538,11 @@ _bt_tapenext(BTTapeBlock *tape, char **pos) * the beginning of free space. */ static void -_bt_tapeadd(BTTapeBlock *tape, BTItem item, int itemsz) +_bt_tapeadd(BTTapeBlock * tape, BTItem item, int itemsz) { - memcpy(tape->bttb_data + tape->bttb_top, item, itemsz); - ++tape->bttb_ntup; - tape->bttb_top += DOUBLEALIGN(itemsz); + memcpy(tape->bttb_data + tape->bttb_top, item, itemsz); + ++tape->bttb_ntup; + tape->bttb_top += DOUBLEALIGN(itemsz); } /*------------------------------------------------------------------------- @@ -530,41 +554,44 @@ _bt_tapeadd(BTTapeBlock *tape, BTItem item, int itemsz) * create and initialize a spool structure, including the underlying * files. */ -void * +void * _bt_spoolinit(Relation index, int ntapes, bool isunique) { - BTSpool *btspool = (BTSpool *) palloc(sizeof(BTSpool)); - int i; - char *fname = (char *) palloc(sizeof(TAPETEMP) + 1); - - if (btspool == (BTSpool *) NULL || fname == (char *) NULL) { - elog(WARN, "_bt_spoolinit: out of memory"); - } - memset((char *) btspool, 0, sizeof(BTSpool)); - btspool->bts_ntapes = ntapes; - btspool->bts_tape = 0; - btspool->isunique = isunique; - - btspool->bts_itape = - (BTTapeBlock **) palloc(sizeof(BTTapeBlock *) * ntapes); - btspool->bts_otape = - (BTTapeBlock **) palloc(sizeof(BTTapeBlock *) * ntapes); - if (btspool->bts_itape == (BTTapeBlock **) NULL || - btspool->bts_otape == (BTTapeBlock **) NULL) { - elog(WARN, "_bt_spoolinit: out of memory"); - } - - for (i = 0; i < ntapes; ++i) { - btspool->bts_itape[i] = - _bt_tapecreate(mktemp(strcpy(fname, TAPETEMP))); - btspool->bts_otape[i] = - _bt_tapecreate(mktemp(strcpy(fname, TAPETEMP))); - } - pfree((void *) fname); - - _bt_isortcmpinit(index, btspool); - - return((void *) btspool); + BTSpool *btspool = (BTSpool *) palloc(sizeof(BTSpool)); + int i; + char *fname = (char *) palloc(sizeof(TAPETEMP) + 1); + + if (btspool == (BTSpool *) NULL || fname == (char *) NULL) + { + elog(WARN, "_bt_spoolinit: out of memory"); + } + memset((char *) btspool, 0, sizeof(BTSpool)); + btspool->bts_ntapes = ntapes; + btspool->bts_tape = 0; + btspool->isunique = isunique; + + btspool->bts_itape = + (BTTapeBlock **) palloc(sizeof(BTTapeBlock *) * ntapes); + btspool->bts_otape = + (BTTapeBlock **) palloc(sizeof(BTTapeBlock *) * ntapes); + if (btspool->bts_itape == (BTTapeBlock **) NULL || + btspool->bts_otape == (BTTapeBlock **) NULL) + { + elog(WARN, "_bt_spoolinit: out of memory"); + } + + for (i = 0; i < ntapes; ++i) + { + btspool->bts_itape[i] = + _bt_tapecreate(mktemp(strcpy(fname, TAPETEMP))); + btspool->bts_otape[i] = + _bt_tapecreate(mktemp(strcpy(fname, TAPETEMP))); + } + pfree((void *) fname); + + _bt_isortcmpinit(index, btspool); + + return ((void *) btspool); } /* @@ -573,29 +600,32 @@ _bt_spoolinit(Relation index, int ntapes, bool isunique) void _bt_spooldestroy(void *spool) { - BTSpool *btspool = (BTSpool *) spool; - int i; - - for (i = 0; i < btspool->bts_ntapes; ++i) { - _bt_tapedestroy(btspool->bts_otape[i]); - _bt_tapedestroy(btspool->bts_itape[i]); - } - pfree((void *) btspool); + BTSpool *btspool = (BTSpool *) spool; + int i; + + for (i = 0; i < btspool->bts_ntapes; ++i) + { + _bt_tapedestroy(btspool->bts_otape[i]); + _bt_tapedestroy(btspool->bts_itape[i]); + } + pfree((void *) btspool); } /* * flush out any dirty output tape blocks */ static void -_bt_spoolflush(BTSpool *btspool) +_bt_spoolflush(BTSpool * btspool) { - int i; + int i; - for (i = 0; i < btspool->bts_ntapes; ++i) { - if (!EMPTYTAPE(btspool->bts_otape[i])) { - _bt_tapewrite(btspool->bts_otape[i], 1); + for (i = 0; i < btspool->bts_ntapes; ++i) + { + if (!EMPTYTAPE(btspool->bts_otape[i])) + { + _bt_tapewrite(btspool->bts_otape[i], 1); + } } - } } /* @@ -605,36 +635,37 @@ _bt_spoolflush(BTSpool *btspool) * output tapes. */ static void -_bt_spoolswap(BTSpool *btspool) +_bt_spoolswap(BTSpool * btspool) { - File tmpfd; - BTTapeBlock *itape; - BTTapeBlock *otape; - int i; + File tmpfd; + BTTapeBlock *itape; + BTTapeBlock *otape; + int i; - for (i = 0; i < btspool->bts_ntapes; ++i) { - itape = btspool->bts_itape[i]; - otape = btspool->bts_otape[i]; + for (i = 0; i < btspool->bts_ntapes; ++i) + { + itape = btspool->bts_itape[i]; + otape = btspool->bts_otape[i]; - /* - * swap the input and output VFDs. - */ - tmpfd = itape->bttb_fd; - itape->bttb_fd = otape->bttb_fd; - otape->bttb_fd = tmpfd; + /* + * swap the input and output VFDs. + */ + tmpfd = itape->bttb_fd; + itape->bttb_fd = otape->bttb_fd; + otape->bttb_fd = tmpfd; - /* - * rewind the new input tape. - */ - _bt_taperewind(itape); - _bt_tapereset(itape); + /* + * rewind the new input tape. + */ + _bt_taperewind(itape); + _bt_tapereset(itape); - /* - * clear the new output tape -- it's ok to throw away the old - * inputs. - */ - _bt_tapeclear(otape); - } + /* + * clear the new output tape -- it's ok to throw away the old + * inputs. + */ + _bt_tapeclear(otape); + } } /*------------------------------------------------------------------------- @@ -643,7 +674,7 @@ _bt_spoolswap(BTSpool *btspool) */ /* - * spool 'btitem' into an initial run. as tape blocks are filled, the + * spool 'btitem' into an initial run. as tape blocks are filled, the * block BTItems are qsorted and written into some output tape (it * doesn't matter which; we go round-robin for simplicity). the * initial runs are therefore always just one block. @@ -651,134 +682,137 @@ _bt_spoolswap(BTSpool *btspool) void _bt_spool(Relation index, BTItem btitem, void *spool) { - BTSpool *btspool = (BTSpool *) spool; - BTTapeBlock *itape; - Size itemsz; - - _bt_isortcmpinit (index, btspool); - - itape = btspool->bts_itape[btspool->bts_tape]; - itemsz = BTITEMSZ(btitem); - itemsz = DOUBLEALIGN(itemsz); - - /* - * if this buffer is too full for this BTItemData, or if we have - * run out of BTItems, we need to sort the buffer and write it - * out. in this case, the BTItemData will go into the next tape's - * buffer. - */ - if (btitem == (BTItem) NULL || SPCLEFT(itape) < itemsz) { - BTSortKey *parray = (BTSortKey *) NULL; - BTTapeBlock *otape; - BTItem bti; - char *pos; - int btisz; - int it_ntup = itape->bttb_ntup; - int i; + BTSpool *btspool = (BTSpool *) spool; + BTTapeBlock *itape; + Size itemsz; - /* - * build an array of pointers to the BTItemDatas on the input - * block. - */ - if (it_ntup > 0) { - parray = - (BTSortKey *) palloc(it_ntup * sizeof(BTSortKey)); - pos = itape->bttb_data; - for (i = 0; i < it_ntup; ++i) { - _bt_setsortkey(index, _bt_tapenext(itape, &pos), &(parray[i])); - } - - /* - * qsort the pointer array. - */ - qsort((void *) parray, it_ntup, sizeof(BTSortKey), - (int (*)(const void *,const void *))_bt_isortcmp); - } + _bt_isortcmpinit(index, btspool); + + itape = btspool->bts_itape[btspool->bts_tape]; + itemsz = BTITEMSZ(btitem); + itemsz = DOUBLEALIGN(itemsz); /* - * write the spooled run into the output tape. we copy the - * BTItemDatas in the order dictated by the sorted array of - * BTItems, not the original order. - * - * (since everything was DOUBLEALIGN'd and is all on a single - * tape block, everything had *better* still fit on one tape - * block..) + * if this buffer is too full for this BTItemData, or if we have run + * out of BTItems, we need to sort the buffer and write it out. in + * this case, the BTItemData will go into the next tape's buffer. */ - otape = btspool->bts_otape[btspool->bts_tape]; - for (i = 0; i < it_ntup; ++i) { - bti = parray[i].btsk_item; - btisz = BTITEMSZ(bti); - btisz = DOUBLEALIGN(btisz); - _bt_tapeadd(otape, bti, btisz); + if (btitem == (BTItem) NULL || SPCLEFT(itape) < itemsz) + { + BTSortKey *parray = (BTSortKey *) NULL; + BTTapeBlock *otape; + BTItem bti; + char *pos; + int btisz; + int it_ntup = itape->bttb_ntup; + int i; + + /* + * build an array of pointers to the BTItemDatas on the input + * block. + */ + if (it_ntup > 0) + { + parray = + (BTSortKey *) palloc(it_ntup * sizeof(BTSortKey)); + pos = itape->bttb_data; + for (i = 0; i < it_ntup; ++i) + { + _bt_setsortkey(index, _bt_tapenext(itape, &pos), &(parray[i])); + } + + /* + * qsort the pointer array. + */ + qsort((void *) parray, it_ntup, sizeof(BTSortKey), + (int (*) (const void *, const void *)) _bt_isortcmp); + } + + /* + * write the spooled run into the output tape. we copy the + * BTItemDatas in the order dictated by the sorted array of + * BTItems, not the original order. + * + * (since everything was DOUBLEALIGN'd and is all on a single tape + * block, everything had *better* still fit on one tape block..) + */ + otape = btspool->bts_otape[btspool->bts_tape]; + for (i = 0; i < it_ntup; ++i) + { + bti = parray[i].btsk_item; + btisz = BTITEMSZ(bti); + btisz = DOUBLEALIGN(btisz); + _bt_tapeadd(otape, bti, btisz); #if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_SPOOL) - { - bool isnull; - Datum d = index_getattr(&(bti->bti_itup), 1, index->rd_att, - &isnull); - printf("_bt_spool: inserted <%x> into output tape %d\n", - d, btspool->bts_tape); - } -#endif /* FASTBUILD_DEBUG && FASTBUILD_SPOOL */ - } + { + bool isnull; + Datum d = index_getattr(&(bti->bti_itup), 1, index->rd_att, + &isnull); - /* - * the initial runs are always single tape blocks. flush the - * output block, marking End-Of-Run. - */ - _bt_tapewrite(otape, 1); + printf("_bt_spool: inserted <%x> into output tape %d\n", + d, btspool->bts_tape); + } +#endif /* FASTBUILD_DEBUG && FASTBUILD_SPOOL */ + } - /* - * reset the input buffer for the next run. we don't have to - * write it out or anything -- we only use it to hold the - * unsorted BTItemDatas, the output tape contains all the - * sorted stuff. - * - * changing bts_tape changes the output tape and input tape; - * we change itape for the code below. - */ - _bt_tapereset(itape); - btspool->bts_tape = (btspool->bts_tape + 1) % btspool->bts_ntapes; - itape = btspool->bts_itape[btspool->bts_tape]; + /* + * the initial runs are always single tape blocks. flush the + * output block, marking End-Of-Run. + */ + _bt_tapewrite(otape, 1); - /* - * destroy the pointer array. - */ - if (parray != (BTSortKey *) NULL) - { - for (i = 0; i < it_ntup; i++) - { - if ( parray[i].btsk_datum != (Datum*) NULL ) - pfree ((void*)(parray[i].btsk_datum)); - if ( parray[i].btsk_nulls != (char*) NULL ) - pfree ((void*)(parray[i].btsk_nulls)); - } - pfree((void *) parray); + /* + * reset the input buffer for the next run. we don't have to + * write it out or anything -- we only use it to hold the unsorted + * BTItemDatas, the output tape contains all the sorted stuff. + * + * changing bts_tape changes the output tape and input tape; we + * change itape for the code below. + */ + _bt_tapereset(itape); + btspool->bts_tape = (btspool->bts_tape + 1) % btspool->bts_ntapes; + itape = btspool->bts_itape[btspool->bts_tape]; + + /* + * destroy the pointer array. + */ + if (parray != (BTSortKey *) NULL) + { + for (i = 0; i < it_ntup; i++) + { + if (parray[i].btsk_datum != (Datum *) NULL) + pfree((void *) (parray[i].btsk_datum)); + if (parray[i].btsk_nulls != (char *) NULL) + pfree((void *) (parray[i].btsk_nulls)); + } + pfree((void *) parray); + } } - } - /* insert this item into the current buffer */ - if (btitem != (BTItem) NULL) { - _bt_tapeadd(itape, btitem, itemsz); - } + /* insert this item into the current buffer */ + if (btitem != (BTItem) NULL) + { + _bt_tapeadd(itape, btitem, itemsz); + } } /* * allocate a new, clean btree page, not linked to any siblings. */ static void -_bt_blnewpage(Relation index, Buffer *buf, Page *page, int flags) +_bt_blnewpage(Relation index, Buffer * buf, Page * page, int flags) { - BTPageOpaque opaque; + BTPageOpaque opaque; - *buf = _bt_getbuf(index, P_NEW, BT_WRITE); + *buf = _bt_getbuf(index, P_NEW, BT_WRITE); #if 0 - printf("\tblk=%d\n", BufferGetBlockNumber(*buf)); + printf("\tblk=%d\n", BufferGetBlockNumber(*buf)); #endif - *page = BufferGetPage(*buf); - _bt_pageinit(*page, BufferGetPageSize(*buf)); - opaque = (BTPageOpaque) PageGetSpecialPointer(*page); - opaque->btpo_prev = opaque->btpo_next = P_NONE; - opaque->btpo_flags = flags; + *page = BufferGetPage(*buf); + _bt_pageinit(*page, BufferGetPageSize(*buf)); + opaque = (BTPageOpaque) PageGetSpecialPointer(*page); + opaque->btpo_prev = opaque->btpo_next = P_NONE; + opaque->btpo_flags = flags; } /* @@ -790,42 +824,44 @@ _bt_blnewpage(Relation index, Buffer *buf, Page *page, int flags) static void _bt_slideleft(Relation index, Buffer buf, Page page) { - OffsetNumber off; - OffsetNumber maxoff; - ItemId previi; - ItemId thisii; - - if (!PageIsEmpty(page)) { - maxoff = PageGetMaxOffsetNumber(page); - previi = PageGetItemId(page, P_HIKEY); - for (off = P_FIRSTKEY; off <= maxoff; off = OffsetNumberNext(off)) { - thisii = PageGetItemId(page, off); - *previi = *thisii; - previi = thisii; + OffsetNumber off; + OffsetNumber maxoff; + ItemId previi; + ItemId thisii; + + if (!PageIsEmpty(page)) + { + maxoff = PageGetMaxOffsetNumber(page); + previi = PageGetItemId(page, P_HIKEY); + for (off = P_FIRSTKEY; off <= maxoff; off = OffsetNumberNext(off)) + { + thisii = PageGetItemId(page, off); + *previi = *thisii; + previi = thisii; + } + ((PageHeader) page)->pd_lower -= sizeof(ItemIdData); } - ((PageHeader) page)->pd_lower -= sizeof(ItemIdData); - } } /* * allocate and initialize a new BTPageState. the returned structure * is suitable for immediate use by _bt_buildadd. */ -static void * +static void * _bt_pagestate(Relation index, int flags, int level, bool doupper) { - BTPageState *state = (BTPageState *) palloc(sizeof(BTPageState)); - - memset((char *) state, 0, sizeof(BTPageState)); - _bt_blnewpage(index, &(state->btps_buf), &(state->btps_page), flags); - state->btps_firstoff = InvalidOffsetNumber; - state->btps_lastoff = P_HIKEY; - state->btps_lastbti = (BTItem) NULL; - state->btps_next = (BTPageState *) NULL; - state->btps_level = level; - state->btps_doupper = doupper; - - return((void *) state); + BTPageState *state = (BTPageState *) palloc(sizeof(BTPageState)); + + memset((char *) state, 0, sizeof(BTPageState)); + _bt_blnewpage(index, &(state->btps_buf), &(state->btps_page), flags); + state->btps_firstoff = InvalidOffsetNumber; + state->btps_lastoff = P_HIKEY; + state->btps_lastbti = (BTItem) NULL; + state->btps_next = (BTPageState *) NULL; + state->btps_level = level; + state->btps_doupper = doupper; + + return ((void *) state); } /* @@ -834,19 +870,19 @@ _bt_pagestate(Relation index, int flags, int level, bool doupper) * the page to which the item used to point, e.g., a heap page if * 'opage' is a leaf page). */ -static BTItem +static BTItem _bt_minitem(Page opage, BlockNumber oblkno, int atend) { - OffsetNumber off; - BTItem obti; - BTItem nbti; + OffsetNumber off; + BTItem obti; + BTItem nbti; - off = atend ? P_HIKEY : P_FIRSTKEY; - obti = (BTItem) PageGetItem(opage, PageGetItemId(opage, off)); - nbti = _bt_formitem(&(obti->bti_itup)); - ItemPointerSet(&(nbti->bti_itup.t_tid), oblkno, P_HIKEY); + off = atend ? P_HIKEY : P_FIRSTKEY; + obti = (BTItem) PageGetItem(opage, PageGetItemId(opage, off)); + nbti = _bt_formitem(&(obti->bti_itup)); + ItemPointerSet(&(nbti->bti_itup.t_tid), oblkno, P_HIKEY); - return(nbti); + return (nbti); } /* @@ -855,26 +891,26 @@ _bt_minitem(Page opage, BlockNumber oblkno, int atend) * we must be careful to observe the following restrictions, placed * upon us by the conventions in nbtsearch.c: * - rightmost pages start data items at P_HIKEY instead of at - * P_FIRSTKEY. + * P_FIRSTKEY. * - duplicates cannot be split among pages unless the chain of - * duplicates starts at the first data item. + * duplicates starts at the first data item. * * a leaf page being built looks like: * * +----------------+---------------------------------+ - * | PageHeaderData | linp0 linp1 linp2 ... | + * | PageHeaderData | linp0 linp1 linp2 ... | * +-----------+----+---------------------------------+ - * | ... linpN | ^ first | + * | ... linpN | ^ first | * +-----------+--------------------------------------+ - * | ^ last | - * | | - * | v last | + * | ^ last | + * | | + * | v last | * +-------------+------------------------------------+ - * | | itemN ... | + * | | itemN ... | * +-------------+------------------+-----------------+ - * | ... item3 item2 item1 | "special space" | + * | ... item3 item2 item1 | "special space" | * +--------------------------------+-----------------+ - * ^ first + * ^ first * * contrast this with the diagram in bufpage.h; note the mismatch * between linps and items. this is because we reserve linp0 as a @@ -888,216 +924,230 @@ _bt_minitem(Page opage, BlockNumber oblkno, int atend) * * if all keys are unique, 'first' will always be the same as 'last'. */ -static BTItem +static BTItem _bt_buildadd(Relation index, void *pstate, BTItem bti, int flags) { - BTPageState *state = (BTPageState *) pstate; - Buffer nbuf; - Page npage; - BTItem last_bti; - OffsetNumber first_off; - OffsetNumber last_off; - OffsetNumber off; - Size pgspc; - Size btisz; - - nbuf = state->btps_buf; - npage = state->btps_page; - first_off = state->btps_firstoff; - last_off = state->btps_lastoff; - last_bti = state->btps_lastbti; - - pgspc = PageGetFreeSpace(npage); - btisz = BTITEMSZ(bti); - btisz = DOUBLEALIGN(btisz); - if (pgspc < btisz) { - Buffer obuf = nbuf; - Page opage = npage; - OffsetNumber o, n; - ItemId ii; - ItemId hii; - - _bt_blnewpage(index, &nbuf, &npage, flags); + BTPageState *state = (BTPageState *) pstate; + Buffer nbuf; + Page npage; + BTItem last_bti; + OffsetNumber first_off; + OffsetNumber last_off; + OffsetNumber off; + Size pgspc; + Size btisz; + + nbuf = state->btps_buf; + npage = state->btps_page; + first_off = state->btps_firstoff; + last_off = state->btps_lastoff; + last_bti = state->btps_lastbti; + + pgspc = PageGetFreeSpace(npage); + btisz = BTITEMSZ(bti); + btisz = DOUBLEALIGN(btisz); + if (pgspc < btisz) + { + Buffer obuf = nbuf; + Page opage = npage; + OffsetNumber o, + n; + ItemId ii; + ItemId hii; - /* - * if 'last' is part of a chain of duplicates that does not - * start at the beginning of the old page, the entire chain is - * copied to the new page; we delete all of the duplicates - * from the old page except the first, which becomes the high - * key item of the old page. - * - * if the chain starts at the beginning of the page or there - * is no chain ('first' == 'last'), we need only copy 'last' - * to the new page. again, 'first' (== 'last') becomes the - * high key of the old page. - * - * note that in either case, we copy at least one item to the - * new page, so 'last_bti' will always be valid. 'bti' will - * never be the first data item on the new page. - */ - if (first_off == P_FIRSTKEY) { - Assert(last_off != P_FIRSTKEY); - first_off = last_off; - } - for (o = first_off, n = P_FIRSTKEY; - o <= last_off; - o = OffsetNumberNext(o), n = OffsetNumberNext(n)) { - ii = PageGetItemId(opage, o); - if ( PageAddItem(npage, PageGetItem(opage, ii), - ii->lp_len, n, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add item to the page in _bt_sort (1)"); + _bt_blnewpage(index, &nbuf, &npage, flags); + + /* + * if 'last' is part of a chain of duplicates that does not start + * at the beginning of the old page, the entire chain is copied to + * the new page; we delete all of the duplicates from the old page + * except the first, which becomes the high key item of the old + * page. + * + * if the chain starts at the beginning of the page or there is no + * chain ('first' == 'last'), we need only copy 'last' to the new + * page. again, 'first' (== 'last') becomes the high key of the + * old page. + * + * note that in either case, we copy at least one item to the new + * page, so 'last_bti' will always be valid. 'bti' will never be + * the first data item on the new page. + */ + if (first_off == P_FIRSTKEY) + { + Assert(last_off != P_FIRSTKEY); + first_off = last_off; + } + for (o = first_off, n = P_FIRSTKEY; + o <= last_off; + o = OffsetNumberNext(o), n = OffsetNumberNext(n)) + { + ii = PageGetItemId(opage, o); + if (PageAddItem(npage, PageGetItem(opage, ii), + ii->lp_len, n, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add item to the page in _bt_sort (1)"); #if 0 #if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_MERGE) - { - bool isnull; - BTItem tmpbti = - (BTItem) PageGetItem(npage, PageGetItemId(npage, n)); - Datum d = index_getattr(&(tmpbti->bti_itup), 1, - index->rd_att, &isnull); - printf("_bt_buildadd: moved <%x> to offset %d at level %d\n", - d, n, state->btps_level); - } -#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ + { + bool isnull; + BTItem tmpbti = + (BTItem) PageGetItem(npage, PageGetItemId(npage, n)); + Datum d = index_getattr(&(tmpbti->bti_itup), 1, + index->rd_att, &isnull); + + printf("_bt_buildadd: moved <%x> to offset %d at level %d\n", + d, n, state->btps_level); + } +#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ #endif - } - /* - * this loop is backward because PageIndexTupleDelete shuffles - * the tuples to fill holes in the page -- by starting at the - * end and working back, we won't create holes (and thereby - * avoid shuffling). - */ - for (o = last_off; o > first_off; o = OffsetNumberPrev(o)) { - PageIndexTupleDelete(opage, o); - } - hii = PageGetItemId(opage, P_HIKEY); - ii = PageGetItemId(opage, first_off); - *hii = *ii; - ii->lp_flags &= ~LP_USED; - ((PageHeader) opage)->pd_lower -= sizeof(ItemIdData); + } - first_off = P_FIRSTKEY; - last_off = PageGetMaxOffsetNumber(npage); - last_bti = (BTItem) PageGetItem(npage, PageGetItemId(npage, last_off)); + /* + * this loop is backward because PageIndexTupleDelete shuffles the + * tuples to fill holes in the page -- by starting at the end and + * working back, we won't create holes (and thereby avoid + * shuffling). + */ + for (o = last_off; o > first_off; o = OffsetNumberPrev(o)) + { + PageIndexTupleDelete(opage, o); + } + hii = PageGetItemId(opage, P_HIKEY); + ii = PageGetItemId(opage, first_off); + *hii = *ii; + ii->lp_flags &= ~LP_USED; + ((PageHeader) opage)->pd_lower -= sizeof(ItemIdData); - /* - * set the page (side link) pointers. - */ - { - BTPageOpaque oopaque = (BTPageOpaque) PageGetSpecialPointer(opage); - BTPageOpaque nopaque = (BTPageOpaque) PageGetSpecialPointer(npage); - - oopaque->btpo_next = BufferGetBlockNumber(nbuf); - nopaque->btpo_prev = BufferGetBlockNumber(obuf); - nopaque->btpo_next = P_NONE; - - if ( _bt_itemcmp(index, _bt_nattr, - (BTItem) PageGetItem(opage, PageGetItemId(opage, P_HIKEY)), - (BTItem) PageGetItem(opage, PageGetItemId(opage, P_FIRSTKEY)), - BTEqualStrategyNumber) ) - oopaque->btpo_flags |= BTP_CHAIN; - } + first_off = P_FIRSTKEY; + last_off = PageGetMaxOffsetNumber(npage); + last_bti = (BTItem) PageGetItem(npage, PageGetItemId(npage, last_off)); - /* - * copy the old buffer's minimum key to its parent. if we - * don't have a parent, we have to create one; this adds a new - * btree level. - */ - if (state->btps_doupper) { - BTItem nbti; - - if (state->btps_next == (BTPageState *) NULL) { - state->btps_next = - _bt_pagestate(index, 0, state->btps_level + 1, true); - } - nbti = _bt_minitem(opage, BufferGetBlockNumber(obuf), 0); - _bt_buildadd(index, state->btps_next, nbti, 0); - pfree((void *) nbti); + /* + * set the page (side link) pointers. + */ + { + BTPageOpaque oopaque = (BTPageOpaque) PageGetSpecialPointer(opage); + BTPageOpaque nopaque = (BTPageOpaque) PageGetSpecialPointer(npage); + + oopaque->btpo_next = BufferGetBlockNumber(nbuf); + nopaque->btpo_prev = BufferGetBlockNumber(obuf); + nopaque->btpo_next = P_NONE; + + if (_bt_itemcmp(index, _bt_nattr, + (BTItem) PageGetItem(opage, PageGetItemId(opage, P_HIKEY)), + (BTItem) PageGetItem(opage, PageGetItemId(opage, P_FIRSTKEY)), + BTEqualStrategyNumber)) + oopaque->btpo_flags |= BTP_CHAIN; + } + + /* + * copy the old buffer's minimum key to its parent. if we don't + * have a parent, we have to create one; this adds a new btree + * level. + */ + if (state->btps_doupper) + { + BTItem nbti; + + if (state->btps_next == (BTPageState *) NULL) + { + state->btps_next = + _bt_pagestate(index, 0, state->btps_level + 1, true); + } + nbti = _bt_minitem(opage, BufferGetBlockNumber(obuf), 0); + _bt_buildadd(index, state->btps_next, nbti, 0); + pfree((void *) nbti); + } + + /* + * write out the old stuff. we never want to see it again, so we + * can give up our lock (if we had one; BuildingBtree is set, so + * we aren't locking). + */ + _bt_wrtbuf(index, obuf); } /* - * write out the old stuff. we never want to see it again, so - * we can give up our lock (if we had one; BuildingBtree is - * set, so we aren't locking). + * if this item is different from the last item added, we start a new + * chain of duplicates. */ - _bt_wrtbuf(index, obuf); - } - - /* - * if this item is different from the last item added, we start a - * new chain of duplicates. - */ - off = OffsetNumberNext(last_off); - if ( PageAddItem(npage, (Item) bti, btisz, off, LP_USED) == InvalidOffsetNumber ) - elog (FATAL, "btree: failed to add item to the page in _bt_sort (2)"); + off = OffsetNumberNext(last_off); + if (PageAddItem(npage, (Item) bti, btisz, off, LP_USED) == InvalidOffsetNumber) + elog(FATAL, "btree: failed to add item to the page in _bt_sort (2)"); #if 0 #if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_MERGE) - { - bool isnull; - Datum d = index_getattr(&(bti->bti_itup), 1, index->rd_att, &isnull); - printf("_bt_buildadd: inserted <%x> at offset %d at level %d\n", - d, off, state->btps_level); - } -#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ + { + bool isnull; + Datum d = index_getattr(&(bti->bti_itup), 1, index->rd_att, &isnull); + + printf("_bt_buildadd: inserted <%x> at offset %d at level %d\n", + d, off, state->btps_level); + } +#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ #endif - if (last_bti == (BTItem) NULL) - { - first_off = P_FIRSTKEY; - } - else if ( !_bt_itemcmp(index, _bt_nattr, - bti, last_bti, BTEqualStrategyNumber) ) - { - first_off = off; - } - last_off = off; - last_bti = (BTItem) PageGetItem(npage, PageGetItemId(npage, off)); - - state->btps_buf = nbuf; - state->btps_page = npage; - state->btps_lastbti = last_bti; - state->btps_lastoff = last_off; - state->btps_firstoff = first_off; - - return(last_bti); + if (last_bti == (BTItem) NULL) + { + first_off = P_FIRSTKEY; + } + else if (!_bt_itemcmp(index, _bt_nattr, + bti, last_bti, BTEqualStrategyNumber)) + { + first_off = off; + } + last_off = off; + last_bti = (BTItem) PageGetItem(npage, PageGetItemId(npage, off)); + + state->btps_buf = nbuf; + state->btps_page = npage; + state->btps_lastbti = last_bti; + state->btps_lastoff = last_off; + state->btps_firstoff = first_off; + + return (last_bti); } static void -_bt_uppershutdown(Relation index, BTPageState *state) +_bt_uppershutdown(Relation index, BTPageState * state) { - BTPageState *s; - BlockNumber blkno; - BTPageOpaque opaque; - BTItem bti; + BTPageState *s; + BlockNumber blkno; + BTPageOpaque opaque; + BTItem bti; - for (s = state; s != (BTPageState *) NULL; s = s->btps_next) { - blkno = BufferGetBlockNumber(s->btps_buf); - opaque = (BTPageOpaque) PageGetSpecialPointer(s->btps_page); + for (s = state; s != (BTPageState *) NULL; s = s->btps_next) + { + blkno = BufferGetBlockNumber(s->btps_buf); + opaque = (BTPageOpaque) PageGetSpecialPointer(s->btps_page); - /* - * if this is the root, attach it to the metapage. otherwise, - * stick the minimum key of the last page on this level (which - * has not been split, or else it wouldn't be the last page) - * into its parent. this may cause the last page of upper - * levels to split, but that's not a problem -- we haven't - * gotten to them yet. - */ - if (s->btps_doupper) { - if (s->btps_next == (BTPageState *) NULL) { - opaque->btpo_flags |= BTP_ROOT; - _bt_metaproot(index, blkno, s->btps_level + 1); - } else { - bti = _bt_minitem(s->btps_page, blkno, 0); - _bt_buildadd(index, s->btps_next, bti, 0); - pfree((void *) bti); - } - } + /* + * if this is the root, attach it to the metapage. otherwise, + * stick the minimum key of the last page on this level (which has + * not been split, or else it wouldn't be the last page) into its + * parent. this may cause the last page of upper levels to split, + * but that's not a problem -- we haven't gotten to them yet. + */ + if (s->btps_doupper) + { + if (s->btps_next == (BTPageState *) NULL) + { + opaque->btpo_flags |= BTP_ROOT; + _bt_metaproot(index, blkno, s->btps_level + 1); + } + else + { + bti = _bt_minitem(s->btps_page, blkno, 0); + _bt_buildadd(index, s->btps_next, bti, 0); + pfree((void *) bti); + } + } - /* - * this is the rightmost page, so the ItemId array needs to be - * slid back one slot. - */ - _bt_slideleft(index, s->btps_buf, s->btps_page); - _bt_wrtbuf(index, s->btps_buf); - } + /* + * this is the rightmost page, so the ItemId array needs to be + * slid back one slot. + */ + _bt_slideleft(index, s->btps_buf, s->btps_page); + _bt_wrtbuf(index, s->btps_buf); + } } /* @@ -1105,203 +1155,230 @@ _bt_uppershutdown(Relation index, BTPageState *state) * merging passes until at most one run is left in each tape. at that * point, merge the final tape runs into a set of btree leaves. * - * XXX three nested loops? gross. cut me up into smaller routines. + * XXX three nested loops? gross. cut me up into smaller routines. */ static void -_bt_merge(Relation index, BTSpool *btspool) +_bt_merge(Relation index, BTSpool * btspool) { - BTPageState *state; - BTPriQueue q; - BTPriQueueElem e; - BTSortKey btsk; - BTItem bti; - BTTapeBlock *itape; - BTTapeBlock *otape; - char *tapepos[MAXTAPES]; - int tapedone[MAXTAPES]; - int t; - int goodtapes; - int npass; - int nruns; - Size btisz; - bool doleaf = false; - - /* - * initialize state needed for the merge into the btree leaf pages. - */ - state = (BTPageState *) _bt_pagestate(index, BTP_LEAF, 0, true); - - npass = 0; - do { /* pass */ + BTPageState *state; + BTPriQueue q; + BTPriQueueElem e; + BTSortKey btsk; + BTItem bti; + BTTapeBlock *itape; + BTTapeBlock *otape; + char *tapepos[MAXTAPES]; + int tapedone[MAXTAPES]; + int t; + int goodtapes; + int npass; + int nruns; + Size btisz; + bool doleaf = false; + /* - * each pass starts by flushing the previous outputs and - * swapping inputs and outputs. flushing sets End-of-Run for - * any dirty output tapes. swapping clears the new output - * tapes and rewinds the new input tapes. + * initialize state needed for the merge into the btree leaf pages. */ - btspool->bts_tape = btspool->bts_ntapes - 1; - _bt_spoolflush(btspool); - _bt_spoolswap(btspool); - - ++npass; - nruns = 0; - - for (;;) { /* run */ - /* - * each run starts by selecting a new output tape. the - * merged results of a given run are always sent to this - * one tape. - */ - btspool->bts_tape = (btspool->bts_tape + 1) % btspool->bts_ntapes; - otape = btspool->bts_otape[btspool->bts_tape]; - - /* - * initialize the priority queue by loading it with the - * first element of the given run in each tape. since we - * are starting a new run, we reset the tape (clearing the - * End-Of-Run marker) before reading it. this means that - * _bt_taperead will return 0 only if the tape is actually - * at EOF. - */ - memset((char *) &q, 0, sizeof(BTPriQueue)); - goodtapes = 0; - for (t = 0; t < btspool->bts_ntapes; ++t) { - itape = btspool->bts_itape[t]; - tapepos[t] = itape->bttb_data; - tapedone[t] = 0; - _bt_tapereset(itape); - do { - if (_bt_taperead(itape) == 0) { - tapedone[t] = 1; - } - } while (!tapedone[t] && EMPTYTAPE(itape)); - if (!tapedone[t]) { - ++goodtapes; - e.btpqe_tape = t; - _bt_setsortkey(index, _bt_tapenext(itape, &tapepos[t]), - &(e.btpqe_item)); - if (e.btpqe_item.btsk_item != (BTItem) NULL) { - _bt_pqadd(&q, &e); - } - } - } - /* - * if we don't have any tapes with any input (i.e., they - * are all at EOF), there is no work to do in this run -- - * we must be done with this pass. - */ - if (goodtapes == 0) { - break; /* for */ - } - ++nruns; - - /* - * output the smallest element from the queue until there - * are no more. - */ - while (_bt_pqnext(&q, &e) >= 0) { /* item */ + state = (BTPageState *) _bt_pagestate(index, BTP_LEAF, 0, true); + + npass = 0; + do + { /* pass */ + /* - * replace the element taken from priority queue, - * fetching a new block if needed. a tape can run out - * if it hits either End-Of-Run or EOF. + * each pass starts by flushing the previous outputs and swapping + * inputs and outputs. flushing sets End-of-Run for any dirty + * output tapes. swapping clears the new output tapes and rewinds + * the new input tapes. */ - t = e.btpqe_tape; - btsk = e.btpqe_item; - bti = btsk.btsk_item; - if (bti != (BTItem) NULL) { - btisz = BTITEMSZ(bti); - btisz = DOUBLEALIGN(btisz); - if (doleaf) { - _bt_buildadd(index, state, bti, BTP_LEAF); -#if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_MERGE) + btspool->bts_tape = btspool->bts_ntapes - 1; + _bt_spoolflush(btspool); + _bt_spoolswap(btspool); + + ++npass; + nruns = 0; + + for (;;) + { /* run */ + + /* + * each run starts by selecting a new output tape. the merged + * results of a given run are always sent to this one tape. + */ + btspool->bts_tape = (btspool->bts_tape + 1) % btspool->bts_ntapes; + otape = btspool->bts_otape[btspool->bts_tape]; + + /* + * initialize the priority queue by loading it with the first + * element of the given run in each tape. since we are + * starting a new run, we reset the tape (clearing the + * End-Of-Run marker) before reading it. this means that + * _bt_taperead will return 0 only if the tape is actually at + * EOF. + */ + memset((char *) &q, 0, sizeof(BTPriQueue)); + goodtapes = 0; + for (t = 0; t < btspool->bts_ntapes; ++t) { - bool isnull; - Datum d = index_getattr(&(bti->bti_itup), 1, - index->rd_att, &isnull); - printf("_bt_merge: [pass %d run %d] inserted <%x> from tape %d into block %d\n", - npass, nruns, d, t, - BufferGetBlockNumber(state->btps_buf)); + itape = btspool->bts_itape[t]; + tapepos[t] = itape->bttb_data; + tapedone[t] = 0; + _bt_tapereset(itape); + do + { + if (_bt_taperead(itape) == 0) + { + tapedone[t] = 1; + } + } while (!tapedone[t] && EMPTYTAPE(itape)); + if (!tapedone[t]) + { + ++goodtapes; + e.btpqe_tape = t; + _bt_setsortkey(index, _bt_tapenext(itape, &tapepos[t]), + &(e.btpqe_item)); + if (e.btpqe_item.btsk_item != (BTItem) NULL) + { + _bt_pqadd(&q, &e); + } + } } -#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ - } else { - if (SPCLEFT(otape) < btisz) { - /* - * if it's full, write it out and add the - * item to the next block. (since we will - * be adding another tuple immediately - * after this, we can be sure that there - * will be at least one more block in this - * run and so we know we do *not* want to - * set End-Of-Run here.) - */ - _bt_tapewrite(otape, 0); - } - _bt_tapeadd(otape, bti, btisz); -#if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_MERGE) + + /* + * if we don't have any tapes with any input (i.e., they are + * all at EOF), there is no work to do in this run -- we must + * be done with this pass. + */ + if (goodtapes == 0) { - bool isnull; - Datum d = index_getattr(&(bti->bti_itup), 1, - index->rd_att, &isnull); - printf("_bt_merge: [pass %d run %d] inserted <%x> from tape %d into output tape %d\n", - npass, nruns, d, t, - btspool->bts_tape); - } -#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ - } - - if ( btsk.btsk_datum != (Datum*) NULL ) - pfree ((void*)(btsk.btsk_datum)); - if ( btsk.btsk_nulls != (char*) NULL ) - pfree ((void*)(btsk.btsk_nulls)); - - } - itape = btspool->bts_itape[t]; - if (!tapedone[t]) { - BTItem newbti = _bt_tapenext(itape, &tapepos[t]); - - if (newbti == (BTItem) NULL) { - do { - if (_bt_taperead(itape) == 0) { - tapedone[t] = 1; - } - } while (!tapedone[t] && EMPTYTAPE(itape)); - if (!tapedone[t]) { - tapepos[t] = itape->bttb_data; - newbti = _bt_tapenext(itape, &tapepos[t]); + break; /* for */ } - } - if (newbti != (BTItem) NULL) { - BTPriQueueElem nexte; - - nexte.btpqe_tape = t; - _bt_setsortkey(index, newbti, &(nexte.btpqe_item)); - _bt_pqadd(&q, &nexte); - } + ++nruns; + + /* + * output the smallest element from the queue until there are + * no more. + */ + while (_bt_pqnext(&q, &e) >= 0) + { /* item */ + + /* + * replace the element taken from priority queue, fetching + * a new block if needed. a tape can run out if it hits + * either End-Of-Run or EOF. + */ + t = e.btpqe_tape; + btsk = e.btpqe_item; + bti = btsk.btsk_item; + if (bti != (BTItem) NULL) + { + btisz = BTITEMSZ(bti); + btisz = DOUBLEALIGN(btisz); + if (doleaf) + { + _bt_buildadd(index, state, bti, BTP_LEAF); +#if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_MERGE) + { + bool isnull; + Datum d = index_getattr(&(bti->bti_itup), 1, + index->rd_att, &isnull); + + printf("_bt_merge: [pass %d run %d] inserted <%x> from tape %d into block %d\n", + npass, nruns, d, t, + BufferGetBlockNumber(state->btps_buf)); + } +#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ + } + else + { + if (SPCLEFT(otape) < btisz) + { + + /* + * if it's full, write it out and add the item + * to the next block. (since we will be + * adding another tuple immediately after + * this, we can be sure that there will be at + * least one more block in this run and so we + * know we do *not* want to set End-Of-Run + * here.) + */ + _bt_tapewrite(otape, 0); + } + _bt_tapeadd(otape, bti, btisz); +#if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_MERGE) + { + bool isnull; + Datum d = index_getattr(&(bti->bti_itup), 1, + index->rd_att, &isnull); + + printf("_bt_merge: [pass %d run %d] inserted <%x> from tape %d into output tape %d\n", + npass, nruns, d, t, + btspool->bts_tape); + } +#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ + } + + if (btsk.btsk_datum != (Datum *) NULL) + pfree((void *) (btsk.btsk_datum)); + if (btsk.btsk_nulls != (char *) NULL) + pfree((void *) (btsk.btsk_nulls)); + + } + itape = btspool->bts_itape[t]; + if (!tapedone[t]) + { + BTItem newbti = _bt_tapenext(itape, &tapepos[t]); + + if (newbti == (BTItem) NULL) + { + do + { + if (_bt_taperead(itape) == 0) + { + tapedone[t] = 1; + } + } while (!tapedone[t] && EMPTYTAPE(itape)); + if (!tapedone[t]) + { + tapepos[t] = itape->bttb_data; + newbti = _bt_tapenext(itape, &tapepos[t]); + } + } + if (newbti != (BTItem) NULL) + { + BTPriQueueElem nexte; + + nexte.btpqe_tape = t; + _bt_setsortkey(index, newbti, &(nexte.btpqe_item)); + _bt_pqadd(&q, &nexte); + } + } + } /* item */ + + /* + * that's it for this run. flush the output tape, marking + * End-of-Run. + */ + _bt_tapewrite(otape, 1); + } /* run */ + + /* + * we are here because we ran out of input on all of the input + * tapes. + * + * if this pass did not generate more actual output runs than we have + * tapes, we know we have at most one run in each tape. this + * means that we are ready to merge into the final btree leaf + * pages instead of merging into a tape file. + */ + if (nruns <= btspool->bts_ntapes) + { + doleaf = true; } - } /* item */ - - /* - * that's it for this run. flush the output tape, marking - * End-of-Run. - */ - _bt_tapewrite(otape, 1); - } /* run */ - - /* - * we are here because we ran out of input on all of the input - * tapes. - * - * if this pass did not generate more actual output runs than - * we have tapes, we know we have at most one run in each - * tape. this means that we are ready to merge into the final - * btree leaf pages instead of merging into a tape file. - */ - if (nruns <= btspool->bts_ntapes) { - doleaf = true; - } - } while (nruns > 0); /* pass */ + } while (nruns > 0); /* pass */ - _bt_uppershutdown(index, state); + _bt_uppershutdown(index, state); } @@ -1320,62 +1397,65 @@ _bt_merge(Relation index, BTSpool *btspool) void _bt_upperbuild(Relation index) { - Buffer rbuf; - BlockNumber blk; - Page rpage; - BTPageOpaque ropaque; - BTPageState *state; - BTItem nbti; - - /* - * find the first leaf block. while we're at it, clear the - * BTP_ROOT flag that we set while building it (so we could find - * it later). - */ - rbuf = _bt_getroot(index, BT_WRITE); - blk = BufferGetBlockNumber(rbuf); - rpage = BufferGetPage(rbuf); - ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage); - ropaque->btpo_flags &= ~BTP_ROOT; - _bt_wrtbuf(index, rbuf); - - state = (BTPageState *) _bt_pagestate(index, 0, 0, true); - - /* for each page... */ - do { -#if 0 - printf("\t\tblk=%d\n", blk); -#endif - rbuf = _bt_getbuf(index, blk, BT_READ); + Buffer rbuf; + BlockNumber blk; + Page rpage; + BTPageOpaque ropaque; + BTPageState *state; + BTItem nbti; + + /* + * find the first leaf block. while we're at it, clear the BTP_ROOT + * flag that we set while building it (so we could find it later). + */ + rbuf = _bt_getroot(index, BT_WRITE); + blk = BufferGetBlockNumber(rbuf); rpage = BufferGetPage(rbuf); ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage); - - /* for each item... */ - if (!PageIsEmpty(rpage)) { - /* - * form a new index tuple corresponding to the minimum key - * of the lower page and insert it into a page at this - * level. - */ - nbti = _bt_minitem(rpage, blk, P_RIGHTMOST(ropaque)); + ropaque->btpo_flags &= ~BTP_ROOT; + _bt_wrtbuf(index, rbuf); + + state = (BTPageState *) _bt_pagestate(index, 0, 0, true); + + /* for each page... */ + do + { +#if 0 + printf("\t\tblk=%d\n", blk); +#endif + rbuf = _bt_getbuf(index, blk, BT_READ); + rpage = BufferGetPage(rbuf); + ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage); + + /* for each item... */ + if (!PageIsEmpty(rpage)) + { + + /* + * form a new index tuple corresponding to the minimum key of + * the lower page and insert it into a page at this level. + */ + nbti = _bt_minitem(rpage, blk, P_RIGHTMOST(ropaque)); #if defined(FASTBUILD_DEBUG) && defined(FASTBUILD_MERGE) - { - bool isnull; - Datum d = index_getattr(&(nbti->bti_itup), 1, index->rd_att, - &isnull); - printf("_bt_upperbuild: inserting <%x> at %d\n", - d, state->btps_level); - } -#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ - _bt_buildadd(index, state, nbti, 0); - pfree((void *) nbti); - } - blk = ropaque->btpo_next; - _bt_relbuf(index, rbuf, BT_READ); - } while (blk != P_NONE); - - _bt_uppershutdown(index, state); + { + bool isnull; + Datum d = index_getattr(&(nbti->bti_itup), 1, index->rd_att, + &isnull); + + printf("_bt_upperbuild: inserting <%x> at %d\n", + d, state->btps_level); + } +#endif /* FASTBUILD_DEBUG && FASTBUILD_MERGE */ + _bt_buildadd(index, state, nbti, 0); + pfree((void *) nbti); + } + blk = ropaque->btpo_next; + _bt_relbuf(index, rbuf, BT_READ); + } while (blk != P_NONE); + + _bt_uppershutdown(index, state); } + #endif /* @@ -1385,17 +1465,17 @@ _bt_upperbuild(Relation index) void _bt_leafbuild(Relation index, void *spool) { - _bt_isortcmpinit (index, (BTSpool *) spool); + _bt_isortcmpinit(index, (BTSpool *) spool); #ifdef BTREE_BUILD_STATS - if ( ShowExecutorStats ) - { - fprintf(stderr, "! BtreeBuild (Spool) Stats:\n"); - ShowUsage (); - ResetUsage (); - } + if (ShowExecutorStats) + { + fprintf(stderr, "! BtreeBuild (Spool) Stats:\n"); + ShowUsage(); + ResetUsage(); + } #endif - _bt_merge(index, (BTSpool *) spool); + _bt_merge(index, (BTSpool *) spool); } diff --git a/src/backend/access/nbtree/nbtstrat.c b/src/backend/access/nbtree/nbtstrat.c index 6de003c06a9..5215d2000d8 100644 --- a/src/backend/access/nbtree/nbtstrat.c +++ b/src/backend/access/nbtree/nbtstrat.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * btstrat.c-- - * Srategy map entries for the btree indexed access method + * Srategy map entries for the btree indexed access method * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtstrat.c,v 1.4 1996/11/05 10:35:37 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtstrat.c,v 1.5 1997/09/07 04:39:04 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,111 +20,111 @@ /* * Note: - * StrategyNegate, StrategyCommute, and StrategyNegateCommute - * assume <, <=, ==, >=, > ordering. + * StrategyNegate, StrategyCommute, and StrategyNegateCommute + * assume <, <=, ==, >=, > ordering. */ -static StrategyNumber BTNegate[5] = { - BTGreaterEqualStrategyNumber, - BTGreaterStrategyNumber, - InvalidStrategy, - BTLessStrategyNumber, - BTLessEqualStrategyNumber +static StrategyNumber BTNegate[5] = { + BTGreaterEqualStrategyNumber, + BTGreaterStrategyNumber, + InvalidStrategy, + BTLessStrategyNumber, + BTLessEqualStrategyNumber }; -static StrategyNumber BTCommute[5] = { - BTGreaterStrategyNumber, - BTGreaterEqualStrategyNumber, - InvalidStrategy, - BTLessEqualStrategyNumber, - BTLessStrategyNumber +static StrategyNumber BTCommute[5] = { + BTGreaterStrategyNumber, + BTGreaterEqualStrategyNumber, + InvalidStrategy, + BTLessEqualStrategyNumber, + BTLessStrategyNumber }; -static StrategyNumber BTNegateCommute[5] = { - BTLessEqualStrategyNumber, - BTLessStrategyNumber, - InvalidStrategy, - BTGreaterStrategyNumber, - BTGreaterEqualStrategyNumber +static StrategyNumber BTNegateCommute[5] = { + BTLessEqualStrategyNumber, + BTLessStrategyNumber, + InvalidStrategy, + BTGreaterStrategyNumber, + BTGreaterEqualStrategyNumber }; -static uint16 BTLessTermData[] = { /* XXX type clash */ - 2, - BTLessStrategyNumber, - SK_NEGATE, - BTLessStrategyNumber, - SK_NEGATE | SK_COMMUTE +static uint16 BTLessTermData[] = { /* XXX type clash */ + 2, + BTLessStrategyNumber, + SK_NEGATE, + BTLessStrategyNumber, + SK_NEGATE | SK_COMMUTE }; -static uint16 BTLessEqualTermData[] = { /* XXX type clash */ - 2, - BTLessEqualStrategyNumber, - 0x0, - BTLessEqualStrategyNumber, - SK_COMMUTE +static uint16 BTLessEqualTermData[] = { /* XXX type clash */ + 2, + BTLessEqualStrategyNumber, + 0x0, + BTLessEqualStrategyNumber, + SK_COMMUTE }; static uint16 BTGreaterEqualTermData[] = { /* XXX type clash */ - 2, - BTGreaterEqualStrategyNumber, - 0x0, - BTGreaterEqualStrategyNumber, - SK_COMMUTE - }; - -static uint16 BTGreaterTermData[] = { /* XXX type clash */ - 2, - BTGreaterStrategyNumber, - SK_NEGATE, - BTGreaterStrategyNumber, - SK_NEGATE | SK_COMMUTE + 2, + BTGreaterEqualStrategyNumber, + 0x0, + BTGreaterEqualStrategyNumber, + SK_COMMUTE }; -static StrategyTerm BTEqualExpressionData[] = { - (StrategyTerm)BTLessTermData, /* XXX */ - (StrategyTerm)BTLessEqualTermData, /* XXX */ - (StrategyTerm)BTGreaterEqualTermData, /* XXX */ - (StrategyTerm)BTGreaterTermData, /* XXX */ - NULL +static uint16 BTGreaterTermData[] = { /* XXX type clash */ + 2, + BTGreaterStrategyNumber, + SK_NEGATE, + BTGreaterStrategyNumber, + SK_NEGATE | SK_COMMUTE }; -static StrategyEvaluationData BTEvaluationData = { - /* XXX static for simplicity */ - - BTMaxStrategyNumber, - (StrategyTransformMap)BTNegate, /* XXX */ - (StrategyTransformMap)BTCommute, /* XXX */ - (StrategyTransformMap)BTNegateCommute, /* XXX */ +static StrategyTerm BTEqualExpressionData[] = { + (StrategyTerm) BTLessTermData, /* XXX */ + (StrategyTerm) BTLessEqualTermData, /* XXX */ + (StrategyTerm) BTGreaterEqualTermData, /* XXX */ + (StrategyTerm) BTGreaterTermData, /* XXX */ + NULL +}; + +static StrategyEvaluationData BTEvaluationData = { + /* XXX static for simplicity */ + + BTMaxStrategyNumber, + (StrategyTransformMap) BTNegate, /* XXX */ + (StrategyTransformMap) BTCommute, /* XXX */ + (StrategyTransformMap) BTNegateCommute, /* XXX */ - { NULL, NULL, (StrategyExpression)BTEqualExpressionData, NULL, NULL, - NULL,NULL,NULL,NULL,NULL,NULL,NULL} + {NULL, NULL, (StrategyExpression) BTEqualExpressionData, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL} }; /* ---------------------------------------------------------------- - * RelationGetBTStrategy + * RelationGetBTStrategy * ---------------------------------------------------------------- */ StrategyNumber _bt_getstrat(Relation rel, - AttrNumber attno, - RegProcedure proc) + AttrNumber attno, + RegProcedure proc) { - StrategyNumber strat; - - strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc); - - Assert(StrategyNumberIsValid(strat)); - - return (strat); + StrategyNumber strat; + + strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc); + + Assert(StrategyNumberIsValid(strat)); + + return (strat); } bool _bt_invokestrat(Relation rel, - AttrNumber attno, - StrategyNumber strat, - Datum left, - Datum right) + AttrNumber attno, + StrategyNumber strat, + Datum left, + Datum right) { - return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat, - left, right)); + return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat, + left, right)); } diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index 738e55dbccd..096f1d2691e 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * btutils.c-- - * Utility code for Postgres btree implementation. + * Utility code for Postgres btree implementation. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.11 1997/08/19 21:29:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.12 1997/09/07 04:39:05 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,367 +23,384 @@ #include <catalog/pg_proc.h> #include <executor/execdebug.h> -extern int NIndexTupleProcessed; +extern int NIndexTupleProcessed; #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -ScanKey +ScanKey _bt_mkscankey(Relation rel, IndexTuple itup) -{ - ScanKey skey; - TupleDesc itupdesc; - int natts; - int i; - Datum arg; - RegProcedure proc; - bool null; - bits16 flag; - - natts = rel->rd_rel->relnatts; - itupdesc = RelationGetTupleDescriptor(rel); - - skey = (ScanKey) palloc(natts * sizeof(ScanKeyData)); - - for (i = 0; i < natts; i++) { - arg = index_getattr(itup, i + 1, itupdesc, &null); - if ( null ) - { - proc = NullValueRegProcedure; - flag = SK_ISNULL; - } - else +{ + ScanKey skey; + TupleDesc itupdesc; + int natts; + int i; + Datum arg; + RegProcedure proc; + bool null; + bits16 flag; + + natts = rel->rd_rel->relnatts; + itupdesc = RelationGetTupleDescriptor(rel); + + skey = (ScanKey) palloc(natts * sizeof(ScanKeyData)); + + for (i = 0; i < natts; i++) { - proc = index_getprocid(rel, i + 1, BTORDER_PROC); - flag = 0x0; + arg = index_getattr(itup, i + 1, itupdesc, &null); + if (null) + { + proc = NullValueRegProcedure; + flag = SK_ISNULL; + } + else + { + proc = index_getprocid(rel, i + 1, BTORDER_PROC); + flag = 0x0; + } + ScanKeyEntryInitialize(&skey[i], + flag, (AttrNumber) (i + 1), proc, arg); } - ScanKeyEntryInitialize(&skey[i], - flag, (AttrNumber) (i + 1), proc, arg); - } - - return (skey); + + return (skey); } void _bt_freeskey(ScanKey skey) { - pfree(skey); + pfree(skey); } void _bt_freestack(BTStack stack) { - BTStack ostack; - - while (stack != (BTStack) NULL) { - ostack = stack; - stack = stack->bts_parent; - pfree(ostack->bts_btitem); - pfree(ostack); - } + BTStack ostack; + + while (stack != (BTStack) NULL) + { + ostack = stack; + stack = stack->bts_parent; + pfree(ostack->bts_btitem); + pfree(ostack); + } } /* - * _bt_orderkeys() -- Put keys in a sensible order for conjunctive quals. + * _bt_orderkeys() -- Put keys in a sensible order for conjunctive quals. * - * The order of the keys in the qual match the ordering imposed by - * the index. This routine only needs to be called if there are - * more than one qual clauses using this index. + * The order of the keys in the qual match the ordering imposed by + * the index. This routine only needs to be called if there are + * more than one qual clauses using this index. */ void _bt_orderkeys(Relation relation, BTScanOpaque so) { - ScanKey xform; - ScanKeyData *cur; - StrategyMap map; - int nbytes; - long test; - int i, j; - int init[BTMaxStrategyNumber+1]; - ScanKey key; - uint16 numberOfKeys = so->numberOfKeys; - uint16 new_numberOfKeys = 0; - AttrNumber attno = 1; - - if ( numberOfKeys < 1 ) - return; - - key = so->keyData; - - cur = &key[0]; - if ( cur->sk_attno != 1 ) - elog (WARN, "_bt_orderkeys: key(s) for attribute 1 missed"); - - if ( numberOfKeys == 1 ) - { - /* - * We don't use indices for 'A is null' and 'A is not null' - * currently and 'A < = > <> NULL' is non-sense' - so - * qual is not Ok. - vadim 03/21/97 - */ - if ( cur->sk_flags & SK_ISNULL ) - so->qual_ok = 0; - so->numberOfFirstKeys = 1; - return; - } - - /* get space for the modified array of keys */ - nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData); - xform = (ScanKey) palloc(nbytes); - - memset(xform, 0, nbytes); - map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), - BTMaxStrategyNumber, - attno); - for (j = 0; j <= BTMaxStrategyNumber; j++) - init[j] = 0; - - /* check each key passed in */ - for (i = 0; ; ) - { - if ( i < numberOfKeys ) - cur = &key[i]; - - if ( cur->sk_flags & SK_ISNULL ) /* see comments above */ - so->qual_ok = 0; - - if ( i == numberOfKeys || cur->sk_attno != attno ) + ScanKey xform; + ScanKeyData *cur; + StrategyMap map; + int nbytes; + long test; + int i, + j; + int init[BTMaxStrategyNumber + 1]; + ScanKey key; + uint16 numberOfKeys = so->numberOfKeys; + uint16 new_numberOfKeys = 0; + AttrNumber attno = 1; + + if (numberOfKeys < 1) + return; + + key = so->keyData; + + cur = &key[0]; + if (cur->sk_attno != 1) + elog(WARN, "_bt_orderkeys: key(s) for attribute 1 missed"); + + if (numberOfKeys == 1) { - if ( cur->sk_attno != attno + 1 && i < numberOfKeys ) - { - elog (WARN, "_bt_orderkeys: key(s) for attribute %d missed", attno + 1); - } - /* - * If = has been specified, no other key will be used. - * In case of key < 2 && key == 1 and so on - * we have to set qual_ok to 0 - */ - if (init[BTEqualStrategyNumber - 1]) - { - ScanKeyData *eq, *chk; - - eq = &xform[BTEqualStrategyNumber - 1]; - for (j = BTMaxStrategyNumber; --j >= 0; ) - { - if ( j == (BTEqualStrategyNumber - 1) || init[j] == 0 ) - continue; - chk = &xform[j]; - test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument); - if (!test) - so->qual_ok = 0; - } - init[BTLessStrategyNumber - 1] = 0; - init[BTLessEqualStrategyNumber - 1] = 0; - init[BTGreaterEqualStrategyNumber - 1] = 0; - init[BTGreaterStrategyNumber - 1] = 0; - } - - /* only one of <, <= */ - if (init[BTLessStrategyNumber - 1] - && init[BTLessEqualStrategyNumber - 1]) - { - ScanKeyData *lt, *le; - - lt = &xform[BTLessStrategyNumber - 1]; - le = &xform[BTLessEqualStrategyNumber - 1]; + /* - * DO NOT use the cached function stuff here -- this is key - * ordering, happens only when the user expresses a hokey - * qualification, and gets executed only once, anyway. The - * transform maps are hard-coded, and can't be initialized - * in the correct way. + * We don't use indices for 'A is null' and 'A is not null' + * currently and 'A < = > <> NULL' is non-sense' - so qual is not + * Ok. - vadim 03/21/97 */ - test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument); - if (test) - init[BTLessEqualStrategyNumber - 1] = 0; - else - init[BTLessStrategyNumber - 1] = 0; - } - - /* only one of >, >= */ - if (init[BTGreaterStrategyNumber - 1] - && init[BTGreaterEqualStrategyNumber - 1]) - { - ScanKeyData *gt, *ge; - - gt = &xform[BTGreaterStrategyNumber - 1]; - ge = &xform[BTGreaterEqualStrategyNumber - 1]; - - /* see note above on function cache */ - test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument); - if (test) - init[BTGreaterEqualStrategyNumber - 1] = 0; - else - init[BTGreaterStrategyNumber - 1] = 0; - } - - /* okay, reorder and count */ - for (j = BTMaxStrategyNumber; --j >= 0; ) - if (init[j]) - key[new_numberOfKeys++] = xform[j]; - - if ( attno == 1 ) - so->numberOfFirstKeys = new_numberOfKeys; - - if ( i == numberOfKeys ) - break; - - /* initialization for new attno */ - attno = cur->sk_attno; - memset(xform, 0, nbytes); - map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), - BTMaxStrategyNumber, - attno); - /* haven't looked at any strategies yet */ - for (j = 0; j <= BTMaxStrategyNumber; j++) - init[j] = 0; + if (cur->sk_flags & SK_ISNULL) + so->qual_ok = 0; + so->numberOfFirstKeys = 1; + return; } - for (j = BTMaxStrategyNumber; --j >= 0; ) - { - if (cur->sk_procedure == map->entry[j].sk_procedure) - break; - } - - /* have we seen one of these before? */ - if (init[j]) - { - /* yup, use the appropriate value */ - test = - (long) FMGR_PTR2(cur->sk_func, cur->sk_procedure, - cur->sk_argument, xform[j].sk_argument); - if (test) - xform[j].sk_argument = cur->sk_argument; - else if ( j == (BTEqualStrategyNumber - 1) ) - so->qual_ok = 0; /* key == a && key == b, but a != b */ - } else + /* get space for the modified array of keys */ + nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData); + xform = (ScanKey) palloc(nbytes); + + memset(xform, 0, nbytes); + map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), + BTMaxStrategyNumber, + attno); + for (j = 0; j <= BTMaxStrategyNumber; j++) + init[j] = 0; + + /* check each key passed in */ + for (i = 0;;) { - /* nope, use this value */ - memmove(&xform[j], cur, sizeof(*cur)); - init[j] = 1; + if (i < numberOfKeys) + cur = &key[i]; + + if (cur->sk_flags & SK_ISNULL) /* see comments above */ + so->qual_ok = 0; + + if (i == numberOfKeys || cur->sk_attno != attno) + { + if (cur->sk_attno != attno + 1 && i < numberOfKeys) + { + elog(WARN, "_bt_orderkeys: key(s) for attribute %d missed", attno + 1); + } + + /* + * If = has been specified, no other key will be used. In case + * of key < 2 && key == 1 and so on we have to set qual_ok to + * 0 + */ + if (init[BTEqualStrategyNumber - 1]) + { + ScanKeyData *eq, + *chk; + + eq = &xform[BTEqualStrategyNumber - 1]; + for (j = BTMaxStrategyNumber; --j >= 0;) + { + if (j == (BTEqualStrategyNumber - 1) || init[j] == 0) + continue; + chk = &xform[j]; + test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument); + if (!test) + so->qual_ok = 0; + } + init[BTLessStrategyNumber - 1] = 0; + init[BTLessEqualStrategyNumber - 1] = 0; + init[BTGreaterEqualStrategyNumber - 1] = 0; + init[BTGreaterStrategyNumber - 1] = 0; + } + + /* only one of <, <= */ + if (init[BTLessStrategyNumber - 1] + && init[BTLessEqualStrategyNumber - 1]) + { + ScanKeyData *lt, + *le; + + lt = &xform[BTLessStrategyNumber - 1]; + le = &xform[BTLessEqualStrategyNumber - 1]; + + /* + * DO NOT use the cached function stuff here -- this is + * key ordering, happens only when the user expresses a + * hokey qualification, and gets executed only once, + * anyway. The transform maps are hard-coded, and can't + * be initialized in the correct way. + */ + test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument); + if (test) + init[BTLessEqualStrategyNumber - 1] = 0; + else + init[BTLessStrategyNumber - 1] = 0; + } + + /* only one of >, >= */ + if (init[BTGreaterStrategyNumber - 1] + && init[BTGreaterEqualStrategyNumber - 1]) + { + ScanKeyData *gt, + *ge; + + gt = &xform[BTGreaterStrategyNumber - 1]; + ge = &xform[BTGreaterEqualStrategyNumber - 1]; + + /* see note above on function cache */ + test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument); + if (test) + init[BTGreaterEqualStrategyNumber - 1] = 0; + else + init[BTGreaterStrategyNumber - 1] = 0; + } + + /* okay, reorder and count */ + for (j = BTMaxStrategyNumber; --j >= 0;) + if (init[j]) + key[new_numberOfKeys++] = xform[j]; + + if (attno == 1) + so->numberOfFirstKeys = new_numberOfKeys; + + if (i == numberOfKeys) + break; + + /* initialization for new attno */ + attno = cur->sk_attno; + memset(xform, 0, nbytes); + map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), + BTMaxStrategyNumber, + attno); + /* haven't looked at any strategies yet */ + for (j = 0; j <= BTMaxStrategyNumber; j++) + init[j] = 0; + } + + for (j = BTMaxStrategyNumber; --j >= 0;) + { + if (cur->sk_procedure == map->entry[j].sk_procedure) + break; + } + + /* have we seen one of these before? */ + if (init[j]) + { + /* yup, use the appropriate value */ + test = + (long) FMGR_PTR2(cur->sk_func, cur->sk_procedure, + cur->sk_argument, xform[j].sk_argument); + if (test) + xform[j].sk_argument = cur->sk_argument; + else if (j == (BTEqualStrategyNumber - 1)) + so->qual_ok = 0;/* key == a && key == b, but a != b */ + } + else + { + /* nope, use this value */ + memmove(&xform[j], cur, sizeof(*cur)); + init[j] = 1; + } + + i++; } - - i++; - } - - so->numberOfKeys = new_numberOfKeys; - - pfree(xform); + + so->numberOfKeys = new_numberOfKeys; + + pfree(xform); } BTItem _bt_formitem(IndexTuple itup) { - int nbytes_btitem; - BTItem btitem; - Size tuplen; - extern Oid newoid(); - - /* see comments in btbuild - - if (itup->t_info & INDEX_NULL_MASK) - elog(WARN, "btree indices cannot include null keys"); - */ - - /* make a copy of the index tuple with room for the sequence number */ - tuplen = IndexTupleSize(itup); - nbytes_btitem = tuplen + - (sizeof(BTItemData) - sizeof(IndexTupleData)); - - btitem = (BTItem) palloc(nbytes_btitem); - memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen); - + int nbytes_btitem; + BTItem btitem; + Size tuplen; + extern Oid newoid(); + + /* + * see comments in btbuild + * + * if (itup->t_info & INDEX_NULL_MASK) elog(WARN, "btree indices cannot + * include null keys"); + */ + + /* make a copy of the index tuple with room for the sequence number */ + tuplen = IndexTupleSize(itup); + nbytes_btitem = tuplen + + (sizeof(BTItemData) - sizeof(IndexTupleData)); + + btitem = (BTItem) palloc(nbytes_btitem); + memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen); + #ifndef BTREE_VERSION_1 - btitem->bti_oid = newoid(); + btitem->bti_oid = newoid(); #endif - return (btitem); + return (btitem); } #ifdef NOT_USED bool _bt_checkqual(IndexScanDesc scan, IndexTuple itup) { - BTScanOpaque so; - - so = (BTScanOpaque) scan->opaque; - if (so->numberOfKeys > 0) - return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation), - so->numberOfKeys, so->keyData)); - else - return (true); + BTScanOpaque so; + + so = (BTScanOpaque) scan->opaque; + if (so->numberOfKeys > 0) + return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation), + so->numberOfKeys, so->keyData)); + else + return (true); } + #endif #ifdef NOT_USED bool _bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz) { - BTScanOpaque so; - - so = (BTScanOpaque) scan->opaque; - if ( keysz > 0 && so->numberOfKeys >= keysz ) - return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation), - keysz, so->keyData)); - else - return (true); + BTScanOpaque so; + + so = (BTScanOpaque) scan->opaque; + if (keysz > 0 && so->numberOfKeys >= keysz) + return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation), + keysz, so->keyData)); + else + return (true); } + #endif bool -_bt_checkkeys (IndexScanDesc scan, IndexTuple tuple, Size *keysok) +_bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, Size * keysok) { - BTScanOpaque so = (BTScanOpaque) scan->opaque; - Size keysz = so->numberOfKeys; - TupleDesc tupdesc; - ScanKey key; - Datum datum; - bool isNull; - int test; - - *keysok = 0; - if ( keysz == 0 ) - return (true); - - key = so->keyData; - tupdesc = RelationGetTupleDescriptor(scan->relation); - - IncrIndexProcessed(); - - while (keysz > 0) - { - datum = index_getattr(tuple, - key[0].sk_attno, - tupdesc, - &isNull); - - /* btree doesn't support 'A is null' clauses, yet */ - if ( isNull || key[0].sk_flags & SK_ISNULL ) + BTScanOpaque so = (BTScanOpaque) scan->opaque; + Size keysz = so->numberOfKeys; + TupleDesc tupdesc; + ScanKey key; + Datum datum; + bool isNull; + int test; + + *keysok = 0; + if (keysz == 0) + return (true); + + key = so->keyData; + tupdesc = RelationGetTupleDescriptor(scan->relation); + + IncrIndexProcessed(); + + while (keysz > 0) { - return (false); - } + datum = index_getattr(tuple, + key[0].sk_attno, + tupdesc, + &isNull); - if (key[0].sk_flags & SK_COMMUTE) { - test = (int) (*(key[0].sk_func)) - (DatumGetPointer(key[0].sk_argument), - datum); - } else { - test = (int) (*(key[0].sk_func)) - (datum, - DatumGetPointer(key[0].sk_argument)); - } - - if (!test == !(key[0].sk_flags & SK_NEGATE)) { - return (false); + /* btree doesn't support 'A is null' clauses, yet */ + if (isNull || key[0].sk_flags & SK_ISNULL) + { + return (false); + } + + if (key[0].sk_flags & SK_COMMUTE) + { + test = (int) (*(key[0].sk_func)) + (DatumGetPointer(key[0].sk_argument), + datum); + } + else + { + test = (int) (*(key[0].sk_func)) + (datum, + DatumGetPointer(key[0].sk_argument)); + } + + if (!test == !(key[0].sk_flags & SK_NEGATE)) + { + return (false); + } + + keysz -= 1; + key++; + (*keysok)++; } - - keysz -= 1; - key++; - (*keysok)++; - } - - return (true); + + return (true); } diff --git a/src/backend/access/rtree/rtget.c b/src/backend/access/rtree/rtget.c index 09f10f1aa98..eaf16c1ae9d 100644 --- a/src/backend/access/rtree/rtget.c +++ b/src/backend/access/rtree/rtget.c @@ -1,19 +1,19 @@ /*------------------------------------------------------------------------- * * rtget.c-- - * fetch tuples from an rtree scan. + * fetch tuples from an rtree scan. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtget.c,v 1.7 1996/11/21 06:13:43 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtget.c,v 1.8 1997/09/07 04:39:11 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <storage/bufmgr.h> #include <access/sdir.h> #include <access/relscan.h> @@ -21,14 +21,15 @@ #include <access/rtree.h> #include <storage/bufpage.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static OffsetNumber findnext(IndexScanDesc s, Page p, OffsetNumber n, - ScanDirection dir); +static OffsetNumber +findnext(IndexScanDesc s, Page p, OffsetNumber n, + ScanDirection dir); static RetrieveIndexResult rtscancache(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult rtfirst(IndexScanDesc s, ScanDirection dir); static RetrieveIndexResult rtnext(IndexScanDesc s, ScanDirection dir); @@ -38,278 +39,315 @@ static ItemPointer rtheapptr(Relation r, ItemPointer itemp); RetrieveIndexResult rtgettuple(IndexScanDesc s, ScanDirection dir) { - RetrieveIndexResult res; - - /* if we have it cached in the scan desc, just return the value */ - if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL) + RetrieveIndexResult res; + + /* if we have it cached in the scan desc, just return the value */ + if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL) + return (res); + + /* not cached, so we'll have to do some work */ + if (ItemPointerIsValid(&(s->currentItemData))) + { + res = rtnext(s, dir); + } + else + { + res = rtfirst(s, dir); + } return (res); - - /* not cached, so we'll have to do some work */ - if (ItemPointerIsValid(&(s->currentItemData))) { - res = rtnext(s, dir); - } else { - res = rtfirst(s, dir); - } - return (res); } -static RetrieveIndexResult +static RetrieveIndexResult rtfirst(IndexScanDesc s, ScanDirection dir) { - Buffer b; - Page p; - OffsetNumber n; - OffsetNumber maxoff; - RetrieveIndexResult res; - RTreePageOpaque po; - RTreeScanOpaque so; - RTSTACK *stk; - BlockNumber blk; - IndexTuple it; - - b = ReadBuffer(s->relation, P_ROOT); - p = BufferGetPage(b); - po = (RTreePageOpaque) PageGetSpecialPointer(p); - so = (RTreeScanOpaque) s->opaque; - - for (;;) { - maxoff = PageGetMaxOffsetNumber(p); - if (ScanDirectionIsBackward(dir)) - n = findnext(s, p, maxoff, dir); - else - n = findnext(s, p, FirstOffsetNumber, dir); - - while (n < FirstOffsetNumber || n > maxoff) { - - ReleaseBuffer(b); - if (so->s_stack == (RTSTACK *) NULL) - return ((RetrieveIndexResult) NULL); - - stk = so->s_stack; - b = ReadBuffer(s->relation, stk->rts_blk); - p = BufferGetPage(b); - po = (RTreePageOpaque) PageGetSpecialPointer(p); - maxoff = PageGetMaxOffsetNumber(p); - - if (ScanDirectionIsBackward(dir)) { - n = OffsetNumberPrev(stk->rts_child); - } else { - n = OffsetNumberNext(stk->rts_child); - } - so->s_stack = stk->rts_parent; - pfree(stk); - - n = findnext(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 = (RTSTACK *) palloc(sizeof(RTSTACK)); - stk->rts_child = n; - stk->rts_blk = BufferGetBlockNumber(b); - stk->rts_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 = (RTreePageOpaque) PageGetSpecialPointer(p); + Buffer b; + Page p; + OffsetNumber n; + OffsetNumber maxoff; + RetrieveIndexResult res; + RTreePageOpaque po; + RTreeScanOpaque so; + RTSTACK *stk; + BlockNumber blk; + IndexTuple it; + + b = ReadBuffer(s->relation, P_ROOT); + p = BufferGetPage(b); + po = (RTreePageOpaque) PageGetSpecialPointer(p); + so = (RTreeScanOpaque) s->opaque; + + for (;;) + { + maxoff = PageGetMaxOffsetNumber(p); + if (ScanDirectionIsBackward(dir)) + n = findnext(s, p, maxoff, dir); + else + n = findnext(s, p, FirstOffsetNumber, dir); + + while (n < FirstOffsetNumber || n > maxoff) + { + + ReleaseBuffer(b); + if (so->s_stack == (RTSTACK *) NULL) + return ((RetrieveIndexResult) NULL); + + stk = so->s_stack; + b = ReadBuffer(s->relation, stk->rts_blk); + p = BufferGetPage(b); + po = (RTreePageOpaque) PageGetSpecialPointer(p); + maxoff = PageGetMaxOffsetNumber(p); + + if (ScanDirectionIsBackward(dir)) + { + n = OffsetNumberPrev(stk->rts_child); + } + else + { + n = OffsetNumberNext(stk->rts_child); + } + so->s_stack = stk->rts_parent; + pfree(stk); + + n = findnext(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 = (RTSTACK *) palloc(sizeof(RTSTACK)); + stk->rts_child = n; + stk->rts_blk = BufferGetBlockNumber(b); + stk->rts_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 = (RTreePageOpaque) PageGetSpecialPointer(p); + } } - } } -static RetrieveIndexResult +static RetrieveIndexResult rtnext(IndexScanDesc s, ScanDirection dir) { - Buffer b; - Page p; - OffsetNumber n; - OffsetNumber maxoff; - RetrieveIndexResult res; - RTreePageOpaque po; - RTreeScanOpaque so; - RTSTACK *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 = (RTreePageOpaque) PageGetSpecialPointer(p); - so = (RTreeScanOpaque) s->opaque; - - for (;;) { - maxoff = PageGetMaxOffsetNumber(p); - n = findnext(s, p, n, dir); - - while (n < FirstOffsetNumber || n > maxoff) { - - ReleaseBuffer(b); - if (so->s_stack == (RTSTACK *) NULL) - return ((RetrieveIndexResult) NULL); - - stk = so->s_stack; - b = ReadBuffer(s->relation, stk->rts_blk); - p = BufferGetPage(b); - maxoff = PageGetMaxOffsetNumber(p); - po = (RTreePageOpaque) PageGetSpecialPointer(p); - - if (ScanDirectionIsBackward(dir)) { - n = OffsetNumberPrev(stk->rts_child); - } else { - n = OffsetNumberNext(stk->rts_child); - } - so->s_stack = stk->rts_parent; - pfree(stk); - - n = findnext(s, p, n, dir); + Buffer b; + Page p; + OffsetNumber n; + OffsetNumber maxoff; + RetrieveIndexResult res; + RTreePageOpaque po; + RTreeScanOpaque so; + RTSTACK *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 = (RTSTACK *) palloc(sizeof(RTSTACK)); - stk->rts_child = n; - stk->rts_blk = BufferGetBlockNumber(b); - stk->rts_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 = (RTreePageOpaque) PageGetSpecialPointer(p); - - if (ScanDirectionIsBackward(dir)) { - n = PageGetMaxOffsetNumber(p); - } else { - n = FirstOffsetNumber; - } + + b = ReadBuffer(s->relation, blk); + p = BufferGetPage(b); + po = (RTreePageOpaque) PageGetSpecialPointer(p); + so = (RTreeScanOpaque) s->opaque; + + for (;;) + { + maxoff = PageGetMaxOffsetNumber(p); + n = findnext(s, p, n, dir); + + while (n < FirstOffsetNumber || n > maxoff) + { + + ReleaseBuffer(b); + if (so->s_stack == (RTSTACK *) NULL) + return ((RetrieveIndexResult) NULL); + + stk = so->s_stack; + b = ReadBuffer(s->relation, stk->rts_blk); + p = BufferGetPage(b); + maxoff = PageGetMaxOffsetNumber(p); + po = (RTreePageOpaque) PageGetSpecialPointer(p); + + if (ScanDirectionIsBackward(dir)) + { + n = OffsetNumberPrev(stk->rts_child); + } + else + { + n = OffsetNumberNext(stk->rts_child); + } + so->s_stack = stk->rts_parent; + pfree(stk); + + n = findnext(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 = (RTSTACK *) palloc(sizeof(RTSTACK)); + stk->rts_child = n; + stk->rts_blk = BufferGetBlockNumber(b); + stk->rts_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 = (RTreePageOpaque) PageGetSpecialPointer(p); + + if (ScanDirectionIsBackward(dir)) + { + n = PageGetMaxOffsetNumber(p); + } + else + { + n = FirstOffsetNumber; + } + } } - } } -static OffsetNumber +static OffsetNumber findnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir) { - OffsetNumber maxoff; - IndexTuple it; - RTreePageOpaque po; - RTreeScanOpaque so; - - maxoff = PageGetMaxOffsetNumber(p); - po = (RTreePageOpaque) PageGetSpecialPointer(p); - so = (RTreeScanOpaque) s->opaque; - - /* - * 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 & RTS_CURBEFORE) { - so->s_flags &= ~RTS_CURBEFORE; - n = OffsetNumberPrev(n); - } - - while (n >= FirstOffsetNumber && n <= maxoff) { - it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); - if (po->flags & F_LEAF) { - if (index_keytest(it, - RelationGetTupleDescriptor(s->relation), - s->numberOfKeys, s->keyData)) - break; - } else { - if (index_keytest(it, - RelationGetTupleDescriptor(s->relation), - so->s_internalNKey, so->s_internalKey)) - break; + OffsetNumber maxoff; + IndexTuple it; + RTreePageOpaque po; + RTreeScanOpaque so; + + maxoff = PageGetMaxOffsetNumber(p); + po = (RTreePageOpaque) PageGetSpecialPointer(p); + so = (RTreeScanOpaque) s->opaque; + + /* + * 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 & RTS_CURBEFORE) + { + so->s_flags &= ~RTS_CURBEFORE; + n = OffsetNumberPrev(n); } - - if (ScanDirectionIsBackward(dir)) { - n = OffsetNumberPrev(n); - } else { - n = OffsetNumberNext(n); + + while (n >= FirstOffsetNumber && n <= maxoff) + { + it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); + if (po->flags & F_LEAF) + { + if (index_keytest(it, + RelationGetTupleDescriptor(s->relation), + s->numberOfKeys, s->keyData)) + break; + } + else + { + if (index_keytest(it, + RelationGetTupleDescriptor(s->relation), + so->s_internalNKey, so->s_internalKey)) + break; + } + + if (ScanDirectionIsBackward(dir)) + { + n = OffsetNumberPrev(n); + } + else + { + n = OffsetNumberNext(n); + } } - } - - return (n); + + return (n); } -static RetrieveIndexResult +static RetrieveIndexResult rtscancache(IndexScanDesc s, ScanDirection dir) { - RetrieveIndexResult res; - ItemPointer ip; - - if (!(ScanDirectionIsNoMovement(dir) - && ItemPointerIsValid(&(s->currentItemData)))) { - - return ((RetrieveIndexResult) NULL); - } - - ip = rtheapptr(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 = rtheapptr(s->relation, &(s->currentItemData)); + + if (ItemPointerIsValid(ip)) + res = FormRetrieveIndexResult(&(s->currentItemData), ip); + else + res = (RetrieveIndexResult) NULL; + + pfree(ip); + + return (res); } /* - * rtheapptr returns the item pointer to the tuple in the heap relation - * for which itemp is the index relation item pointer. + * rtheapptr returns the item pointer to the tuple in the heap relation + * for which itemp is the index relation item pointer. */ -static ItemPointer +static ItemPointer rtheapptr(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/rtree/rtproc.c b/src/backend/access/rtree/rtproc.c index ac7a3abfecf..4b7a9f2a266 100644 --- a/src/backend/access/rtree/rtproc.c +++ b/src/backend/access/rtree/rtproc.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * rtproc.c-- - * pg_amproc entries for rtrees. + * pg_amproc entries for rtrees. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.7 1997/04/22 17:31:23 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.8 1997/09/07 04:39:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -17,136 +17,139 @@ #include <utils/builtins.h> #include <utils/geo_decls.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif BOX -*rt_box_union(BOX *a, BOX *b) +* rt_box_union(BOX * a, BOX * b) { - BOX *n; - - if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL) - elog(WARN, "Cannot allocate box for union"); - - n->high.x = Max(a->high.x, b->high.x); - n->high.y = Max(a->high.y, b->high.y); - n->low.x = Min(a->low.x, b->low.x); - n->low.y = Min(a->low.y, b->low.y); - - return (n); + BOX *n; + + if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL) + elog(WARN, "Cannot allocate box for union"); + + n->high.x = Max(a->high.x, b->high.x); + n->high.y = Max(a->high.y, b->high.y); + n->low.x = Min(a->low.x, b->low.x); + n->low.y = Min(a->low.y, b->low.y); + + return (n); } -BOX * -rt_box_inter(BOX *a, BOX *b) +BOX * +rt_box_inter(BOX * a, BOX * b) { - BOX *n; - - if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL) - elog(WARN, "Cannot allocate box for union"); - - n->high.x = Min(a->high.x, b->high.x); - n->high.y = Min(a->high.y, b->high.y); - n->low.x = Max(a->low.x, b->low.x); - n->low.y = Max(a->low.y, b->low.y); - - if (n->high.x < n->low.x || n->high.y < n->low.y) { - pfree(n); - return ((BOX *) NULL); - } - - return (n); + BOX *n; + + if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL) + elog(WARN, "Cannot allocate box for union"); + + n->high.x = Min(a->high.x, b->high.x); + n->high.y = Min(a->high.y, b->high.y); + n->low.x = Max(a->low.x, b->low.x); + n->low.y = Max(a->low.y, b->low.y); + + if (n->high.x < n->low.x || n->high.y < n->low.y) + { + pfree(n); + return ((BOX *) NULL); + } + + return (n); } void -rt_box_size(BOX *a, float *size) +rt_box_size(BOX * a, float *size) { - if (a == (BOX *) NULL || a->high.x <= a->low.x || a->high.y <= a->low.y) - *size = 0.0; - else - *size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y)); - - return; + if (a == (BOX *) NULL || a->high.x <= a->low.x || a->high.y <= a->low.y) + *size = 0.0; + else + *size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y)); + + return; } /* - * rt_bigbox_size() -- Compute a size for big boxes. + * rt_bigbox_size() -- Compute a size for big boxes. * - * In an earlier release of the system, this routine did something - * different from rt_box_size. We now use floats, rather than ints, - * as the return type for the size routine, so we no longer need to - * have a special return type for big boxes. + * In an earlier release of the system, this routine did something + * different from rt_box_size. We now use floats, rather than ints, + * as the return type for the size routine, so we no longer need to + * have a special return type for big boxes. */ void -rt_bigbox_size(BOX *a, float *size) +rt_bigbox_size(BOX * a, float *size) { - rt_box_size(a, size); + rt_box_size(a, size); } -POLYGON * -rt_poly_union(POLYGON *a, POLYGON *b) +POLYGON * +rt_poly_union(POLYGON * a, POLYGON * b) { - POLYGON *p; - - p = (POLYGON *)PALLOCTYPE(POLYGON); - - if (!PointerIsValid(p)) - elog(WARN, "Cannot allocate polygon for union"); - - memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */ - p->size = sizeof(POLYGON); - p->npts = 0; - p->boundbox.high.x = Max(a->boundbox.high.x, b->boundbox.high.x); - p->boundbox.high.y = Max(a->boundbox.high.y, b->boundbox.high.y); - p->boundbox.low.x = Min(a->boundbox.low.x, b->boundbox.low.x); - p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y); - return p; + POLYGON *p; + + p = (POLYGON *) PALLOCTYPE(POLYGON); + + if (!PointerIsValid(p)) + elog(WARN, "Cannot allocate polygon for union"); + + memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */ + p->size = sizeof(POLYGON); + p->npts = 0; + p->boundbox.high.x = Max(a->boundbox.high.x, b->boundbox.high.x); + p->boundbox.high.y = Max(a->boundbox.high.y, b->boundbox.high.y); + p->boundbox.low.x = Min(a->boundbox.low.x, b->boundbox.low.x); + p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y); + return p; } void -rt_poly_size(POLYGON *a, float *size) +rt_poly_size(POLYGON * a, float *size) { - double xdim, ydim; - - size = (float *) palloc(sizeof(float)); - if (a == (POLYGON *) NULL || - a->boundbox.high.x <= a->boundbox.low.x || - a->boundbox.high.y <= a->boundbox.low.y) - *size = 0.0; - else { - xdim = (a->boundbox.high.x - a->boundbox.low.x); - ydim = (a->boundbox.high.y - a->boundbox.low.y); - - *size = (float) (xdim * ydim); - } - - return; + double xdim, + ydim; + + size = (float *) palloc(sizeof(float)); + if (a == (POLYGON *) NULL || + a->boundbox.high.x <= a->boundbox.low.x || + a->boundbox.high.y <= a->boundbox.low.y) + *size = 0.0; + else + { + xdim = (a->boundbox.high.x - a->boundbox.low.x); + ydim = (a->boundbox.high.y - a->boundbox.low.y); + + *size = (float) (xdim * ydim); + } + + return; } -POLYGON * -rt_poly_inter(POLYGON *a, POLYGON *b) +POLYGON * +rt_poly_inter(POLYGON * a, POLYGON * b) { - POLYGON *p; - - p = (POLYGON *) PALLOCTYPE(POLYGON); - - if (!PointerIsValid(p)) - elog(WARN, "Cannot allocate polygon for intersection"); - - memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */ - p->size = sizeof(POLYGON); - p->npts = 0; - p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x); - p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y); - p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x); - p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y); - - if (p->boundbox.high.x < p->boundbox.low.x || p->boundbox.high.y < p->boundbox.low.y) + POLYGON *p; + + p = (POLYGON *) PALLOCTYPE(POLYGON); + + if (!PointerIsValid(p)) + elog(WARN, "Cannot allocate polygon for intersection"); + + memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */ + p->size = sizeof(POLYGON); + p->npts = 0; + p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x); + p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y); + p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x); + p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y); + + if (p->boundbox.high.x < p->boundbox.low.x || p->boundbox.high.y < p->boundbox.low.y) { - pfree(p); - return ((POLYGON *) NULL); + pfree(p); + return ((POLYGON *) NULL); } - - return (p); + + return (p); } diff --git a/src/backend/access/rtree/rtree.c b/src/backend/access/rtree/rtree.c index 4cd0580c973..ae92ea20136 100644 --- a/src/backend/access/rtree/rtree.c +++ b/src/backend/access/rtree/rtree.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * rtree.c-- - * interface routines for the postgres rtree indexed access method. + * interface routines for the postgres rtree indexed access method. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.13 1997/08/12 22:51:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.14 1997/09/07 04:39:22 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -27,886 +27,983 @@ #include <storage/bufpage.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -typedef struct SPLITVEC { - OffsetNumber *spl_left; - int spl_nleft; - char *spl_ldatum; - OffsetNumber *spl_right; - int spl_nright; - char *spl_rdatum; -} SPLITVEC; - -typedef struct RTSTATE { - func_ptr unionFn; /* union function */ - func_ptr sizeFn; /* size function */ - func_ptr interFn; /* intersection function */ -} RTSTATE; +typedef struct SPLITVEC +{ + OffsetNumber *spl_left; + int spl_nleft; + char *spl_ldatum; + OffsetNumber *spl_right; + int spl_nright; + char *spl_rdatum; +} SPLITVEC; + +typedef struct RTSTATE +{ + func_ptr unionFn; /* union function */ + func_ptr sizeFn; /* size function */ + func_ptr interFn; /* intersection function */ +} RTSTATE; /* non-export function prototypes */ -static InsertIndexResult rtdoinsert(Relation r, IndexTuple itup, - RTSTATE *rtstate); -static void rttighten(Relation r, RTSTACK *stk, char *datum, int att_size, - RTSTATE *rtstate); -static InsertIndexResult dosplit(Relation r, Buffer buffer, RTSTACK *stack, - IndexTuple itup, RTSTATE *rtstate); -static void rtintinsert(Relation r, RTSTACK *stk, IndexTuple ltup, - IndexTuple rtup, RTSTATE *rtstate); -static void rtnewroot(Relation r, IndexTuple lt, IndexTuple rt); -static void picksplit(Relation r, Page page, SPLITVEC *v, IndexTuple itup, - RTSTATE *rtstate); -static void RTInitBuffer(Buffer b, uint32 f); -static OffsetNumber choose(Relation r, Page p, IndexTuple it, - RTSTATE *rtstate); -static int nospace(Page p, IndexTuple it); -static void initRtstate(RTSTATE *rtstate, Relation index); +static InsertIndexResult +rtdoinsert(Relation r, IndexTuple itup, + RTSTATE * rtstate); +static void +rttighten(Relation r, RTSTACK * stk, char *datum, int att_size, + RTSTATE * rtstate); +static InsertIndexResult +dosplit(Relation r, Buffer buffer, RTSTACK * stack, + IndexTuple itup, RTSTATE * rtstate); +static void +rtintinsert(Relation r, RTSTACK * stk, IndexTuple ltup, + IndexTuple rtup, RTSTATE * rtstate); +static void rtnewroot(Relation r, IndexTuple lt, IndexTuple rt); +static void +picksplit(Relation r, Page page, SPLITVEC * v, IndexTuple itup, + RTSTATE * rtstate); +static void RTInitBuffer(Buffer b, uint32 f); +static OffsetNumber +choose(Relation r, Page p, IndexTuple it, + RTSTATE * rtstate); +static int nospace(Page p, IndexTuple it); +static void initRtstate(RTSTATE * rtstate, Relation index); void rtbuild(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 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; - RTSTATE rtState; - - initRtstate(&rtState, index); - - /* rtrees only know how to do stupid locking now */ - RelationSetLockForWrite(index); - - 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, "%s already contains data", index->rd_rel->relname.data); - - /* initialize the root page (if this is a new index) */ - if (oldPred == NULL) { - buffer = ReadBuffer(index, P_NEW); - RTInitBuffer(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; + RTSTATE rtState; + + initRtstate(&rtState, index); + + /* rtrees only know how to do stupid locking now */ + RelationSetLockForWrite(index); + + 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, "%s already contains data", index->rd_rel->relname.data); + + /* initialize the root page (if this is a new index) */ + if (oldPred == NULL) + { + buffer = ReadBuffer(index, P_NEW); + RTInitBuffer(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); - } + if (pred != NULL || oldPred != NULL) + { + tupleTable = ExecCreateTupleTable(1); + slot = ExecAllocTableSlot(tupleTable); + econtext = makeNode(ExprContext); + FillDummyExprContext(econtext, slot, hd, buffer); + } else { econtext = NULL; tupleTable = NULL; slot = NULL; } -#endif /* OMIT_PARTIAL_INDEX */ - scan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL); - htup = heap_getnext(scan, 0, &buffer); - - /* count 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); + + /* count 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' : ' '); + } + + /* 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 + * rtdoinsert directly. Normal access method calls dispatch + * through rtinsert, 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 = rtdoinsert(index, itup, &rtState); + 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(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 counted 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 + * CommandCounterIncrement(), 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' : ' '); + + 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 rtdoinsert directly. Normal access method calls - * dispatch through rtinsert, 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 = rtdoinsert(index, itup, &rtState); - pfree(itup); - pfree(res); - } - - /* 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 counted 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 - * CommandCounterIncrement(), 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); } /* - * rtinsert -- wrapper for rtree tuple insertion. + * rtinsert -- wrapper for rtree tuple insertion. * - * This is the public interface routine for tuple insertion in rtrees. - * It doesn't do any work; just locks the relation and passes the buck. + * This is the public interface routine for tuple insertion in rtrees. + * It doesn't do any work; just locks the relation and passes the buck. */ InsertIndexResult -rtinsert(Relation r, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) +rtinsert(Relation r, Datum * datum, char *nulls, ItemPointer ht_ctid, Relation heapRel) { - InsertIndexResult res; - IndexTuple itup; - RTSTATE rtState; - - /* generate an index tuple */ - itup = index_formtuple(RelationGetTupleDescriptor(r), datum, nulls); - itup->t_tid = *ht_ctid; - initRtstate(&rtState, r); - - RelationSetLockForWrite(r); - res = rtdoinsert(r, itup, &rtState); - - /* XXX two-phase locking -- don't unlock the relation until EOT */ - return (res); + InsertIndexResult res; + IndexTuple itup; + RTSTATE rtState; + + /* generate an index tuple */ + itup = index_formtuple(RelationGetTupleDescriptor(r), datum, nulls); + itup->t_tid = *ht_ctid; + initRtstate(&rtState, r); + + RelationSetLockForWrite(r); + res = rtdoinsert(r, itup, &rtState); + + /* XXX two-phase locking -- don't unlock the relation until EOT */ + return (res); } -static InsertIndexResult -rtdoinsert(Relation r, IndexTuple itup, RTSTATE *rtstate) +static InsertIndexResult +rtdoinsert(Relation r, IndexTuple itup, RTSTATE * rtstate) { - Page page; - Buffer buffer; - BlockNumber blk; - IndexTuple which; - OffsetNumber l; - RTSTACK *stack; - InsertIndexResult res; - RTreePageOpaque opaque; - char *datum; - - blk = P_ROOT; - buffer = InvalidBuffer; - stack = (RTSTACK *) 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 = (RTreePageOpaque) PageGetSpecialPointer(page); - if (!(opaque->flags & F_LEAF)) { - RTSTACK *n; - ItemId iid; - - n = (RTSTACK *) palloc(sizeof(RTSTACK)); - n->rts_parent = stack; - n->rts_blk = blk; - n->rts_child = choose(r, page, itup, rtstate); - stack = n; - - iid = PageGetItemId(page, n->rts_child); - which = (IndexTuple) PageGetItem(page, iid); - blk = ItemPointerGetBlockNumber(&(which->t_tid)); + Page page; + Buffer buffer; + BlockNumber blk; + IndexTuple which; + OffsetNumber l; + RTSTACK *stack; + InsertIndexResult res; + RTreePageOpaque opaque; + char *datum; + + blk = P_ROOT; + buffer = InvalidBuffer; + stack = (RTSTACK *) 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 = (RTreePageOpaque) PageGetSpecialPointer(page); + if (!(opaque->flags & F_LEAF)) + { + RTSTACK *n; + ItemId iid; + + n = (RTSTACK *) palloc(sizeof(RTSTACK)); + n->rts_parent = stack; + n->rts_blk = blk; + n->rts_child = choose(r, page, itup, rtstate); + stack = n; + + iid = PageGetItemId(page, n->rts_child); + which = (IndexTuple) PageGetItem(page, iid); + blk = ItemPointerGetBlockNumber(&(which->t_tid)); + } + } while (!(opaque->flags & F_LEAF)); + + if (nospace(page, itup)) + { + /* need to do a split */ + res = dosplit(r, buffer, stack, itup, rtstate); + freestack(stack); + WriteBuffer(buffer); /* don't forget to release buffer! */ + return (res); + } + + /* add the item and write the buffer */ + if (PageIsEmpty(page)) + { + l = PageAddItem(page, (Item) itup, IndexTupleSize(itup), + FirstOffsetNumber, + LP_USED); } - } while (!(opaque->flags & F_LEAF)); - - if (nospace(page, itup)) { - /* need to do a split */ - res = dosplit(r, buffer, stack, itup, rtstate); + else + { + l = PageAddItem(page, (Item) itup, IndexTupleSize(itup), + OffsetNumberNext(PageGetMaxOffsetNumber(page)), + LP_USED); + } + + WriteBuffer(buffer); + + datum = (((char *) itup) + sizeof(IndexTupleData)); + + /* now expand the page boundary in the parent to include the new child */ + rttighten(r, stack, datum, + (IndexTupleSize(itup) - sizeof(IndexTupleData)), rtstate); freestack(stack); - WriteBuffer(buffer); /* don't forget to release buffer! */ + + /* build and return an InsertIndexResult for this insertion */ + res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); + ItemPointerSet(&(res->pointerData), blk, l); + return (res); - } - - /* add the item and write the buffer */ - if (PageIsEmpty(page)) { - l = PageAddItem(page, (Item) itup, IndexTupleSize(itup), - FirstOffsetNumber, - LP_USED); - } else { - l = PageAddItem(page, (Item) itup, IndexTupleSize(itup), - OffsetNumberNext(PageGetMaxOffsetNumber(page)), - LP_USED); - } - - WriteBuffer(buffer); - - datum = (((char *) itup) + sizeof(IndexTupleData)); - - /* now expand the page boundary in the parent to include the new child */ - rttighten(r, stack, datum, - (IndexTupleSize(itup) - sizeof(IndexTupleData)), rtstate); - freestack(stack); - - /* build and return an InsertIndexResult for this insertion */ - res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); - ItemPointerSet(&(res->pointerData), blk, l); - - return (res); } static void rttighten(Relation r, - RTSTACK *stk, - char *datum, - int att_size, - RTSTATE *rtstate) + RTSTACK * stk, + char *datum, + int att_size, + RTSTATE * rtstate) { - char *oldud; - char *tdatum; - Page p; - float old_size, newd_size; - Buffer b; - - if (stk == (RTSTACK *) NULL) - return; - - b = ReadBuffer(r, stk->rts_blk); - p = BufferGetPage(b); - - oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->rts_child)); - oldud += sizeof(IndexTupleData); - - (*rtstate->sizeFn)(oldud, &old_size); - datum = (char *) (*rtstate->unionFn)(oldud, datum); - - (*rtstate->sizeFn)(datum, &newd_size); - - if (newd_size != old_size) { - TupleDesc td = RelationGetTupleDescriptor(r); - - if (td->attrs[0]->attlen < 0) { - /* - * This is an internal page, so 'oldud' had better be a - * union (constant-length) key, too. (See comment below.) - */ - Assert(VARSIZE(datum) == VARSIZE(oldud)); - memmove(oldud, datum, VARSIZE(datum)); - } else { - memmove(oldud, datum, att_size); + char *oldud; + char *tdatum; + Page p; + float old_size, + newd_size; + Buffer b; + + if (stk == (RTSTACK *) NULL) + return; + + b = ReadBuffer(r, stk->rts_blk); + p = BufferGetPage(b); + + oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->rts_child)); + oldud += sizeof(IndexTupleData); + + (*rtstate->sizeFn) (oldud, &old_size); + datum = (char *) (*rtstate->unionFn) (oldud, datum); + + (*rtstate->sizeFn) (datum, &newd_size); + + if (newd_size != old_size) + { + TupleDesc td = RelationGetTupleDescriptor(r); + + if (td->attrs[0]->attlen < 0) + { + + /* + * This is an internal page, so 'oldud' had better be a union + * (constant-length) key, too. (See comment below.) + */ + Assert(VARSIZE(datum) == VARSIZE(oldud)); + memmove(oldud, datum, VARSIZE(datum)); + } + else + { + memmove(oldud, datum, att_size); + } + WriteBuffer(b); + + /* + * The user may be defining an index on variable-sized data (like + * polygons). If so, we need to get a constant-sized datum for + * insertion on the internal page. We do this by calling the + * union proc, which is guaranteed to return a rectangle. + */ + + tdatum = (char *) (*rtstate->unionFn) (datum, datum); + rttighten(r, stk->rts_parent, tdatum, att_size, rtstate); + pfree(tdatum); } - WriteBuffer(b); - - /* - * The user may be defining an index on variable-sized data (like - * polygons). If so, we need to get a constant-sized datum for - * insertion on the internal page. We do this by calling the union - * proc, which is guaranteed to return a rectangle. - */ - - tdatum = (char *) (*rtstate->unionFn)(datum, datum); - rttighten(r, stk->rts_parent, tdatum, att_size, rtstate); - pfree(tdatum); - } else { - ReleaseBuffer(b); - } - pfree(datum); + else + { + ReleaseBuffer(b); + } + pfree(datum); } /* - * dosplit -- split a page in the tree. + * dosplit -- split a page in the tree. * - * This is the quadratic-cost split algorithm Guttman describes in - * his paper. The reason we chose it is that you can implement this - * with less information about the data types on which you're operating. + * This is the quadratic-cost split algorithm Guttman describes in + * his paper. The reason we chose it is that you can implement this + * with less information about the data types on which you're operating. */ -static InsertIndexResult +static InsertIndexResult dosplit(Relation r, - Buffer buffer, - RTSTACK *stack, - IndexTuple itup, - RTSTATE *rtstate) + Buffer buffer, + RTSTACK * stack, + IndexTuple itup, + RTSTATE * rtstate) { - Page p; - Buffer leftbuf, rightbuf; - Page left, right; - ItemId itemid; - IndexTuple item; - IndexTuple ltup, rtup; - OffsetNumber maxoff; - OffsetNumber i; - OffsetNumber leftoff, rightoff; - BlockNumber lbknum, rbknum; - BlockNumber bufblock; - RTreePageOpaque opaque; - int blank; - InsertIndexResult res; - char *isnull; - SPLITVEC v; - TupleDesc tupDesc; - - isnull = (char *) palloc(r->rd_rel->relnatts); - for (blank = 0; blank < r->rd_rel->relnatts; blank++) - isnull[blank] = ' '; - p = (Page) BufferGetPage(buffer); - opaque = (RTreePageOpaque) 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) == P_ROOT) { - leftbuf = ReadBuffer(r, P_NEW); - RTInitBuffer(leftbuf, opaque->flags); - lbknum = BufferGetBlockNumber(leftbuf); - left = (Page) BufferGetPage(leftbuf); - } else { - leftbuf = buffer; - IncrBufferRefCount(buffer); - lbknum = BufferGetBlockNumber(buffer); - left = (Page) PageGetTempPage(p, sizeof(RTreePageOpaqueData)); - } - - rightbuf = ReadBuffer(r, P_NEW); - RTInitBuffer(rightbuf, opaque->flags); - rbknum = BufferGetBlockNumber(rightbuf); - right = (Page) BufferGetPage(rightbuf); - - picksplit(r, p, &v, itup, rtstate); - - 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)) { - PageAddItem(left, (Item) item, IndexTupleSize(item), - leftoff, LP_USED); - leftoff = OffsetNumberNext(leftoff); - v.spl_left++; /* advance in left split vector */ - } else { - PageAddItem(right, (Item) item, IndexTupleSize(item), - rightoff, LP_USED); - rightoff = OffsetNumberNext(rightoff); - v.spl_right++; /* advance in right split vector */ + Page p; + Buffer leftbuf, + rightbuf; + Page left, + right; + ItemId itemid; + IndexTuple item; + IndexTuple ltup, + rtup; + OffsetNumber maxoff; + OffsetNumber i; + OffsetNumber leftoff, + rightoff; + BlockNumber lbknum, + rbknum; + BlockNumber bufblock; + RTreePageOpaque opaque; + int blank; + InsertIndexResult res; + char *isnull; + SPLITVEC v; + TupleDesc tupDesc; + + isnull = (char *) palloc(r->rd_rel->relnatts); + for (blank = 0; blank < r->rd_rel->relnatts; blank++) + isnull[blank] = ' '; + p = (Page) BufferGetPage(buffer); + opaque = (RTreePageOpaque) 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) == P_ROOT) + { + leftbuf = ReadBuffer(r, P_NEW); + RTInitBuffer(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) { - PageAddItem(left, (Item) itup, IndexTupleSize(itup), - leftoff, LP_USED); - leftoff = OffsetNumberNext(leftoff); - ItemPointerSet(&(res->pointerData), lbknum, leftoff); - } else { - PageAddItem(right, (Item) itup, IndexTupleSize(itup), - rightoff, LP_USED); - rightoff = OffsetNumberNext(rightoff); - ItemPointerSet(&(res->pointerData), rbknum, rightoff); - } - - if ((bufblock = BufferGetBlockNumber(buffer)) != P_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 */ - rtadjscans(r, RTOP_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); - - rtintinsert(r, stack, ltup, rtup, rtstate); - - pfree(ltup); - pfree(rtup); - - return (res); + else + { + leftbuf = buffer; + IncrBufferRefCount(buffer); + lbknum = BufferGetBlockNumber(buffer); + left = (Page) PageGetTempPage(p, sizeof(RTreePageOpaqueData)); + } + + rightbuf = ReadBuffer(r, P_NEW); + RTInitBuffer(rightbuf, opaque->flags); + rbknum = BufferGetBlockNumber(rightbuf); + right = (Page) BufferGetPage(rightbuf); + + picksplit(r, p, &v, itup, rtstate); + + 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)) + { + PageAddItem(left, (Item) item, IndexTupleSize(item), + leftoff, LP_USED); + leftoff = OffsetNumberNext(leftoff); + v.spl_left++; /* advance in left split vector */ + } + else + { + PageAddItem(right, (Item) item, IndexTupleSize(item), + rightoff, LP_USED); + rightoff = OffsetNumberNext(rightoff); + v.spl_right++; /* advance in right split vector */ + } + } + + /* build an InsertIndexResult for this insertion */ + res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData)); + + /* now insert the new index tuple */ + if (*(v.spl_left) != FirstOffsetNumber) + { + PageAddItem(left, (Item) itup, IndexTupleSize(itup), + leftoff, LP_USED); + leftoff = OffsetNumberNext(leftoff); + ItemPointerSet(&(res->pointerData), lbknum, leftoff); + } + else + { + PageAddItem(right, (Item) itup, IndexTupleSize(itup), + rightoff, LP_USED); + rightoff = OffsetNumberNext(rightoff); + ItemPointerSet(&(res->pointerData), rbknum, rightoff); + } + + if ((bufblock = BufferGetBlockNumber(buffer)) != P_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 */ + rtadjscans(r, RTOP_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); + + rtintinsert(r, stack, ltup, rtup, rtstate); + + pfree(ltup); + pfree(rtup); + + return (res); } static void rtintinsert(Relation r, - RTSTACK *stk, - IndexTuple ltup, - IndexTuple rtup, - RTSTATE *rtstate) + RTSTACK * stk, + IndexTuple ltup, + IndexTuple rtup, + RTSTATE * rtstate) { - IndexTuple old; - Buffer b; - Page p; - char *ldatum, *rdatum, *newdatum; - InsertIndexResult res; - - if (stk == (RTSTACK *) NULL) { - rtnewroot(r, ltup, rtup); - return; - } - - b = ReadBuffer(r, stk->rts_blk); - p = BufferGetPage(b); - old = (IndexTuple) PageGetItem(p, PageGetItemId(p, stk->rts_child)); - - /* - * This is a hack. Right now, we force rtree keys to be constant size. - * To fix this, need delete the old key and add both left and right - * for the two new pages. The insertion of left may force a split if - * the new left key is bigger than the old key. - */ - - if (IndexTupleSize(old) != IndexTupleSize(ltup)) - elog(WARN, "Variable-length rtree keys are not supported."); - - /* install pointer to left child */ - memmove(old, ltup,IndexTupleSize(ltup)); - - if (nospace(p, rtup)) { - newdatum = (((char *) ltup) + sizeof(IndexTupleData)); - rttighten(r, stk->rts_parent, newdatum, - (IndexTupleSize(ltup) - sizeof(IndexTupleData)), rtstate); - res = dosplit(r, b, stk->rts_parent, rtup, rtstate); - WriteBuffer(b); /* don't forget to release buffer! - 01/31/94 */ - pfree(res); - } else { - PageAddItem(p, (Item) rtup, IndexTupleSize(rtup), - PageGetMaxOffsetNumber(p), LP_USED); - WriteBuffer(b); - ldatum = (((char *) ltup) + sizeof(IndexTupleData)); - rdatum = (((char *) rtup) + sizeof(IndexTupleData)); - newdatum = (char *) (*rtstate->unionFn)(ldatum, rdatum); - - rttighten(r, stk->rts_parent, newdatum, - (IndexTupleSize(rtup) - sizeof(IndexTupleData)), rtstate); - - pfree(newdatum); - } + IndexTuple old; + Buffer b; + Page p; + char *ldatum, + *rdatum, + *newdatum; + InsertIndexResult res; + + if (stk == (RTSTACK *) NULL) + { + rtnewroot(r, ltup, rtup); + return; + } + + b = ReadBuffer(r, stk->rts_blk); + p = BufferGetPage(b); + old = (IndexTuple) PageGetItem(p, PageGetItemId(p, stk->rts_child)); + + /* + * This is a hack. Right now, we force rtree keys to be constant + * size. To fix this, need delete the old key and add both left and + * right for the two new pages. The insertion of left may force a + * split if the new left key is bigger than the old key. + */ + + if (IndexTupleSize(old) != IndexTupleSize(ltup)) + elog(WARN, "Variable-length rtree keys are not supported."); + + /* install pointer to left child */ + memmove(old, ltup, IndexTupleSize(ltup)); + + if (nospace(p, rtup)) + { + newdatum = (((char *) ltup) + sizeof(IndexTupleData)); + rttighten(r, stk->rts_parent, newdatum, + (IndexTupleSize(ltup) - sizeof(IndexTupleData)), rtstate); + res = dosplit(r, b, stk->rts_parent, rtup, rtstate); + WriteBuffer(b); /* don't forget to release buffer! - + * 01/31/94 */ + pfree(res); + } + else + { + PageAddItem(p, (Item) rtup, IndexTupleSize(rtup), + PageGetMaxOffsetNumber(p), LP_USED); + WriteBuffer(b); + ldatum = (((char *) ltup) + sizeof(IndexTupleData)); + rdatum = (((char *) rtup) + sizeof(IndexTupleData)); + newdatum = (char *) (*rtstate->unionFn) (ldatum, rdatum); + + rttighten(r, stk->rts_parent, newdatum, + (IndexTupleSize(rtup) - sizeof(IndexTupleData)), rtstate); + + pfree(newdatum); + } } static void rtnewroot(Relation r, IndexTuple lt, IndexTuple rt) { - Buffer b; - Page p; - - b = ReadBuffer(r, P_ROOT); - RTInitBuffer(b, 0); - p = BufferGetPage(b); - PageAddItem(p, (Item) lt, IndexTupleSize(lt), - FirstOffsetNumber, LP_USED); - PageAddItem(p, (Item) rt, IndexTupleSize(rt), - OffsetNumberNext(FirstOffsetNumber), LP_USED); - WriteBuffer(b); + Buffer b; + Page p; + + b = ReadBuffer(r, P_ROOT); + RTInitBuffer(b, 0); + p = BufferGetPage(b); + PageAddItem(p, (Item) lt, IndexTupleSize(lt), + FirstOffsetNumber, LP_USED); + PageAddItem(p, (Item) rt, IndexTupleSize(rt), + OffsetNumberNext(FirstOffsetNumber), LP_USED); + WriteBuffer(b); } static void picksplit(Relation r, - Page page, - SPLITVEC *v, - IndexTuple itup, - RTSTATE *rtstate) + Page page, + SPLITVEC * v, + IndexTuple itup, + RTSTATE * rtstate) { - OffsetNumber maxoff; - OffsetNumber i, j; - IndexTuple item_1, item_2; - char *datum_alpha, *datum_beta; - char *datum_l, *datum_r; - char *union_d, *union_dl, *union_dr; - char *inter_d; - bool firsttime; - float size_alpha, size_beta, size_union, size_inter; - float size_waste, waste; - float size_l, size_r; - int nbytes; - OffsetNumber seed_1 = 0, seed_2 = 0; - OffsetNumber *left, *right; - - maxoff = PageGetMaxOffsetNumber(page); - - nbytes = (maxoff + 2) * sizeof(OffsetNumber); - v->spl_left = (OffsetNumber *) palloc(nbytes); - v->spl_right = (OffsetNumber *) palloc(nbytes); - - firsttime = true; - waste = 0.0; - - for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) { - item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, i)); - datum_alpha = ((char *) item_1) + sizeof(IndexTupleData); - for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) { - item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, j)); - datum_beta = ((char *) item_2) + sizeof(IndexTupleData); - - /* compute the wasted space by unioning these guys */ - union_d = (char *)(rtstate->unionFn)(datum_alpha, datum_beta); - (rtstate->sizeFn)(union_d, &size_union); - inter_d = (char *)(rtstate->interFn)(datum_alpha, datum_beta); - (rtstate->sizeFn)(inter_d, &size_inter); - size_waste = size_union - size_inter; - - pfree(union_d); - - if (inter_d != (char *) NULL) - pfree(inter_d); - - /* - * are these a more promising split that what we've - * already seen? - */ - - if (size_waste > waste || firsttime) { - waste = size_waste; - seed_1 = i; - seed_2 = j; - firsttime = false; - } + OffsetNumber maxoff; + OffsetNumber i, + j; + IndexTuple item_1, + item_2; + char *datum_alpha, + *datum_beta; + char *datum_l, + *datum_r; + char *union_d, + *union_dl, + *union_dr; + char *inter_d; + bool firsttime; + float size_alpha, + size_beta, + size_union, + size_inter; + float size_waste, + waste; + float size_l, + size_r; + int nbytes; + OffsetNumber seed_1 = 0, + seed_2 = 0; + OffsetNumber *left, + *right; + + maxoff = PageGetMaxOffsetNumber(page); + + nbytes = (maxoff + 2) * sizeof(OffsetNumber); + v->spl_left = (OffsetNumber *) palloc(nbytes); + v->spl_right = (OffsetNumber *) palloc(nbytes); + + firsttime = true; + waste = 0.0; + + for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) + { + item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, i)); + datum_alpha = ((char *) item_1) + sizeof(IndexTupleData); + for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) + { + item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, j)); + datum_beta = ((char *) item_2) + sizeof(IndexTupleData); + + /* compute the wasted space by unioning these guys */ + union_d = (char *) (rtstate->unionFn) (datum_alpha, datum_beta); + (rtstate->sizeFn) (union_d, &size_union); + inter_d = (char *) (rtstate->interFn) (datum_alpha, datum_beta); + (rtstate->sizeFn) (inter_d, &size_inter); + size_waste = size_union - size_inter; + + pfree(union_d); + + if (inter_d != (char *) NULL) + pfree(inter_d); + + /* + * are these a more promising split that what we've already + * seen? + */ + + if (size_waste > waste || firsttime) + { + waste = size_waste; + seed_1 = i; + seed_2 = j; + firsttime = false; + } + } } - } - - left = v->spl_left; - v->spl_nleft = 0; - right = v->spl_right; - v->spl_nright = 0; - - item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_1)); - datum_alpha = ((char *) item_1) + sizeof(IndexTupleData); - datum_l = (char *)(*rtstate->unionFn)(datum_alpha, datum_alpha); - (*rtstate->sizeFn)(datum_l, &size_l); - item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_2)); - datum_beta = ((char *) item_2) + sizeof(IndexTupleData); - datum_r = (char *)(*rtstate->unionFn)(datum_beta, datum_beta); - (*rtstate->sizeFn)(datum_r, &size_r); - - /* - * Now split up the regions between the two seeds. An important - * property of this split algorithm is that the split vector v - * has the indices of items to be split in order in its left and - * right vectors. We exploit this property by doing a merge in - * the code that actually splits the page. - * - * For efficiency, we also place the new index tuple in this loop. - * This is handled at the very end, when we have placed all the - * existing tuples and i == maxoff + 1. - */ - - maxoff = OffsetNumberNext(maxoff); - for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { - + + left = v->spl_left; + v->spl_nleft = 0; + right = v->spl_right; + v->spl_nright = 0; + + item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_1)); + datum_alpha = ((char *) item_1) + sizeof(IndexTupleData); + datum_l = (char *) (*rtstate->unionFn) (datum_alpha, datum_alpha); + (*rtstate->sizeFn) (datum_l, &size_l); + item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_2)); + datum_beta = ((char *) item_2) + sizeof(IndexTupleData); + datum_r = (char *) (*rtstate->unionFn) (datum_beta, datum_beta); + (*rtstate->sizeFn) (datum_r, &size_r); + /* - * If we've already decided where to place this item, just - * put it on the right list. Otherwise, we need to figure - * out which page needs the least enlargement in order to - * store the item. + * Now split up the regions between the two seeds. An important + * property of this split algorithm is that the split vector v has the + * indices of items to be split in order in its left and right + * vectors. We exploit this property by doing a merge in the code + * that actually splits the page. + * + * For efficiency, we also place the new index tuple in this loop. This + * is handled at the very end, when we have placed all the existing + * tuples and i == maxoff + 1. */ - - if (i == seed_1) { - *left++ = i; - v->spl_nleft++; - continue; - } else if (i == seed_2) { - *right++ = i; - v->spl_nright++; - continue; - } - - /* okay, which page needs least enlargement? */ - if (i == maxoff) { - item_1 = itup; - } else { - item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, i)); - } - - datum_alpha = ((char *) item_1) + sizeof(IndexTupleData); - union_dl = (char *)(*rtstate->unionFn)(datum_l, datum_alpha); - union_dr = (char *)(*rtstate->unionFn)(datum_r, datum_alpha); - (*rtstate->sizeFn)(union_dl, &size_alpha); - (*rtstate->sizeFn)(union_dr, &size_beta); - - /* pick which page to add it to */ - if (size_alpha - size_l < size_beta - size_r) { - pfree(datum_l); - pfree(union_dr); - datum_l = union_dl; - size_l = size_alpha; - *left++ = i; - v->spl_nleft++; - } else { - pfree(datum_r); - pfree(union_dl); - datum_r = union_dr; - size_r = size_alpha; - *right++ = i; - v->spl_nright++; + + maxoff = OffsetNumberNext(maxoff); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + + /* + * If we've already decided where to place this item, just put it + * on the right list. Otherwise, we need to figure out which page + * needs the least enlargement in order to store the item. + */ + + if (i == seed_1) + { + *left++ = i; + v->spl_nleft++; + continue; + } + else if (i == seed_2) + { + *right++ = i; + v->spl_nright++; + continue; + } + + /* okay, which page needs least enlargement? */ + if (i == maxoff) + { + item_1 = itup; + } + else + { + item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, i)); + } + + datum_alpha = ((char *) item_1) + sizeof(IndexTupleData); + union_dl = (char *) (*rtstate->unionFn) (datum_l, datum_alpha); + union_dr = (char *) (*rtstate->unionFn) (datum_r, datum_alpha); + (*rtstate->sizeFn) (union_dl, &size_alpha); + (*rtstate->sizeFn) (union_dr, &size_beta); + + /* pick which page to add it to */ + if (size_alpha - size_l < size_beta - size_r) + { + pfree(datum_l); + pfree(union_dr); + datum_l = union_dl; + size_l = size_alpha; + *left++ = i; + v->spl_nleft++; + } + else + { + pfree(datum_r); + pfree(union_dl); + datum_r = union_dr; + size_r = size_alpha; + *right++ = i; + v->spl_nright++; + } } - } - *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ - - v->spl_ldatum = datum_l; - v->spl_rdatum = datum_r; + *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ + + v->spl_ldatum = datum_l; + v->spl_rdatum = datum_r; } static void RTInitBuffer(Buffer b, uint32 f) { - RTreePageOpaque opaque; - Page page; - Size pageSize; - - pageSize = BufferGetPageSize(b); - - page = BufferGetPage(b); - memset(page, 0, (int) pageSize); - PageInit(page, pageSize, sizeof(RTreePageOpaqueData)); - - opaque = (RTreePageOpaque) PageGetSpecialPointer(page); - opaque->flags = f; + RTreePageOpaque opaque; + Page page; + Size pageSize; + + pageSize = BufferGetPageSize(b); + + page = BufferGetPage(b); + memset(page, 0, (int) pageSize); + PageInit(page, pageSize, sizeof(RTreePageOpaqueData)); + + opaque = (RTreePageOpaque) PageGetSpecialPointer(page); + opaque->flags = f; } -static OffsetNumber -choose(Relation r, Page p, IndexTuple it, RTSTATE *rtstate) +static OffsetNumber +choose(Relation r, Page p, IndexTuple it, RTSTATE * rtstate) { - OffsetNumber maxoff; - OffsetNumber i; - char *ud, *id; - char *datum; - float usize, dsize; - OffsetNumber which; - float which_grow; - - id = ((char *) it) + sizeof(IndexTupleData); - maxoff = PageGetMaxOffsetNumber(p); - which_grow = -1.0; - which = -1; - - for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { - datum = (char *) PageGetItem(p, PageGetItemId(p, i)); - datum += sizeof(IndexTupleData); - (*rtstate->sizeFn)(datum, &dsize); - ud = (char *) (*rtstate->unionFn)(datum, id); - (*rtstate->sizeFn)(ud, &usize); - pfree(ud); - if (which_grow < 0 || usize - dsize < which_grow) { - which = i; - which_grow = usize - dsize; - if (which_grow == 0) - break; + OffsetNumber maxoff; + OffsetNumber i; + char *ud, + *id; + char *datum; + float usize, + dsize; + OffsetNumber which; + float which_grow; + + id = ((char *) it) + sizeof(IndexTupleData); + maxoff = PageGetMaxOffsetNumber(p); + which_grow = -1.0; + which = -1; + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + datum = (char *) PageGetItem(p, PageGetItemId(p, i)); + datum += sizeof(IndexTupleData); + (*rtstate->sizeFn) (datum, &dsize); + ud = (char *) (*rtstate->unionFn) (datum, id); + (*rtstate->sizeFn) (ud, &usize); + pfree(ud); + if (which_grow < 0 || usize - dsize < which_grow) + { + which = i; + which_grow = usize - dsize; + if (which_grow == 0) + break; + } } - } - - return (which); + + return (which); } static int nospace(Page p, IndexTuple it) { - return (PageGetFreeSpace(p) < IndexTupleSize(it)); + return (PageGetFreeSpace(p) < IndexTupleSize(it)); } void -freestack(RTSTACK *s) +freestack(RTSTACK * s) { - RTSTACK *p; - - while (s != (RTSTACK *) NULL) { - p = s->rts_parent; - pfree(s); - s = p; - } + RTSTACK *p; + + while (s != (RTSTACK *) NULL) + { + p = s->rts_parent; + pfree(s); + s = p; + } } -char * +char * rtdelete(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 */ - rtadjscans(r, RTOP_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 */ - return ((char *) NULL); + 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 */ + rtadjscans(r, RTOP_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 */ + return ((char *) NULL); } -static void initRtstate(RTSTATE *rtstate, Relation index) +static void +initRtstate(RTSTATE * rtstate, Relation index) { - RegProcedure union_proc, size_proc, inter_proc; - func_ptr user_fn; - int pronargs; - - union_proc = index_getprocid(index, 1, RT_UNION_PROC); - size_proc = index_getprocid(index, 1, RT_SIZE_PROC); - inter_proc = index_getprocid(index, 1, RT_INTER_PROC); - fmgr_info(union_proc, &user_fn, &pronargs); - rtstate->unionFn = user_fn; - fmgr_info(size_proc, &user_fn, &pronargs); - rtstate->sizeFn = user_fn; - fmgr_info(inter_proc, &user_fn, &pronargs); - rtstate->interFn = user_fn; - return; + RegProcedure union_proc, + size_proc, + inter_proc; + func_ptr user_fn; + int pronargs; + + union_proc = index_getprocid(index, 1, RT_UNION_PROC); + size_proc = index_getprocid(index, 1, RT_SIZE_PROC); + inter_proc = index_getprocid(index, 1, RT_INTER_PROC); + fmgr_info(union_proc, &user_fn, &pronargs); + rtstate->unionFn = user_fn; + fmgr_info(size_proc, &user_fn, &pronargs); + rtstate->sizeFn = user_fn; + fmgr_info(inter_proc, &user_fn, &pronargs); + rtstate->interFn = user_fn; + return; } #ifdef RTDEBUG @@ -914,48 +1011,52 @@ static void initRtstate(RTSTATE *rtstate, Relation index) void _rtdump(Relation r) { - Buffer buf; - Page page; - OffsetNumber offnum, maxoff; - BlockNumber blkno; - BlockNumber nblocks; - RTreePageOpaque 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 = (RTreePageOpaque) 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); - itkey = (char *) box_out((BOX *) datum); - 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; + RTreePageOpaque 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 = (RTreePageOpaque) 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); + itkey = (char *) box_out((BOX *) datum); + printf("\t[%d] size %d heap <%d,%d> key:%s\n", + offnum, IndexTupleSize(itup), itblkno, itoffno, itkey); + pfree(itkey); + } + + ReleaseBuffer(buf); } - - ReleaseBuffer(buf); - } } -#endif /* defined RTDEBUG */ +#endif /* defined RTDEBUG */ diff --git a/src/backend/access/rtree/rtscan.c b/src/backend/access/rtree/rtscan.c index bb8e1dcc719..26590059d6c 100644 --- a/src/backend/access/rtree/rtscan.c +++ b/src/backend/access/rtree/rtscan.c @@ -1,19 +1,19 @@ /*------------------------------------------------------------------------- * * rtscan.c-- - * routines to manage scans on index relations + * routines to manage scans on index relations * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.10 1997/05/20 10:29:30 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.11 1997/09/07 04:39:24 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <storage/bufmgr.h> #include <access/genam.h> #include <storage/lmgr.h> @@ -21,377 +21,411 @@ #include <access/rtree.h> #include <access/rtstrat.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 rtregscan(IndexScanDesc s); -static void rtdropscan(IndexScanDesc s); -static void rtadjone(IndexScanDesc s, int op, BlockNumber blkno, - OffsetNumber offnum); -static void adjuststack(RTSTACK *stk, BlockNumber blkno, +static void rtregscan(IndexScanDesc s); +static void rtdropscan(IndexScanDesc s); +static void +rtadjone(IndexScanDesc s, int op, BlockNumber blkno, + OffsetNumber offnum); +static void +adjuststack(RTSTACK * 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 an rtree scan in a backend, we register it in private - * space. Then if the rtree 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 rtree 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 an rtree scan in a backend, we register it in private + * space. Then if the rtree 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 rtree 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 RTScanListData { - IndexScanDesc rtsl_scan; - struct RTScanListData *rtsl_next; -} RTScanListData; +typedef struct RTScanListData +{ + IndexScanDesc rtsl_scan; + struct RTScanListData *rtsl_next; +} RTScanListData; -typedef RTScanListData *RTScanList; +typedef RTScanListData *RTScanList; /* pointer to list of local scans on rtrees */ static RTScanList RTScans = (RTScanList) NULL; - + IndexScanDesc rtbeginscan(Relation r, - bool fromEnd, - uint16 nkeys, - ScanKey key) + bool fromEnd, + uint16 nkeys, + ScanKey key) { - IndexScanDesc s; - - RelationSetLockForRead(r); - s = RelationGetIndexScan(r, fromEnd, nkeys, key); - rtregscan(s); - - return (s); + IndexScanDesc s; + + RelationSetLockForRead(r); + s = RelationGetIndexScan(r, fromEnd, nkeys, key); + rtregscan(s); + + return (s); } void rtrescan(IndexScanDesc s, bool fromEnd, ScanKey key) { - RTreeScanOpaque p; - RegProcedure internal_proc; - int i; - - if (!IndexScanIsValid(s)) { - elog(WARN, "rtrescan: 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 = (RTreeScanOpaque) s->opaque; - if (p != (RTreeScanOpaque) NULL) { - freestack(p->s_stack); - freestack(p->s_markstk); - p->s_stack = p->s_markstk = (RTSTACK *) NULL; - p->s_flags = 0x0; - for (i = 0; i < s->numberOfKeys; i++) + RTreeScanOpaque p; + RegProcedure internal_proc; + int i; + + if (!IndexScanIsValid(s)) + { + elog(WARN, "rtrescan: 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) { - p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument; + memmove(s->keyData, + key, + s->numberOfKeys * sizeof(ScanKeyData)); } - } else { - /* initialize opaque data */ - p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData)); - p->s_stack = p->s_markstk = (RTSTACK *) NULL; - p->s_internalNKey = s->numberOfKeys; - p->s_flags = 0x0; - s->opaque = p; - if (s->numberOfKeys > 0) { - p->s_internalKey = - (ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys); - - /* - * Scans on internal pages use different operators than they - * do on leaf pages. For example, if the user wants all boxes - * that exactly match (x1,y1,x2,y2), then on internal pages - * we need to find all boxes that contain (x1,y1,x2,y2). - */ - - for (i = 0; i < s->numberOfKeys; i++) { - p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument; - internal_proc = RTMapOperator(s->relation, - s->keyData[i].sk_attno, - s->keyData[i].sk_procedure); - ScanKeyEntryInitialize(&(p->s_internalKey[i]), - s->keyData[i].sk_flags, - s->keyData[i].sk_attno, - internal_proc, - s->keyData[i].sk_argument); - } + + p = (RTreeScanOpaque) s->opaque; + if (p != (RTreeScanOpaque) NULL) + { + freestack(p->s_stack); + freestack(p->s_markstk); + p->s_stack = p->s_markstk = (RTSTACK *) NULL; + p->s_flags = 0x0; + for (i = 0; i < s->numberOfKeys; i++) + { + p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument; + } + } + else + { + /* initialize opaque data */ + p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData)); + p->s_stack = p->s_markstk = (RTSTACK *) NULL; + p->s_internalNKey = s->numberOfKeys; + p->s_flags = 0x0; + s->opaque = p; + if (s->numberOfKeys > 0) + { + p->s_internalKey = + (ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys); + + /* + * Scans on internal pages use different operators than they + * do on leaf pages. For example, if the user wants all boxes + * that exactly match (x1,y1,x2,y2), then on internal pages we + * need to find all boxes that contain (x1,y1,x2,y2). + */ + + for (i = 0; i < s->numberOfKeys; i++) + { + p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument; + internal_proc = RTMapOperator(s->relation, + s->keyData[i].sk_attno, + s->keyData[i].sk_procedure); + ScanKeyEntryInitialize(&(p->s_internalKey[i]), + s->keyData[i].sk_flags, + s->keyData[i].sk_attno, + internal_proc, + s->keyData[i].sk_argument); + } + } } - } } void rtmarkpos(IndexScanDesc s) { - RTreeScanOpaque p; - RTSTACK *o, *n, *tmp; - - s->currentMarkData = s->currentItemData; - p = (RTreeScanOpaque) s->opaque; - if (p->s_flags & RTS_CURBEFORE) - p->s_flags |= RTS_MRKBEFORE; - else - p->s_flags &= ~RTS_MRKBEFORE; - - o = (RTSTACK *) NULL; - n = p->s_stack; - - /* copy the parent stack from the current item data */ - while (n != (RTSTACK *) NULL) { - tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); - tmp->rts_child = n->rts_child; - tmp->rts_blk = n->rts_blk; - tmp->rts_parent = o; - o = tmp; - n = n->rts_parent; - } - - freestack(p->s_markstk); - p->s_markstk = o; + RTreeScanOpaque p; + RTSTACK *o, + *n, + *tmp; + + s->currentMarkData = s->currentItemData; + p = (RTreeScanOpaque) s->opaque; + if (p->s_flags & RTS_CURBEFORE) + p->s_flags |= RTS_MRKBEFORE; + else + p->s_flags &= ~RTS_MRKBEFORE; + + o = (RTSTACK *) NULL; + n = p->s_stack; + + /* copy the parent stack from the current item data */ + while (n != (RTSTACK *) NULL) + { + tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); + tmp->rts_child = n->rts_child; + tmp->rts_blk = n->rts_blk; + tmp->rts_parent = o; + o = tmp; + n = n->rts_parent; + } + + freestack(p->s_markstk); + p->s_markstk = o; } void rtrestrpos(IndexScanDesc s) { - RTreeScanOpaque p; - RTSTACK *o, *n, *tmp; - - s->currentItemData = s->currentMarkData; - p = (RTreeScanOpaque) s->opaque; - if (p->s_flags & RTS_MRKBEFORE) - p->s_flags |= RTS_CURBEFORE; - else - p->s_flags &= ~RTS_CURBEFORE; - - o = (RTSTACK *) NULL; - n = p->s_markstk; - - /* copy the parent stack from the current item data */ - while (n != (RTSTACK *) NULL) { - tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); - tmp->rts_child = n->rts_child; - tmp->rts_blk = n->rts_blk; - tmp->rts_parent = o; - o = tmp; - n = n->rts_parent; - } - - freestack(p->s_stack); - p->s_stack = o; + RTreeScanOpaque p; + RTSTACK *o, + *n, + *tmp; + + s->currentItemData = s->currentMarkData; + p = (RTreeScanOpaque) s->opaque; + if (p->s_flags & RTS_MRKBEFORE) + p->s_flags |= RTS_CURBEFORE; + else + p->s_flags &= ~RTS_CURBEFORE; + + o = (RTSTACK *) NULL; + n = p->s_markstk; + + /* copy the parent stack from the current item data */ + while (n != (RTSTACK *) NULL) + { + tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); + tmp->rts_child = n->rts_child; + tmp->rts_blk = n->rts_blk; + tmp->rts_parent = o; + o = tmp; + n = n->rts_parent; + } + + freestack(p->s_stack); + p->s_stack = o; } void rtendscan(IndexScanDesc s) { - RTreeScanOpaque p; - - p = (RTreeScanOpaque) s->opaque; - - if (p != (RTreeScanOpaque) NULL) { - freestack(p->s_stack); - freestack(p->s_markstk); - pfree (s->opaque); - } - - rtdropscan(s); - /* XXX don't unset read lock -- two-phase locking */ + RTreeScanOpaque p; + + p = (RTreeScanOpaque) s->opaque; + + if (p != (RTreeScanOpaque) NULL) + { + freestack(p->s_stack); + freestack(p->s_markstk); + pfree(s->opaque); + } + + rtdropscan(s); + /* XXX don't unset read lock -- two-phase locking */ } static void rtregscan(IndexScanDesc s) { - RTScanList l; - - l = (RTScanList) palloc(sizeof(RTScanListData)); - l->rtsl_scan = s; - l->rtsl_next = RTScans; - RTScans = l; + RTScanList l; + + l = (RTScanList) palloc(sizeof(RTScanListData)); + l->rtsl_scan = s; + l->rtsl_next = RTScans; + RTScans = l; } static void rtdropscan(IndexScanDesc s) { - RTScanList l; - RTScanList prev; - - prev = (RTScanList) NULL; - - for (l = RTScans; - l != (RTScanList) NULL && l->rtsl_scan != s; - l = l->rtsl_next) { - prev = l; - } - - if (l == (RTScanList) NULL) - elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s); - - if (prev == (RTScanList) NULL) - RTScans = l->rtsl_next; - else - prev->rtsl_next = l->rtsl_next; - - pfree(l); + RTScanList l; + RTScanList prev; + + prev = (RTScanList) NULL; + + for (l = RTScans; + l != (RTScanList) NULL && l->rtsl_scan != s; + l = l->rtsl_next) + { + prev = l; + } + + if (l == (RTScanList) NULL) + elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s); + + if (prev == (RTScanList) NULL) + RTScans = l->rtsl_next; + else + prev->rtsl_next = l->rtsl_next; + + pfree(l); } void rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum) { - RTScanList l; - Oid relid; - - relid = r->rd_id; - for (l = RTScans; l != (RTScanList) NULL; l = l->rtsl_next) { - if (l->rtsl_scan->relation->rd_id == relid) - rtadjone(l->rtsl_scan, op, blkno, offnum); - } + RTScanList l; + Oid relid; + + relid = r->rd_id; + for (l = RTScans; l != (RTScanList) NULL; l = l->rtsl_next) + { + if (l->rtsl_scan->relation->rd_id == relid) + rtadjone(l->rtsl_scan, op, blkno, offnum); + } } /* - * rtadjone() -- adjust one scan for update. + * rtadjone() -- 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 rtadjone(IndexScanDesc s, - int op, - BlockNumber blkno, - OffsetNumber offnum) + int op, + BlockNumber blkno, + OffsetNumber offnum) { - RTreeScanOpaque so; - - adjustiptr(s, &(s->currentItemData), op, blkno, offnum); - adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); - - so = (RTreeScanOpaque) s->opaque; - - if (op == RTOP_SPLIT) { - adjuststack(so->s_stack, blkno, offnum); - adjuststack(so->s_markstk, blkno, offnum); - } + RTreeScanOpaque so; + + adjustiptr(s, &(s->currentItemData), op, blkno, offnum); + adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); + + so = (RTreeScanOpaque) s->opaque; + + if (op == RTOP_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; - RTreeScanOpaque so; - - if (ItemPointerIsValid(iptr)) { - if (ItemPointerGetBlockNumber(iptr) == blkno) { - curoff = ItemPointerGetOffsetNumber(iptr); - so = (RTreeScanOpaque) s->opaque; - - switch (op) { - case RTOP_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 |= RTS_CURBEFORE; - else - so->s_flags |= RTS_MRKBEFORE; - } + OffsetNumber curoff; + RTreeScanOpaque so; + + if (ItemPointerIsValid(iptr)) + { + if (ItemPointerGetBlockNumber(iptr) == blkno) + { + curoff = ItemPointerGetOffsetNumber(iptr); + so = (RTreeScanOpaque) s->opaque; + + switch (op) + { + case RTOP_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 |= RTS_CURBEFORE; + else + so->s_flags |= RTS_MRKBEFORE; + } + } + break; + + case RTOP_SPLIT: + /* back to start of page on split */ + ItemPointerSet(iptr, blkno, FirstOffsetNumber); + if (iptr == &(s->currentItemData)) + so->s_flags &= ~RTS_CURBEFORE; + else + so->s_flags &= ~RTS_MRKBEFORE; + break; + + default: + elog(WARN, "Bad operation in rtree scan adjust: %d", op); + } } - break; - - case RTOP_SPLIT: - /* back to start of page on split */ - ItemPointerSet(iptr, blkno, FirstOffsetNumber); - if (iptr == &(s->currentItemData)) - so->s_flags &= ~RTS_CURBEFORE; - else - so->s_flags &= ~RTS_MRKBEFORE; - break; - - default: - elog(WARN, "Bad operation in rtree 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 rtrees 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 rtrees 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(RTSTACK *stk, - BlockNumber blkno, - OffsetNumber offnum) +adjuststack(RTSTACK * stk, + BlockNumber blkno, + OffsetNumber offnum) { - while (stk != (RTSTACK *) NULL) { - if (stk->rts_blk == blkno) - stk->rts_child = FirstOffsetNumber; - - stk = stk->rts_parent; - } + while (stk != (RTSTACK *) NULL) + { + if (stk->rts_blk == blkno) + stk->rts_child = FirstOffsetNumber; + + stk = stk->rts_parent; + } } diff --git a/src/backend/access/rtree/rtstrat.c b/src/backend/access/rtree/rtstrat.c index 7025a30999d..c71059d3f09 100644 --- a/src/backend/access/rtree/rtstrat.c +++ b/src/backend/access/rtree/rtstrat.c @@ -1,241 +1,243 @@ /*------------------------------------------------------------------------- * * rtstrat.c-- - * strategy map data for rtrees. + * strategy map data for rtrees. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.6 1997/08/19 21:29:52 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.7 1997/09/07 04:39:26 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> - + #include <utils/rel.h> #include <access/rtree.h> #include <access/istrat.h> -static StrategyNumber RelationGetRTStrategy(Relation r, - AttrNumber attnum, RegProcedure proc); +static StrategyNumber +RelationGetRTStrategy(Relation r, + AttrNumber attnum, RegProcedure proc); /* - * 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: * - * left, left-or-overlap, overlap, right-or-overlap, right, same, - * contains, contained-by + * left, left-or-overlap, overlap, right-or-overlap, right, same, + * 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 rtree indices have - * an operator in their operator class for negating <%. + * the planner can plan an index scan by noting that rtree 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 rtree 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 rtree 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 RTNegate[RTNStrategies] = { - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy - }; +static StrategyNumber RTNegate[RTNStrategies] = { + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy +}; /* if a op_1 b, what is the operator op_2 such that b op_2 a? */ -static StrategyNumber RTCommute[RTNStrategies] = { - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy - }; +static StrategyNumber RTCommute[RTNStrategies] = { + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy +}; /* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */ -static StrategyNumber RTNegateCommute[RTNStrategies] = { - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy, - InvalidStrategy - }; +static StrategyNumber RTNegateCommute[RTNStrategies] = { + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy, + InvalidStrategy +}; /* - * Now do the TermData arrays. These exist in case the user doesn't give - * us a full set of operators for a particular operator class. The idea - * is that by making multiple comparisons using any one of the supplied - * operators, we can decide whether two n-dimensional polygons are equal. - * For example, if a contains b and b contains a, we may conclude that - * a and b are equal. - * - * The presence of the TermData arrays in all this is a historical accident. - * Early in the development of the POSTGRES access methods, it was believed - * that writing functions was harder than writing arrays. This is wrong; - * TermData is hard to understand and hard to get right. In general, when - * someone populates a new operator class, the populate it completely. If - * Mike Hirohama had forced Cimarron Taylor to populate the strategy map - * for btree int2_ops completely in 1988, you wouldn't have to deal with - * all this now. Too bad for you. - * - * Since you can't necessarily do this in all cases (for example, you can't - * do it given only "intersects" or "disjoint"), TermData arrays for some - * operators don't appear below. - * - * Note that if you DO supply all the operators required in a given opclass - * by inserting them into the pg_opclass system catalog, you can get away - * without doing all this TermData stuff. Since the rtree code is intended - * to be a reference for access method implementors, I'm doing TermData - * correctly here. - * - * Note on style: these are all actually of type StrategyTermData, but - * since those have variable-length data at the end of the struct we can't - * properly initialize them if we declare them to be what they are. + * Now do the TermData arrays. These exist in case the user doesn't give + * us a full set of operators for a particular operator class. The idea + * is that by making multiple comparisons using any one of the supplied + * operators, we can decide whether two n-dimensional polygons are equal. + * For example, if a contains b and b contains a, we may conclude that + * a and b are equal. + * + * The presence of the TermData arrays in all this is a historical accident. + * Early in the development of the POSTGRES access methods, it was believed + * that writing functions was harder than writing arrays. This is wrong; + * TermData is hard to understand and hard to get right. In general, when + * someone populates a new operator class, the populate it completely. If + * Mike Hirohama had forced Cimarron Taylor to populate the strategy map + * for btree int2_ops completely in 1988, you wouldn't have to deal with + * all this now. Too bad for you. + * + * Since you can't necessarily do this in all cases (for example, you can't + * do it given only "intersects" or "disjoint"), TermData arrays for some + * operators don't appear below. + * + * Note that if you DO supply all the operators required in a given opclass + * by inserting them into the pg_opclass system catalog, you can get away + * without doing all this TermData stuff. Since the rtree code is intended + * to be a reference for access method implementors, I'm doing TermData + * correctly here. + * + * Note on style: these are all actually of type StrategyTermData, but + * since those have variable-length data at the end of the struct we can't + * properly initialize them if we declare them to be what they are. */ /* if you only have "contained-by", how do you determine equality? */ -static uint16 RTContainedByTermData[] = { - 2, /* make two comparisons */ - RTContainedByStrategyNumber, /* use "a contained-by b" */ - 0x0, /* without any magic */ - RTContainedByStrategyNumber, /* then use contained-by, */ - SK_COMMUTE /* swapping a and b */ - }; +static uint16 RTContainedByTermData[] = { + 2, /* make two comparisons */ + RTContainedByStrategyNumber,/* use "a contained-by b" */ + 0x0, /* without any magic */ + RTContainedByStrategyNumber,/* then use contained-by, */ + SK_COMMUTE /* swapping a and b */ +}; /* if you only have "contains", how do you determine equality? */ -static uint16 RTContainsTermData[] = { - 2, /* make two comparisons */ - RTContainsStrategyNumber, /* use "a contains b" */ - 0x0, /* without any magic */ - RTContainsStrategyNumber, /* then use contains again, */ - SK_COMMUTE /* swapping a and b */ - }; +static uint16 RTContainsTermData[] = { + 2, /* make two comparisons */ + RTContainsStrategyNumber, /* use "a contains b" */ + 0x0, /* without any magic */ + RTContainsStrategyNumber, /* then use contains again, */ + SK_COMMUTE /* swapping a and b */ +}; /* now put all that together in one place for the planner */ static StrategyTerm RTEqualExpressionData[] = { - (StrategyTerm) RTContainedByTermData, - (StrategyTerm) RTContainsTermData, - NULL - }; + (StrategyTerm) RTContainedByTermData, + (StrategyTerm) RTContainsTermData, + NULL +}; /* - * If you were sufficiently attentive to detail, you would go through - * the ExpressionData pain above for every one of the seven 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. + * If you were sufficiently attentive to detail, you would go through + * the ExpressionData pain above for every one of the seven 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. */ static StrategyEvaluationData RTEvaluationData = { - RTNStrategies, /* # of strategies */ - (StrategyTransformMap) RTNegate, /* how to do (not qual) */ - (StrategyTransformMap) RTCommute, /* how to swap operands */ - (StrategyTransformMap) RTNegateCommute, /* how to do both */ - { - NULL, /* express left */ - NULL, /* express overleft */ - NULL, /* express over */ - NULL, /* express overright */ - NULL, /* express right */ - (StrategyExpression) RTEqualExpressionData, /* express same */ - NULL, /* express contains */ - NULL, /* express contained-by */ - NULL, - NULL, - NULL - } + RTNStrategies, /* # of strategies */ + (StrategyTransformMap) RTNegate, /* how to do (not qual) */ + (StrategyTransformMap) RTCommute, /* how to swap operands */ + (StrategyTransformMap) RTNegateCommute, /* how to do both */ + { + NULL, /* express left */ + NULL, /* express overleft */ + NULL, /* express over */ + NULL, /* express overright */ + NULL, /* express right */ + (StrategyExpression) RTEqualExpressionData, /* express same */ + NULL, /* express contains */ + NULL, /* express contained-by */ + NULL, + NULL, + NULL + } }; /* - * Okay, now something peculiar to rtrees that doesn't apply to most other - * indexing structures: When we're searching a tree for a given value, we - * can't do the same sorts of comparisons on internal node entries as we - * do at leaves. The reason is that if we're looking for (say) all boxes - * that are the same as (0,0,10,10), then we need to find all leaf pages - * that overlap that region. So internally we search for overlap, and at - * the leaf we search for equality. - * - * This array maps leaf search operators to the internal search operators. - * We assume the normal ordering on operators: - * - * left, left-or-overlap, overlap, right-or-overlap, right, same, - * contains, contained-by + * Okay, now something peculiar to rtrees that doesn't apply to most other + * indexing structures: When we're searching a tree for a given value, we + * can't do the same sorts of comparisons on internal node entries as we + * do at leaves. The reason is that if we're looking for (say) all boxes + * that are the same as (0,0,10,10), then we need to find all leaf pages + * that overlap that region. So internally we search for overlap, and at + * the leaf we search for equality. + * + * This array maps leaf search operators to the internal search operators. + * We assume the normal ordering on operators: + * + * left, left-or-overlap, overlap, right-or-overlap, right, same, + * contains, contained-by */ static StrategyNumber RTOperMap[RTNStrategies] = { - RTOverLeftStrategyNumber, - RTOverLeftStrategyNumber, - RTOverlapStrategyNumber, - RTOverRightStrategyNumber, - RTOverRightStrategyNumber, - RTContainsStrategyNumber, - RTContainsStrategyNumber, - RTOverlapStrategyNumber - }; + RTOverLeftStrategyNumber, + RTOverLeftStrategyNumber, + RTOverlapStrategyNumber, + RTOverRightStrategyNumber, + RTOverRightStrategyNumber, + RTContainsStrategyNumber, + RTContainsStrategyNumber, + RTOverlapStrategyNumber +}; -static StrategyNumber +static StrategyNumber RelationGetRTStrategy(Relation r, - AttrNumber attnum, - RegProcedure proc) + AttrNumber attnum, + RegProcedure proc) { - return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc)); + return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc)); } #ifdef NOT_USED bool RelationInvokeRTStrategy(Relation r, - AttrNumber attnum, - StrategyNumber s, - Datum left, - Datum right) + AttrNumber attnum, + StrategyNumber s, + Datum left, + Datum right) { - return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s, - left, right)); + return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s, + left, right)); } + #endif RegProcedure RTMapOperator(Relation r, - AttrNumber attnum, - RegProcedure proc) + AttrNumber attnum, + RegProcedure proc) { - StrategyNumber procstrat; - StrategyMap strategyMap; - - procstrat = RelationGetRTStrategy(r, attnum, proc); - strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r), - RTNStrategies, - attnum); - - return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure); + StrategyNumber procstrat; + StrategyMap strategyMap; + + procstrat = RelationGetRTStrategy(r, attnum, proc); + strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r), + RTNStrategies, + attnum); + + return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure); } diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c index 9087e50bc40..6d721fe96af 100644 --- a/src/backend/access/transam/transam.c +++ b/src/backend/access/transam/transam.c @@ -1,18 +1,18 @@ /*------------------------------------------------------------------------- * * transam.c-- - * postgres transaction log/time interface routines + * postgres transaction log/time interface routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.9 1997/08/19 21:29:59 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.10 1997/09/07 04:39:29 momjian Exp $ * * NOTES - * This file contains the high level access-method interface to the - * transaction system. - * + * This file contains the high level access-method interface to the + * transaction system. + * *------------------------------------------------------------------------- */ @@ -26,659 +26,671 @@ #include <storage/spin.h> #include <commands/vacuum.h> -static int RecoveryCheckingEnabled(void); -static void TransRecover(Relation logRelation); -static bool TransactionLogTest(TransactionId transactionId, XidStatus status); -static void TransactionLogUpdate(TransactionId transactionId, - XidStatus status); +static int RecoveryCheckingEnabled(void); +static void TransRecover(Relation logRelation); +static bool TransactionLogTest(TransactionId transactionId, XidStatus status); +static void +TransactionLogUpdate(TransactionId transactionId, + XidStatus status); /* ---------------- - * global variables holding pointers to relations used - * by the transaction system. These are initialized by - * InitializeTransactionLog(). + * global variables holding pointers to relations used + * by the transaction system. These are initialized by + * InitializeTransactionLog(). * ---------------- */ -Relation LogRelation = (Relation) NULL; -Relation TimeRelation = (Relation) NULL; -Relation VariableRelation = (Relation) NULL; +Relation LogRelation = (Relation) NULL; +Relation TimeRelation = (Relation) NULL; +Relation VariableRelation = (Relation) NULL; /* ---------------- - * global variables holding cached transaction id's and statuses. + * global variables holding cached transaction id's and statuses. * ---------------- */ TransactionId cachedGetCommitTimeXid; AbsoluteTime cachedGetCommitTime; TransactionId cachedTestXid; -XidStatus cachedTestXidStatus; +XidStatus cachedTestXidStatus; /* ---------------- - * transaction system constants + * transaction system constants * ---------------- */ /* ---------------------------------------------------------------- - * transaction system constants + * transaction system constants * - * read the comments for GetNewTransactionId in order to - * understand the initial values for AmiTransactionId and - * FirstTransactionId. -cim 3/23/90 + * read the comments for GetNewTransactionId in order to + * understand the initial values for AmiTransactionId and + * FirstTransactionId. -cim 3/23/90 * ---------------------------------------------------------------- */ -TransactionId NullTransactionId = (TransactionId) 0; +TransactionId NullTransactionId = (TransactionId) 0; -TransactionId AmiTransactionId = (TransactionId) 512; +TransactionId AmiTransactionId = (TransactionId) 512; -TransactionId FirstTransactionId = (TransactionId) 514; +TransactionId FirstTransactionId = (TransactionId) 514; /* ---------------- - * transaction recovery state variables - * - * When the transaction system is initialized, we may - * need to do recovery checking. This decision is decided - * by the postmaster or the user by supplying the backend - * with a special flag. In general, we want to do recovery - * checking whenever we are running without a postmaster - * or when the number of backends running under the postmaster - * goes from zero to one. -cim 3/21/90 + * transaction recovery state variables + * + * When the transaction system is initialized, we may + * need to do recovery checking. This decision is decided + * by the postmaster or the user by supplying the backend + * with a special flag. In general, we want to do recovery + * checking whenever we are running without a postmaster + * or when the number of backends running under the postmaster + * goes from zero to one. -cim 3/21/90 * ---------------- */ -int RecoveryCheckingEnableState = 0; +int RecoveryCheckingEnableState = 0; /* ------------------ - * spinlock for oid generation + * spinlock for oid generation * ----------------- */ -extern int OidGenLockId; +extern int OidGenLockId; /* ---------------- - * globals that must be reset at abort + * globals that must be reset at abort * ---------------- */ -extern bool BuildingBtree; +extern bool BuildingBtree; /* ---------------- - * recovery checking accessors + * recovery checking accessors * ---------------- */ static int RecoveryCheckingEnabled(void) -{ - return RecoveryCheckingEnableState; +{ + return RecoveryCheckingEnableState; } #ifdef NOT_USED static void SetRecoveryCheckingEnabled(bool state) -{ - RecoveryCheckingEnableState = (state == true); +{ + RecoveryCheckingEnableState = (state == true); } + #endif /* ---------------------------------------------------------------- - * postgres log/time access method interface - * - * TransactionLogTest - * TransactionLogUpdate - * ======== - * these functions do work for the interface - * functions - they search/retrieve and append/update - * information in the log and time relations. + * postgres log/time access method interface + * + * TransactionLogTest + * TransactionLogUpdate + * ======== + * these functions do work for the interface + * functions - they search/retrieve and append/update + * information in the log and time relations. * ---------------------------------------------------------------- */ /* -------------------------------- - * TransactionLogTest + * TransactionLogTest * -------------------------------- */ -static bool /* true/false: does transaction id have specified status? */ -TransactionLogTest(TransactionId transactionId, /* transaction id to test */ - XidStatus status) /* transaction status */ +static bool /* true/false: does transaction id have + * specified status? */ +TransactionLogTest(TransactionId transactionId, /* transaction id to test */ + XidStatus status) /* transaction status */ { - BlockNumber blockNumber; - XidStatus xidstatus; /* recorded status of xid */ - bool fail = false; /* success/failure */ - - /* ---------------- - * during initialization consider all transactions - * as having been committed - * ---------------- - */ - if (! RelationIsValid(LogRelation)) - return (bool) (status == XID_COMMIT); - - /* ---------------- - * before going to the buffer manager, check our single - * item cache to see if we didn't just check the transaction - * status a moment ago. - * ---------------- - */ - if (TransactionIdEquals(transactionId, cachedTestXid)) - return (bool) - (status == cachedTestXidStatus); - - /* ---------------- - * compute the item pointer corresponding to the - * page containing our transaction id. We save the item in - * our cache to speed up things if we happen to ask for the - * same xid's status more than once. - * ---------------- - */ - TransComputeBlockNumber(LogRelation, transactionId, &blockNumber); - xidstatus = TransBlockNumberGetXidStatus(LogRelation, - blockNumber, - transactionId, - &fail); - - if (! fail) { - TransactionIdStore(transactionId, &cachedTestXid); - cachedTestXidStatus = xidstatus; - return (bool) - (status == xidstatus); - } - - /* ---------------- - * here the block didn't contain the information we wanted - * ---------------- - */ - elog(WARN, "TransactionLogTest: failed to get xidstatus"); - - /* - * so lint is happy... - */ - return(false); + BlockNumber blockNumber; + XidStatus xidstatus; /* recorded status of xid */ + bool fail = false; /* success/failure */ + + /* ---------------- + * during initialization consider all transactions + * as having been committed + * ---------------- + */ + if (!RelationIsValid(LogRelation)) + return (bool) (status == XID_COMMIT); + + /* ---------------- + * before going to the buffer manager, check our single + * item cache to see if we didn't just check the transaction + * status a moment ago. + * ---------------- + */ + if (TransactionIdEquals(transactionId, cachedTestXid)) + return (bool) + (status == cachedTestXidStatus); + + /* ---------------- + * compute the item pointer corresponding to the + * page containing our transaction id. We save the item in + * our cache to speed up things if we happen to ask for the + * same xid's status more than once. + * ---------------- + */ + TransComputeBlockNumber(LogRelation, transactionId, &blockNumber); + xidstatus = TransBlockNumberGetXidStatus(LogRelation, + blockNumber, + transactionId, + &fail); + + if (!fail) + { + TransactionIdStore(transactionId, &cachedTestXid); + cachedTestXidStatus = xidstatus; + return (bool) + (status == xidstatus); + } + + /* ---------------- + * here the block didn't contain the information we wanted + * ---------------- + */ + elog(WARN, "TransactionLogTest: failed to get xidstatus"); + + /* + * so lint is happy... + */ + return (false); } /* -------------------------------- - * TransactionLogUpdate + * TransactionLogUpdate * -------------------------------- */ static void -TransactionLogUpdate(TransactionId transactionId, /* trans id to update */ - XidStatus status) /* new trans status */ +TransactionLogUpdate(TransactionId transactionId, /* trans id to update */ + XidStatus status) /* new trans status */ { - BlockNumber blockNumber; - bool fail = false; /* success/failure */ - AbsoluteTime currentTime; /* time of this transaction */ - - /* ---------------- - * during initialization we don't record any updates. - * ---------------- - */ - if (! RelationIsValid(LogRelation)) - return; - - /* ---------------- - * get the transaction commit time - * ---------------- - */ - currentTime = getSystemTime(); - - /* ---------------- - * update the log relation - * ---------------- - */ - TransComputeBlockNumber(LogRelation, transactionId, &blockNumber); - TransBlockNumberSetXidStatus(LogRelation, - blockNumber, - transactionId, - status, - &fail); - - /* ---------------- - * update (invalidate) our single item TransactionLogTest cache. - * ---------------- - */ - TransactionIdStore(transactionId, &cachedTestXid); - cachedTestXidStatus = status; - - /* ---------------- - * now we update the time relation, if necessary - * (we only record commit times) - * ---------------- - */ - if (RelationIsValid(TimeRelation) && status == XID_COMMIT) { - TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber); - TransBlockNumberSetCommitTime(TimeRelation, - blockNumber, - transactionId, - currentTime, - &fail); + BlockNumber blockNumber; + bool fail = false; /* success/failure */ + AbsoluteTime currentTime;/* time of this transaction */ + + /* ---------------- + * during initialization we don't record any updates. + * ---------------- + */ + if (!RelationIsValid(LogRelation)) + return; + /* ---------------- - * update (invalidate) our single item GetCommitTime cache. + * get the transaction commit time * ---------------- */ - TransactionIdStore(transactionId, &cachedGetCommitTimeXid); - cachedGetCommitTime = currentTime; - } - - /* ---------------- - * now we update the "last committed transaction" field - * in the variable relation if we are recording a commit. - * ---------------- - */ - if (RelationIsValid(VariableRelation) && status == XID_COMMIT) - UpdateLastCommittedXid(transactionId); + currentTime = getSystemTime(); + + /* ---------------- + * update the log relation + * ---------------- + */ + TransComputeBlockNumber(LogRelation, transactionId, &blockNumber); + TransBlockNumberSetXidStatus(LogRelation, + blockNumber, + transactionId, + status, + &fail); + + /* ---------------- + * update (invalidate) our single item TransactionLogTest cache. + * ---------------- + */ + TransactionIdStore(transactionId, &cachedTestXid); + cachedTestXidStatus = status; + + /* ---------------- + * now we update the time relation, if necessary + * (we only record commit times) + * ---------------- + */ + if (RelationIsValid(TimeRelation) && status == XID_COMMIT) + { + TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber); + TransBlockNumberSetCommitTime(TimeRelation, + blockNumber, + transactionId, + currentTime, + &fail); + /* ---------------- + * update (invalidate) our single item GetCommitTime cache. + * ---------------- + */ + TransactionIdStore(transactionId, &cachedGetCommitTimeXid); + cachedGetCommitTime = currentTime; + } + + /* ---------------- + * now we update the "last committed transaction" field + * in the variable relation if we are recording a commit. + * ---------------- + */ + if (RelationIsValid(VariableRelation) && status == XID_COMMIT) + UpdateLastCommittedXid(transactionId); } /* -------------------------------- - * TransactionIdGetCommitTime + * TransactionIdGetCommitTime * -------------------------------- */ -AbsoluteTime /* commit time of transaction id */ -TransactionIdGetCommitTime(TransactionId transactionId) /* transaction id to test */ +AbsoluteTime /* commit time of transaction id */ +TransactionIdGetCommitTime(TransactionId transactionId) /* transaction id to + * test */ { - BlockNumber blockNumber; - AbsoluteTime commitTime; /* commit time */ - bool fail = false; /* success/failure */ - - /* ---------------- - * return invalid if we aren't running yet... - * ---------------- - */ - if (! RelationIsValid(TimeRelation)) - return INVALID_ABSTIME; - - /* ---------------- - * before going to the buffer manager, check our single - * item cache to see if we didn't just get the commit time - * a moment ago. - * ---------------- - */ - if (TransactionIdEquals(transactionId, cachedGetCommitTimeXid)) - return cachedGetCommitTime; - - /* ---------------- - * compute the item pointer corresponding to the - * page containing our transaction commit time - * ---------------- - */ - TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber); - commitTime = TransBlockNumberGetCommitTime(TimeRelation, - blockNumber, - transactionId, - &fail); - - /* ---------------- - * update our cache and return the transaction commit time - * ---------------- - */ - if (! fail) { - TransactionIdStore(transactionId, &cachedGetCommitTimeXid); - cachedGetCommitTime = commitTime; - return commitTime; - } else - return INVALID_ABSTIME; + BlockNumber blockNumber; + AbsoluteTime commitTime; /* commit time */ + bool fail = false; /* success/failure */ + + /* ---------------- + * return invalid if we aren't running yet... + * ---------------- + */ + if (!RelationIsValid(TimeRelation)) + return INVALID_ABSTIME; + + /* ---------------- + * before going to the buffer manager, check our single + * item cache to see if we didn't just get the commit time + * a moment ago. + * ---------------- + */ + if (TransactionIdEquals(transactionId, cachedGetCommitTimeXid)) + return cachedGetCommitTime; + + /* ---------------- + * compute the item pointer corresponding to the + * page containing our transaction commit time + * ---------------- + */ + TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber); + commitTime = TransBlockNumberGetCommitTime(TimeRelation, + blockNumber, + transactionId, + &fail); + + /* ---------------- + * update our cache and return the transaction commit time + * ---------------- + */ + if (!fail) + { + TransactionIdStore(transactionId, &cachedGetCommitTimeXid); + cachedGetCommitTime = commitTime; + return commitTime; + } + else + return INVALID_ABSTIME; } /* ---------------------------------------------------------------- - * transaction recovery code + * transaction recovery code * ---------------------------------------------------------------- */ /* -------------------------------- - * TransRecover + * TransRecover * - * preform transaction recovery checking. + * preform transaction recovery checking. * - * Note: this should only be preformed if no other backends - * are running. This is known by the postmaster and - * conveyed by the postmaster passing a "do recovery checking" - * flag to the backend. + * Note: this should only be preformed if no other backends + * are running. This is known by the postmaster and + * conveyed by the postmaster passing a "do recovery checking" + * flag to the backend. * - * here we get the last recorded transaction from the log, - * get the "last" and "next" transactions from the variable relation - * and then preform some integrity tests: + * here we get the last recorded transaction from the log, + * get the "last" and "next" transactions from the variable relation + * and then preform some integrity tests: * - * 1) No transaction may exist higher then the "next" available - * transaction recorded in the variable relation. If this is the - * case then it means either the log or the variable relation - * has become corrupted. + * 1) No transaction may exist higher then the "next" available + * transaction recorded in the variable relation. If this is the + * case then it means either the log or the variable relation + * has become corrupted. * - * 2) The last committed transaction may not be higher then the - * next available transaction for the same reason. + * 2) The last committed transaction may not be higher then the + * next available transaction for the same reason. * - * 3) The last recorded transaction may not be lower then the - * last committed transaction. (the reverse is ok - it means - * that some transactions have aborted since the last commit) + * 3) The last recorded transaction may not be lower then the + * last committed transaction. (the reverse is ok - it means + * that some transactions have aborted since the last commit) * - * Here is what the proper situation looks like. The line - * represents the data stored in the log. 'c' indicates the - * transaction was recorded as committed, 'a' indicates an - * abortted transaction and '.' represents information not - * recorded. These may correspond to in progress transactions. + * Here is what the proper situation looks like. The line + * represents the data stored in the log. 'c' indicates the + * transaction was recorded as committed, 'a' indicates an + * abortted transaction and '.' represents information not + * recorded. These may correspond to in progress transactions. * - * c c a c . . a . . . . . . . . . . - * | | - * last next + * c c a c . . a . . . . . . . . . . + * | | + * last next * - * Since "next" is only incremented by GetNewTransactionId() which - * is called when transactions are started. Hence if there - * are commits or aborts after "next", then it means we committed - * or aborted BEFORE we started the transaction. This is the - * rational behind constraint (1). + * Since "next" is only incremented by GetNewTransactionId() which + * is called when transactions are started. Hence if there + * are commits or aborts after "next", then it means we committed + * or aborted BEFORE we started the transaction. This is the + * rational behind constraint (1). * - * Likewise, "last" should never greater then "next" for essentially - * the same reason - it would imply we committed before we started. - * This is the reasoning for (2). + * Likewise, "last" should never greater then "next" for essentially + * the same reason - it would imply we committed before we started. + * This is the reasoning for (2). * - * (3) implies we may never have a situation such as: + * (3) implies we may never have a situation such as: * - * c c a c . . a c . . . . . . . . . - * | | - * last next + * c c a c . . a c . . . . . . . . . + * | | + * last next * - * where there is a 'c' greater then "last". + * where there is a 'c' greater then "last". * - * Recovery checking is more difficult in the case where - * several backends are executing concurrently because the - * transactions may be executing in the other backends. - * So, we only do recovery stuff when the backend is explicitly - * passed a flag on the command line. + * Recovery checking is more difficult in the case where + * several backends are executing concurrently because the + * transactions may be executing in the other backends. + * So, we only do recovery stuff when the backend is explicitly + * passed a flag on the command line. * -------------------------------- */ static void TransRecover(Relation logRelation) { -#if 0 - /* ---------------- - * first get the last recorded transaction in the log. - * ---------------- - */ - TransGetLastRecordedTransaction(logRelation, logLastXid, &fail); - if (fail == true) - elog(WARN, "TransRecover: failed TransGetLastRecordedTransaction"); - - /* ---------------- - * next get the "last" and "next" variables - * ---------------- - */ - VariableRelationGetLastXid(&varLastXid); - VariableRelationGetNextXid(&varNextXid); - - /* ---------------- - * intregity test (1) - * ---------------- - */ - if (TransactionIdIsLessThan(varNextXid, logLastXid)) - elog(WARN, "TransRecover: varNextXid < logLastXid"); - - /* ---------------- - * intregity test (2) - * ---------------- - */ - - /* ---------------- - * intregity test (3) - * ---------------- - */ - - /* ---------------- - * here we have a valid " - * - * **** RESUME HERE **** - * ---------------- - */ - varNextXid = TransactionIdDup(varLastXid); - TransactionIdIncrement(&varNextXid); - - VarPut(var, VAR_PUT_LASTXID, varLastXid); - VarPut(var, VAR_PUT_NEXTXID, varNextXid); +#if 0 + /* ---------------- + * first get the last recorded transaction in the log. + * ---------------- + */ + TransGetLastRecordedTransaction(logRelation, logLastXid, &fail); + if (fail == true) + elog(WARN, "TransRecover: failed TransGetLastRecordedTransaction"); + + /* ---------------- + * next get the "last" and "next" variables + * ---------------- + */ + VariableRelationGetLastXid(&varLastXid); + VariableRelationGetNextXid(&varNextXid); + + /* ---------------- + * intregity test (1) + * ---------------- + */ + if (TransactionIdIsLessThan(varNextXid, logLastXid)) + elog(WARN, "TransRecover: varNextXid < logLastXid"); + + /* ---------------- + * intregity test (2) + * ---------------- + */ + + /* ---------------- + * intregity test (3) + * ---------------- + */ + + /* ---------------- + * here we have a valid " + * + * **** RESUME HERE **** + * ---------------- + */ + varNextXid = TransactionIdDup(varLastXid); + TransactionIdIncrement(&varNextXid); + + VarPut(var, VAR_PUT_LASTXID, varLastXid); + VarPut(var, VAR_PUT_NEXTXID, varNextXid); #endif } /* ---------------------------------------------------------------- - * Interface functions - * - * InitializeTransactionLog - * ======== - * this function (called near cinit) initializes - * the transaction log, time and variable relations. - * - * TransactionId DidCommit - * TransactionId DidAbort - * TransactionId IsInProgress - * ======== - * these functions test the transaction status of - * a specified transaction id. - * - * TransactionId Commit - * TransactionId Abort - * TransactionId SetInProgress - * ======== - * these functions set the transaction status - * of the specified xid. TransactionIdCommit() also - * records the current time in the time relation - * and updates the variable relation counter. + * Interface functions + * + * InitializeTransactionLog + * ======== + * this function (called near cinit) initializes + * the transaction log, time and variable relations. + * + * TransactionId DidCommit + * TransactionId DidAbort + * TransactionId IsInProgress + * ======== + * these functions test the transaction status of + * a specified transaction id. + * + * TransactionId Commit + * TransactionId Abort + * TransactionId SetInProgress + * ======== + * these functions set the transaction status + * of the specified xid. TransactionIdCommit() also + * records the current time in the time relation + * and updates the variable relation counter. * * ---------------------------------------------------------------- */ /* * InitializeTransactionLog -- - * Initializes transaction logging. + * Initializes transaction logging. */ void InitializeTransactionLog(void) { - Relation logRelation; - Relation timeRelation; - MemoryContext oldContext; - - /* ---------------- - * don't do anything during bootstrapping - * ---------------- - */ - if (AMI_OVERRIDE) - return; - - /* ---------------- - * disable the transaction system so the access methods - * don't interfere during initialization. - * ---------------- - */ - OverrideTransactionSystem(true); - - /* ---------------- - * make sure allocations occur within the top memory context - * so that our log management structures are protected from - * garbage collection at the end of every transaction. - * ---------------- - */ - oldContext = MemoryContextSwitchTo(TopMemoryContext); - - /* ---------------- - * first open the log and time relations - * (these are created by amiint so they are guaranteed to exist) - * ---------------- - */ - logRelation = heap_openr(LogRelationName); - timeRelation = heap_openr(TimeRelationName); - VariableRelation = heap_openr(VariableRelationName); - /* ---------------- - * XXX TransactionLogUpdate requires that LogRelation - * and TimeRelation are valid so we temporarily set - * them so we can initialize things properly. - * This could be done cleaner. - * ---------------- - */ - LogRelation = logRelation; - TimeRelation = timeRelation; - - /* ---------------- - * if we have a virgin database, we initialize the log and time - * relation by committing the AmiTransactionId (id 512) and we - * initialize the variable relation by setting the next available - * transaction id to FirstTransactionId (id 514). OID initialization - * happens as a side effect of bootstrapping in varsup.c. - * ---------------- - */ - SpinAcquire(OidGenLockId); - if (!TransactionIdDidCommit(AmiTransactionId)) { - + Relation logRelation; + Relation timeRelation; + MemoryContext oldContext; + + /* ---------------- + * don't do anything during bootstrapping + * ---------------- + */ + if (AMI_OVERRIDE) + return; + + /* ---------------- + * disable the transaction system so the access methods + * don't interfere during initialization. + * ---------------- + */ + OverrideTransactionSystem(true); + /* ---------------- - * SOMEDAY initialize the information stored in - * the headers of the log/time/variable relations. + * make sure allocations occur within the top memory context + * so that our log management structures are protected from + * garbage collection at the end of every transaction. * ---------------- */ - TransactionLogUpdate(AmiTransactionId, XID_COMMIT); - VariableRelationPutNextXid(FirstTransactionId); - - } else if (RecoveryCheckingEnabled()) { + oldContext = MemoryContextSwitchTo(TopMemoryContext); + /* ---------------- - * if we have a pre-initialized database and if the - * perform recovery checking flag was passed then we - * do our database integrity checking. + * first open the log and time relations + * (these are created by amiint so they are guaranteed to exist) * ---------------- */ - TransRecover(logRelation); - } - LogRelation = (Relation) NULL; - TimeRelation = (Relation) NULL; - SpinRelease(OidGenLockId); - - /* ---------------- - * now re-enable the transaction system - * ---------------- - */ - OverrideTransactionSystem(false); - - /* ---------------- - * instantiate the global variables - * ---------------- - */ - LogRelation = logRelation; - TimeRelation = timeRelation; - - /* ---------------- - * restore the memory context to the previous context - * before we return from initialization. - * ---------------- - */ - MemoryContextSwitchTo(oldContext); + logRelation = heap_openr(LogRelationName); + timeRelation = heap_openr(TimeRelationName); + VariableRelation = heap_openr(VariableRelationName); + /* ---------------- + * XXX TransactionLogUpdate requires that LogRelation + * and TimeRelation are valid so we temporarily set + * them so we can initialize things properly. + * This could be done cleaner. + * ---------------- + */ + LogRelation = logRelation; + TimeRelation = timeRelation; + + /* ---------------- + * if we have a virgin database, we initialize the log and time + * relation by committing the AmiTransactionId (id 512) and we + * initialize the variable relation by setting the next available + * transaction id to FirstTransactionId (id 514). OID initialization + * happens as a side effect of bootstrapping in varsup.c. + * ---------------- + */ + SpinAcquire(OidGenLockId); + if (!TransactionIdDidCommit(AmiTransactionId)) + { + + /* ---------------- + * SOMEDAY initialize the information stored in + * the headers of the log/time/variable relations. + * ---------------- + */ + TransactionLogUpdate(AmiTransactionId, XID_COMMIT); + VariableRelationPutNextXid(FirstTransactionId); + + } + else if (RecoveryCheckingEnabled()) + { + /* ---------------- + * if we have a pre-initialized database and if the + * perform recovery checking flag was passed then we + * do our database integrity checking. + * ---------------- + */ + TransRecover(logRelation); + } + LogRelation = (Relation) NULL; + TimeRelation = (Relation) NULL; + SpinRelease(OidGenLockId); + + /* ---------------- + * now re-enable the transaction system + * ---------------- + */ + OverrideTransactionSystem(false); + + /* ---------------- + * instantiate the global variables + * ---------------- + */ + LogRelation = logRelation; + TimeRelation = timeRelation; + + /* ---------------- + * restore the memory context to the previous context + * before we return from initialization. + * ---------------- + */ + MemoryContextSwitchTo(oldContext); } /* -------------------------------- - * TransactionId DidCommit - * TransactionId DidAbort - * TransactionId IsInProgress + * TransactionId DidCommit + * TransactionId DidAbort + * TransactionId IsInProgress * -------------------------------- */ /* * TransactionIdDidCommit -- - * True iff transaction associated with the identifier did commit. + * True iff transaction associated with the identifier did commit. * * Note: - * Assumes transaction identifier is valid. + * Assumes transaction identifier is valid. */ -bool /* true if given transaction committed */ +bool /* true if given transaction committed */ TransactionIdDidCommit(TransactionId transactionId) { - if (AMI_OVERRIDE) - return true; - - return - TransactionLogTest(transactionId, XID_COMMIT); + if (AMI_OVERRIDE) + return true; + + return + TransactionLogTest(transactionId, XID_COMMIT); } /* * TransactionIdDidAborted -- - * True iff transaction associated with the identifier did abort. + * True iff transaction associated with the identifier did abort. * * Note: - * Assumes transaction identifier is valid. - * XXX Is this unneeded? + * Assumes transaction identifier is valid. + * XXX Is this unneeded? */ -bool /* true if given transaction aborted */ +bool /* true if given transaction aborted */ TransactionIdDidAbort(TransactionId transactionId) { - if (AMI_OVERRIDE) - return false; - - return - TransactionLogTest(transactionId, XID_ABORT); + if (AMI_OVERRIDE) + return false; + + return + TransactionLogTest(transactionId, XID_ABORT); } -/* +/* * Now this func in shmem.c and gives quality answer by scanning * PROC structures of all running backend. - vadim 11/26/96 * * Old comments: - * true if given transaction neither committed nor aborted - + * true if given transaction neither committed nor aborted + bool TransactionIdIsInProgress(TransactionId transactionId) { - if (AMI_OVERRIDE) - return false; - - return - TransactionLogTest(transactionId, XID_INPROGRESS); + if (AMI_OVERRIDE) + return false; + + return + TransactionLogTest(transactionId, XID_INPROGRESS); } */ /* -------------------------------- - * TransactionId Commit - * TransactionId Abort - * TransactionId SetInProgress + * TransactionId Commit + * TransactionId Abort + * TransactionId SetInProgress * -------------------------------- */ /* * TransactionIdCommit -- - * Commits the transaction associated with the identifier. + * Commits the transaction associated with the identifier. * * Note: - * Assumes transaction identifier is valid. + * Assumes transaction identifier is valid. */ void TransactionIdCommit(TransactionId transactionId) { - if (AMI_OVERRIDE) - return; - - /* - * Within TransactionLogUpdate we call UpdateLastCommited() - * which assumes we have exclusive access to pg_variable. - * Therefore we need to get exclusive access before calling - * TransactionLogUpdate. -mer 18 Aug 1992 - */ - SpinAcquire(OidGenLockId); - TransactionLogUpdate(transactionId, XID_COMMIT); - SpinRelease(OidGenLockId); + if (AMI_OVERRIDE) + return; + + /* + * Within TransactionLogUpdate we call UpdateLastCommited() which + * assumes we have exclusive access to pg_variable. Therefore we need + * to get exclusive access before calling TransactionLogUpdate. -mer + * 18 Aug 1992 + */ + SpinAcquire(OidGenLockId); + TransactionLogUpdate(transactionId, XID_COMMIT); + SpinRelease(OidGenLockId); } /* * TransactionIdAbort -- - * Aborts the transaction associated with the identifier. + * Aborts the transaction associated with the identifier. * * Note: - * Assumes transaction identifier is valid. + * Assumes transaction identifier is valid. */ void TransactionIdAbort(TransactionId transactionId) { - BuildingBtree = false; - - if (VacuumRunning) - vc_abort(); - - if (AMI_OVERRIDE) - return; - - TransactionLogUpdate(transactionId, XID_ABORT); + BuildingBtree = false; + + if (VacuumRunning) + vc_abort(); + + if (AMI_OVERRIDE) + return; + + TransactionLogUpdate(transactionId, XID_ABORT); } #ifdef NOT_USED void TransactionIdSetInProgress(TransactionId transactionId) { - if (AMI_OVERRIDE) - return; - - TransactionLogUpdate(transactionId, XID_INPROGRESS); + if (AMI_OVERRIDE) + return; + + TransactionLogUpdate(transactionId, XID_INPROGRESS); } + #endif diff --git a/src/backend/access/transam/transsup.c b/src/backend/access/transam/transsup.c index c3f1d4fc9fc..9809190c942 100644 --- a/src/backend/access/transam/transsup.c +++ b/src/backend/access/transam/transsup.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * transsup.c-- - * postgres transaction access method support code + * postgres transaction access method support code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/transsup.c,v 1.9 1997/08/19 21:30:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/transsup.c,v 1.10 1997/09/07 04:39:32 momjian Exp $ * * NOTES - * This file contains support functions for the high - * level access method interface routines found in transam.c + * This file contains support functions for the high + * level access method interface routines found in transam.c * *------------------------------------------------------------------------- */ @@ -23,644 +23,659 @@ #include <access/xact.h> #include <storage/lmgr.h> -static AbsoluteTime TransBlockGetCommitTime(Block tblock, - TransactionId transactionId); -static XidStatus TransBlockGetXidStatus(Block tblock, - TransactionId transactionId); -static void TransBlockSetCommitTime(Block tblock, - TransactionId transactionId, AbsoluteTime commitTime); -static void TransBlockSetXidStatus(Block tblock, - TransactionId transactionId, XidStatus xstatus); +static AbsoluteTime +TransBlockGetCommitTime(Block tblock, + TransactionId transactionId); +static XidStatus +TransBlockGetXidStatus(Block tblock, + TransactionId transactionId); +static void +TransBlockSetCommitTime(Block tblock, + TransactionId transactionId, AbsoluteTime commitTime); +static void +TransBlockSetXidStatus(Block tblock, + TransactionId transactionId, XidStatus xstatus); /* ---------------------------------------------------------------- - * general support routines + * general support routines * ---------------------------------------------------------------- */ /* -------------------------------- - * AmiTransactionOverride + * AmiTransactionOverride * - * This function is used to manipulate the bootstrap flag. + * This function is used to manipulate the bootstrap flag. * -------------------------------- */ void AmiTransactionOverride(bool flag) { - AMI_OVERRIDE = flag; + AMI_OVERRIDE = flag; } /* -------------------------------- - * TransComputeBlockNumber + * TransComputeBlockNumber * -------------------------------- */ void -TransComputeBlockNumber(Relation relation, /* relation to test */ - TransactionId transactionId, /* transaction id to test */ - BlockNumber *blockNumberOutP) +TransComputeBlockNumber(Relation relation, /* relation to test */ + TransactionId transactionId, /* transaction id to + * test */ + BlockNumber * blockNumberOutP) { - long itemsPerBlock = 0; - - /* ---------------- - * we calculate the block number of our transaction - * by dividing the transaction id by the number of - * transaction things per block. - * ---------------- - */ - if (relation == LogRelation) - itemsPerBlock = TP_NumXidStatusPerBlock; - else if (relation == TimeRelation) - itemsPerBlock = TP_NumTimePerBlock; - else - elog(WARN, "TransComputeBlockNumber: unknown relation"); - - /* ---------------- - * warning! if the transaction id's get too large - * then a BlockNumber may not be large enough to hold the results - * of our division. - * - * XXX this will all vanish soon when we implement an improved - * transaction id schema -cim 3/23/90 - * - * This has vanished now that xid's are 4 bytes (no longer 5). - * -mer 5/24/92 - * ---------------- - */ - (*blockNumberOutP) = transactionId / itemsPerBlock; + long itemsPerBlock = 0; + + /* ---------------- + * we calculate the block number of our transaction + * by dividing the transaction id by the number of + * transaction things per block. + * ---------------- + */ + if (relation == LogRelation) + itemsPerBlock = TP_NumXidStatusPerBlock; + else if (relation == TimeRelation) + itemsPerBlock = TP_NumTimePerBlock; + else + elog(WARN, "TransComputeBlockNumber: unknown relation"); + + /* ---------------- + * warning! if the transaction id's get too large + * then a BlockNumber may not be large enough to hold the results + * of our division. + * + * XXX this will all vanish soon when we implement an improved + * transaction id schema -cim 3/23/90 + * + * This has vanished now that xid's are 4 bytes (no longer 5). + * -mer 5/24/92 + * ---------------- + */ + (*blockNumberOutP) = transactionId / itemsPerBlock; } /* ---------------------------------------------------------------- - * trans block support routines + * trans block support routines * ---------------------------------------------------------------- */ /* -------------------------------- - * TransBlockGetLastTransactionIdStatus + * TransBlockGetLastTransactionIdStatus * - * This returns the status and transaction id of the last - * transaction information recorded on the given TransBlock. + * This returns the status and transaction id of the last + * transaction information recorded on the given TransBlock. * -------------------------------- */ #ifdef NOT_USED -static XidStatus +static XidStatus TransBlockGetLastTransactionIdStatus(Block tblock, - TransactionId baseXid, - TransactionId *returnXidP) + TransactionId baseXid, + TransactionId * returnXidP) { - Index index; - Index maxIndex; - bits8 bit1; - bits8 bit2; - BitIndex offset; - XidStatus xstatus; - - /* ---------------- - * sanity check - * ---------------- - */ - Assert((tblock != NULL)); - - /* ---------------- - * search downward from the top of the block data, looking - * for the first Non-in progress transaction status. Since we - * are scanning backward, this will be last recorded transaction - * status on the block. - * ---------------- - */ - maxIndex = TP_NumXidStatusPerBlock; - for (index = maxIndex; index > 0; index--) { - offset = BitIndexOf(index-1); - bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1; - bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset); - - xstatus = (bit1 | bit2) ; - - /* ---------------- - * here we have the status of some transaction, so test - * if the status is recorded as "in progress". If so, then - * we save the transaction id in the place specified by the caller. - * ---------------- - */ - if (xstatus != XID_INPROGRESS) { - if (returnXidP != NULL) { - TransactionIdStore(baseXid, returnXidP); - TransactionIdAdd(returnXidP, index - 1); - } - break; + Index index; + Index maxIndex; + bits8 bit1; + bits8 bit2; + BitIndex offset; + XidStatus xstatus; + + /* ---------------- + * sanity check + * ---------------- + */ + Assert((tblock != NULL)); + + /* ---------------- + * search downward from the top of the block data, looking + * for the first Non-in progress transaction status. Since we + * are scanning backward, this will be last recorded transaction + * status on the block. + * ---------------- + */ + maxIndex = TP_NumXidStatusPerBlock; + for (index = maxIndex; index > 0; index--) + { + offset = BitIndexOf(index - 1); + bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1; + bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset); + + xstatus = (bit1 | bit2); + + /* ---------------- + * here we have the status of some transaction, so test + * if the status is recorded as "in progress". If so, then + * we save the transaction id in the place specified by the caller. + * ---------------- + */ + if (xstatus != XID_INPROGRESS) + { + if (returnXidP != NULL) + { + TransactionIdStore(baseXid, returnXidP); + TransactionIdAdd(returnXidP, index - 1); + } + break; + } } - } - - /* ---------------- - * if we get here and index is 0 it means we couldn't find - * a non-inprogress transaction on the block. For now we just - * return this info to the user. They can check if the return - * status is "in progress" to know this condition has arisen. - * ---------------- - */ - if (index == 0) { - if (returnXidP != NULL) - TransactionIdStore(baseXid, returnXidP); - } - - /* ---------------- - * return the status to the user - * ---------------- - */ - return xstatus; + + /* ---------------- + * if we get here and index is 0 it means we couldn't find + * a non-inprogress transaction on the block. For now we just + * return this info to the user. They can check if the return + * status is "in progress" to know this condition has arisen. + * ---------------- + */ + if (index == 0) + { + if (returnXidP != NULL) + TransactionIdStore(baseXid, returnXidP); + } + + /* ---------------- + * return the status to the user + * ---------------- + */ + return xstatus; } + #endif /* -------------------------------- - * TransBlockGetXidStatus + * TransBlockGetXidStatus * - * This returns the status of the desired transaction + * This returns the status of the desired transaction * -------------------------------- */ -static XidStatus +static XidStatus TransBlockGetXidStatus(Block tblock, - TransactionId transactionId) + TransactionId transactionId) { - Index index; - bits8 bit1; - bits8 bit2; - BitIndex offset; - - /* ---------------- - * sanity check - * ---------------- - */ - if (tblock == NULL) { - return XID_INVALID; - } - - /* ---------------- - * calculate the index into the transaction data where - * our transaction status is located - * - * XXX this will be replaced soon when we move to the - * new transaction id scheme -cim 3/23/90 - * - * The old system has now been replaced. -mer 5/24/92 - * ---------------- - */ - index = transactionId % TP_NumXidStatusPerBlock; - - /* ---------------- - * get the data at the specified index - * ---------------- - */ - offset = BitIndexOf(index); - bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1; - bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset); - - /* ---------------- - * return the transaction status to the caller - * ---------------- - */ - return (XidStatus) - (bit1 | bit2); + Index index; + bits8 bit1; + bits8 bit2; + BitIndex offset; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) + { + return XID_INVALID; + } + + /* ---------------- + * calculate the index into the transaction data where + * our transaction status is located + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The old system has now been replaced. -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumXidStatusPerBlock; + + /* ---------------- + * get the data at the specified index + * ---------------- + */ + offset = BitIndexOf(index); + bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1; + bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset); + + /* ---------------- + * return the transaction status to the caller + * ---------------- + */ + return (XidStatus) + (bit1 | bit2); } /* -------------------------------- - * TransBlockSetXidStatus + * TransBlockSetXidStatus * - * This sets the status of the desired transaction + * This sets the status of the desired transaction * -------------------------------- */ static void TransBlockSetXidStatus(Block tblock, - TransactionId transactionId, - XidStatus xstatus) + TransactionId transactionId, + XidStatus xstatus) { - Index index; - BitIndex offset; - - /* ---------------- - * sanity check - * ---------------- - */ - if (tblock == NULL) - return; - - /* ---------------- - * calculate the index into the transaction data where - * we sould store our transaction status. - * - * XXX this will be replaced soon when we move to the - * new transaction id scheme -cim 3/23/90 - * - * The new scheme is here -mer 5/24/92 - * ---------------- - */ - index = transactionId % TP_NumXidStatusPerBlock; - - offset = BitIndexOf(index); - - /* ---------------- - * store the transaction value at the specified offset - * ---------------- - */ - switch(xstatus) { - case XID_COMMIT: /* set 10 */ - BitArraySetBit((BitArray) tblock, offset); - BitArrayClearBit((BitArray) tblock, offset + 1); - break; - case XID_ABORT: /* set 01 */ - BitArrayClearBit((BitArray) tblock, offset); - BitArraySetBit((BitArray) tblock, offset + 1); - break; - case XID_INPROGRESS: /* set 00 */ - BitArrayClearBit((BitArray) tblock, offset); - BitArrayClearBit((BitArray) tblock, offset + 1); - break; - default: - elog(NOTICE, - "TransBlockSetXidStatus: invalid status: %d (ignored)", - xstatus); - break; - } + Index index; + BitIndex offset; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) + return; + + /* ---------------- + * calculate the index into the transaction data where + * we sould store our transaction status. + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The new scheme is here -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumXidStatusPerBlock; + + offset = BitIndexOf(index); + + /* ---------------- + * store the transaction value at the specified offset + * ---------------- + */ + switch (xstatus) + { + case XID_COMMIT: /* set 10 */ + BitArraySetBit((BitArray) tblock, offset); + BitArrayClearBit((BitArray) tblock, offset + 1); + break; + case XID_ABORT: /* set 01 */ + BitArrayClearBit((BitArray) tblock, offset); + BitArraySetBit((BitArray) tblock, offset + 1); + break; + case XID_INPROGRESS: /* set 00 */ + BitArrayClearBit((BitArray) tblock, offset); + BitArrayClearBit((BitArray) tblock, offset + 1); + break; + default: + elog(NOTICE, + "TransBlockSetXidStatus: invalid status: %d (ignored)", + xstatus); + break; + } } /* -------------------------------- - * TransBlockGetCommitTime + * TransBlockGetCommitTime * - * This returns the transaction commit time for the - * specified transaction id in the trans block. + * This returns the transaction commit time for the + * specified transaction id in the trans block. * -------------------------------- */ -static AbsoluteTime +static AbsoluteTime TransBlockGetCommitTime(Block tblock, - TransactionId transactionId) + TransactionId transactionId) { - Index index; - AbsoluteTime *timeArray; - - /* ---------------- - * sanity check - * ---------------- - */ - if (tblock == NULL) - return INVALID_ABSTIME; - - /* ---------------- - * calculate the index into the transaction data where - * our transaction commit time is located - * - * XXX this will be replaced soon when we move to the - * new transaction id scheme -cim 3/23/90 - * - * The new scheme is here. -mer 5/24/92 - * ---------------- - */ - index = transactionId % TP_NumTimePerBlock; - - /* ---------------- - * return the commit time to the caller - * ---------------- - */ - timeArray = (AbsoluteTime *) tblock; - return (AbsoluteTime) - timeArray[ index ]; + Index index; + AbsoluteTime *timeArray; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) + return INVALID_ABSTIME; + + /* ---------------- + * calculate the index into the transaction data where + * our transaction commit time is located + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The new scheme is here. -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumTimePerBlock; + + /* ---------------- + * return the commit time to the caller + * ---------------- + */ + timeArray = (AbsoluteTime *) tblock; + return (AbsoluteTime) + timeArray[index]; } /* -------------------------------- - * TransBlockSetCommitTime + * TransBlockSetCommitTime * - * This sets the commit time of the specified transaction + * This sets the commit time of the specified transaction * -------------------------------- */ static void TransBlockSetCommitTime(Block tblock, - TransactionId transactionId, - AbsoluteTime commitTime) + TransactionId transactionId, + AbsoluteTime commitTime) { - Index index; - AbsoluteTime *timeArray; - - /* ---------------- - * sanity check - * ---------------- - */ - if (tblock == NULL) - return; - - - /* ---------------- - * calculate the index into the transaction data where - * we sould store our transaction status. - * - * XXX this will be replaced soon when we move to the - * new transaction id scheme -cim 3/23/90 - * - * The new scheme is here. -mer 5/24/92 - * ---------------- - */ - index = transactionId % TP_NumTimePerBlock; - - /* ---------------- - * store the transaction commit time at the specified index - * ---------------- - */ - timeArray = (AbsoluteTime *) tblock; - timeArray[ index ] = commitTime; + Index index; + AbsoluteTime *timeArray; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) + return; + + + /* ---------------- + * calculate the index into the transaction data where + * we sould store our transaction status. + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The new scheme is here. -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumTimePerBlock; + + /* ---------------- + * store the transaction commit time at the specified index + * ---------------- + */ + timeArray = (AbsoluteTime *) tblock; + timeArray[index] = commitTime; } /* ---------------------------------------------------------------- - * transam i/o support routines + * transam i/o support routines * ---------------------------------------------------------------- */ /* -------------------------------- - * TransBlockNumberGetXidStatus + * TransBlockNumberGetXidStatus * -------------------------------- */ XidStatus TransBlockNumberGetXidStatus(Relation relation, - BlockNumber blockNumber, - TransactionId xid, - bool *failP) + BlockNumber blockNumber, + TransactionId xid, + bool * failP) { - Buffer buffer; /* buffer associated with block */ - Block block; /* block containing xstatus */ - XidStatus xstatus; /* recorded status of xid */ - bool localfail; /* bool used if failP = NULL */ - - /* ---------------- - * SOMEDAY place a read lock on the log relation - * That someday is today 5 Aug 1991 -mer - * ---------------- - */ - RelationSetLockForRead(relation); - - /* ---------------- - * get the page containing the transaction information - * ---------------- - */ - buffer = ReadBuffer(relation, blockNumber); - block = BufferGetBlock(buffer); - - /* ---------------- - * get the status from the block. note, for now we always - * return false in failP. - * ---------------- - */ - if (failP == NULL) - failP = &localfail; - (*failP) = false; - - xstatus = TransBlockGetXidStatus(block, xid); - - /* ---------------- - * release the buffer and return the status - * ---------------- - */ - ReleaseBuffer(buffer); - - /* ---------------- - * SOMEDAY release our lock on the log relation - * ---------------- - */ - RelationUnsetLockForRead(relation); - - return - xstatus; + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing xstatus */ + XidStatus xstatus; /* recorded status of xid */ + bool localfail; /* bool used if failP = NULL */ + + /* ---------------- + * SOMEDAY place a read lock on the log relation + * That someday is today 5 Aug 1991 -mer + * ---------------- + */ + RelationSetLockForRead(relation); + + /* ---------------- + * get the page containing the transaction information + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * get the status from the block. note, for now we always + * return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + xstatus = TransBlockGetXidStatus(block, xid); + + /* ---------------- + * release the buffer and return the status + * ---------------- + */ + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the log relation + * ---------------- + */ + RelationUnsetLockForRead(relation); + + return + xstatus; } /* -------------------------------- - * TransBlockNumberSetXidStatus + * TransBlockNumberSetXidStatus * -------------------------------- */ void TransBlockNumberSetXidStatus(Relation relation, - BlockNumber blockNumber, - TransactionId xid, - XidStatus xstatus, - bool *failP) + BlockNumber blockNumber, + TransactionId xid, + XidStatus xstatus, + bool * failP) { - Buffer buffer; /* buffer associated with block */ - Block block; /* block containing xstatus */ - bool localfail; /* bool used if failP = NULL */ - - /* ---------------- - * SOMEDAY gain exclusive access to the log relation - * - * That someday is today 5 Aug 1991 -mer - * ---------------- - */ - RelationSetLockForWrite(relation); - - /* ---------------- - * get the block containing the transaction status - * ---------------- - */ - buffer = ReadBuffer(relation, blockNumber); - block = BufferGetBlock(buffer); - - /* ---------------- - * attempt to update the status of the transaction on the block. - * if we are successful, write the block. otherwise release the buffer. - * note, for now we always return false in failP. - * ---------------- - */ - if (failP == NULL) - failP = &localfail; - (*failP) = false; - - TransBlockSetXidStatus(block, xid, xstatus); - - if ((*failP) == false) - WriteBuffer(buffer); - else - ReleaseBuffer(buffer); - - /* ---------------- - * SOMEDAY release our lock on the log relation - * ---------------- - */ - RelationUnsetLockForWrite(relation); + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing xstatus */ + bool localfail; /* bool used if failP = NULL */ + + /* ---------------- + * SOMEDAY gain exclusive access to the log relation + * + * That someday is today 5 Aug 1991 -mer + * ---------------- + */ + RelationSetLockForWrite(relation); + + /* ---------------- + * get the block containing the transaction status + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * attempt to update the status of the transaction on the block. + * if we are successful, write the block. otherwise release the buffer. + * note, for now we always return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + TransBlockSetXidStatus(block, xid, xstatus); + + if ((*failP) == false) + WriteBuffer(buffer); + else + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the log relation + * ---------------- + */ + RelationUnsetLockForWrite(relation); } /* -------------------------------- - * TransBlockNumberGetCommitTime + * TransBlockNumberGetCommitTime * -------------------------------- */ AbsoluteTime TransBlockNumberGetCommitTime(Relation relation, - BlockNumber blockNumber, - TransactionId xid, - bool *failP) + BlockNumber blockNumber, + TransactionId xid, + bool * failP) { - Buffer buffer; /* buffer associated with block */ - Block block; /* block containing commit time */ - bool localfail; /* bool used if failP = NULL */ - AbsoluteTime xtime; /* commit time */ - - /* ---------------- - * SOMEDAY place a read lock on the time relation - * - * That someday is today 5 Aug. 1991 -mer - * ---------------- - */ - RelationSetLockForRead(relation); - - /* ---------------- - * get the block containing the transaction information - * ---------------- - */ - buffer = ReadBuffer(relation, blockNumber); - block = BufferGetBlock(buffer); - - /* ---------------- - * get the commit time from the block - * note, for now we always return false in failP. - * ---------------- - */ - if (failP == NULL) - failP = &localfail; - (*failP) = false; - - xtime = TransBlockGetCommitTime(block, xid); - - /* ---------------- - * release the buffer and return the commit time - * ---------------- - */ - ReleaseBuffer(buffer); - - /* ---------------- - * SOMEDAY release our lock on the time relation - * ---------------- - */ - RelationUnsetLockForRead(relation); - - if ((*failP) == false) - return xtime; - else - return INVALID_ABSTIME; - + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing commit time */ + bool localfail; /* bool used if failP = NULL */ + AbsoluteTime xtime; /* commit time */ + + /* ---------------- + * SOMEDAY place a read lock on the time relation + * + * That someday is today 5 Aug. 1991 -mer + * ---------------- + */ + RelationSetLockForRead(relation); + + /* ---------------- + * get the block containing the transaction information + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * get the commit time from the block + * note, for now we always return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + xtime = TransBlockGetCommitTime(block, xid); + + /* ---------------- + * release the buffer and return the commit time + * ---------------- + */ + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the time relation + * ---------------- + */ + RelationUnsetLockForRead(relation); + + if ((*failP) == false) + return xtime; + else + return INVALID_ABSTIME; + } /* -------------------------------- - * TransBlockNumberSetCommitTime + * TransBlockNumberSetCommitTime * -------------------------------- */ void TransBlockNumberSetCommitTime(Relation relation, - BlockNumber blockNumber, - TransactionId xid, - AbsoluteTime xtime, - bool *failP) + BlockNumber blockNumber, + TransactionId xid, + AbsoluteTime xtime, + bool * failP) { - Buffer buffer; /* buffer associated with block */ - Block block; /* block containing commit time */ - bool localfail; /* bool used if failP = NULL */ - - /* ---------------- - * SOMEDAY gain exclusive access to the time relation - * - * That someday is today 5 Aug. 1991 -mer - * ---------------- - */ - RelationSetLockForWrite(relation); - - /* ---------------- - * get the block containing our commit time - * ---------------- - */ - buffer = ReadBuffer(relation, blockNumber); - block = BufferGetBlock(buffer); - - /* ---------------- - * attempt to update the commit time of the transaction on the block. - * if we are successful, write the block. otherwise release the buffer. - * note, for now we always return false in failP. - * ---------------- - */ - if (failP == NULL) - failP = &localfail; - (*failP) = false; - - TransBlockSetCommitTime(block, xid, xtime); - - if ((*failP) == false) - WriteBuffer(buffer); - else - ReleaseBuffer(buffer); - - /* ---------------- - * SOMEDAY release our lock on the time relation - * ---------------- - */ - RelationUnsetLockForWrite(relation); - + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing commit time */ + bool localfail; /* bool used if failP = NULL */ + + /* ---------------- + * SOMEDAY gain exclusive access to the time relation + * + * That someday is today 5 Aug. 1991 -mer + * ---------------- + */ + RelationSetLockForWrite(relation); + + /* ---------------- + * get the block containing our commit time + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * attempt to update the commit time of the transaction on the block. + * if we are successful, write the block. otherwise release the buffer. + * note, for now we always return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + TransBlockSetCommitTime(block, xid, xtime); + + if ((*failP) == false) + WriteBuffer(buffer); + else + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the time relation + * ---------------- + */ + RelationUnsetLockForWrite(relation); + } /* -------------------------------- - * TransGetLastRecordedTransaction + * TransGetLastRecordedTransaction * -------------------------------- */ #ifdef NOT_USED void TransGetLastRecordedTransaction(Relation relation, - TransactionId xid, /* return: transaction id */ - bool *failP) + TransactionId xid, /* return: transaction + * id */ + bool * failP) { - BlockNumber blockNumber; /* block number */ - Buffer buffer; /* buffer associated with block */ - Block block; /* block containing xid status */ - BlockNumber n; /* number of blocks in the relation */ - TransactionId baseXid; - - (*failP) = false; - - /* ---------------- - * SOMEDAY gain exclusive access to the log relation - * - * That someday is today 5 Aug. 1991 -mer - * It looks to me like we only need to set a read lock here, despite - * the above comment about exclusive access. The block is never - * actually written into, we only check status bits. - * ---------------- - */ - RelationSetLockForRead(relation); - - /* ---------------- - * we assume the last block of the log contains the last - * recorded transaction. If the relation is empty we return - * failure to the user. - * ---------------- - */ - n = RelationGetNumberOfBlocks(relation); - if (n == 0) { - (*failP) = true; - return; - } - - /* ---------------- - * get the block containing the transaction information - * ---------------- - */ - blockNumber = n-1; - buffer = ReadBuffer(relation, blockNumber); - block = BufferGetBlock(buffer); - - /* ---------------- - * get the last xid on the block - * ---------------- - */ - baseXid = blockNumber * TP_NumXidStatusPerBlock; + BlockNumber blockNumber;/* block number */ + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing xid status */ + BlockNumber n; /* number of blocks in the relation */ + TransactionId baseXid; + + (*failP) = false; + + /* ---------------- + * SOMEDAY gain exclusive access to the log relation + * + * That someday is today 5 Aug. 1991 -mer + * It looks to me like we only need to set a read lock here, despite + * the above comment about exclusive access. The block is never + * actually written into, we only check status bits. + * ---------------- + */ + RelationSetLockForRead(relation); + + /* ---------------- + * we assume the last block of the log contains the last + * recorded transaction. If the relation is empty we return + * failure to the user. + * ---------------- + */ + n = RelationGetNumberOfBlocks(relation); + if (n == 0) + { + (*failP) = true; + return; + } + + /* ---------------- + * get the block containing the transaction information + * ---------------- + */ + blockNumber = n - 1; + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * get the last xid on the block + * ---------------- + */ + baseXid = blockNumber * TP_NumXidStatusPerBlock; /* XXX ???? xid won't get returned! - AY '94 */ - TransBlockGetLastTransactionIdStatus(block, baseXid, &xid); - - ReleaseBuffer(buffer); - - /* ---------------- - * SOMEDAY release our lock on the log relation - * ---------------- - */ - RelationUnsetLockForRead(relation); + TransBlockGetLastTransactionIdStatus(block, baseXid, &xid); + + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the log relation + * ---------------- + */ + RelationUnsetLockForRead(relation); } + #endif diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index ba60ca35941..8b4b8557eb2 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * varsup.c-- - * postgres variable relation support routines + * postgres variable relation support routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.9 1997/08/19 21:30:16 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.10 1997/09/07 04:39:35 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,640 +20,647 @@ #include <access/heapam.h> #include <catalog/catname.h> -static void GetNewObjectIdBlock(Oid *oid_return, int oid_block_size); -static void VariableRelationGetNextOid(Oid *oid_return); -static void VariableRelationGetNextXid(TransactionId *xidP); -static void VariableRelationPutLastXid(TransactionId xid); -static void VariableRelationPutNextOid(Oid *oidP); -static void VariableRelationGetLastXid(TransactionId *xidP); +static void GetNewObjectIdBlock(Oid * oid_return, int oid_block_size); +static void VariableRelationGetNextOid(Oid * oid_return); +static void VariableRelationGetNextXid(TransactionId * xidP); +static void VariableRelationPutLastXid(TransactionId xid); +static void VariableRelationPutNextOid(Oid * oidP); +static void VariableRelationGetLastXid(TransactionId * xidP); /* --------------------- - * spin lock for oid generation + * spin lock for oid generation * --------------------- */ -int OidGenLockId; +int OidGenLockId; /* ---------------------------------------------------------------- - * variable relation query/update routines + * variable relation query/update routines * ---------------------------------------------------------------- */ /* -------------------------------- - * VariableRelationGetNextXid + * VariableRelationGetNextXid * -------------------------------- */ static void -VariableRelationGetNextXid(TransactionId *xidP) +VariableRelationGetNextXid(TransactionId * xidP) { - Buffer buf; - VariableRelationContents var; - - /* ---------------- - * We assume that a spinlock has been acquire to guarantee - * exclusive access to the variable relation. - * ---------------- - */ - - /* ---------------- - * do nothing before things are initialized - * ---------------- - */ - if (! RelationIsValid(VariableRelation)) - return; - - /* ---------------- - * read the variable page, get the the nextXid field and - * release the buffer - * ---------------- - */ - buf = ReadBuffer(VariableRelation, 0); - - if (! BufferIsValid(buf)) + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (!RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, get the the nextXid field and + * release the buffer + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (!BufferIsValid(buf)) { - SpinRelease(OidGenLockId); - elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); } - - var = (VariableRelationContents) BufferGetBlock(buf); - - TransactionIdStore(var->nextXidData, xidP); - ReleaseBuffer(buf); + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(var->nextXidData, xidP); + ReleaseBuffer(buf); } /* -------------------------------- - * VariableRelationGetLastXid + * VariableRelationGetLastXid * -------------------------------- */ static void -VariableRelationGetLastXid(TransactionId *xidP) +VariableRelationGetLastXid(TransactionId * xidP) { - Buffer buf; - VariableRelationContents var; - - /* ---------------- - * We assume that a spinlock has been acquire to guarantee - * exclusive access to the variable relation. - * ---------------- - */ - - /* ---------------- - * do nothing before things are initialized - * ---------------- - */ - if (! RelationIsValid(VariableRelation)) - return; - - /* ---------------- - * read the variable page, get the the lastXid field and - * release the buffer - * ---------------- - */ - buf = ReadBuffer(VariableRelation, 0); - - if (! BufferIsValid(buf)) + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (!RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, get the the lastXid field and + * release the buffer + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (!BufferIsValid(buf)) { - SpinRelease(OidGenLockId); - elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); } - - var = (VariableRelationContents) BufferGetBlock(buf); - - TransactionIdStore(var->lastXidData, xidP); - - ReleaseBuffer(buf); + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(var->lastXidData, xidP); + + ReleaseBuffer(buf); } /* -------------------------------- - * VariableRelationPutNextXid + * VariableRelationPutNextXid * -------------------------------- */ void VariableRelationPutNextXid(TransactionId xid) { - Buffer buf; - VariableRelationContents var; - int flushmode; - - /* ---------------- - * We assume that a spinlock has been acquire to guarantee - * exclusive access to the variable relation. - * ---------------- - */ - - /* ---------------- - * do nothing before things are initialized - * ---------------- - */ - if (! RelationIsValid(VariableRelation)) - return; - - /* ---------------- - * read the variable page, update the nextXid field and - * write the page back out to disk. - * ---------------- - */ - buf = ReadBuffer(VariableRelation, 0); - - if (! BufferIsValid(buf)) + Buffer buf; + VariableRelationContents var; + int flushmode; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (!RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, update the nextXid field and + * write the page back out to disk. + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (!BufferIsValid(buf)) { - SpinRelease(OidGenLockId); - elog(WARN, "VariableRelationPutNextXid: ReadBuffer failed"); + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutNextXid: ReadBuffer failed"); } - - var = (VariableRelationContents) BufferGetBlock(buf); - - TransactionIdStore(xid, &(var->nextXidData)); - - flushmode = SetBufferWriteMode (BUFFER_FLUSH_WRITE); - WriteBuffer(buf); - SetBufferWriteMode (flushmode); + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(xid, &(var->nextXidData)); + + flushmode = SetBufferWriteMode(BUFFER_FLUSH_WRITE); + WriteBuffer(buf); + SetBufferWriteMode(flushmode); } /* -------------------------------- - * VariableRelationPutLastXid + * VariableRelationPutLastXid * -------------------------------- */ static void VariableRelationPutLastXid(TransactionId xid) { - Buffer buf; - VariableRelationContents var; - - /* ---------------- - * We assume that a spinlock has been acquire to guarantee - * exclusive access to the variable relation. - * ---------------- - */ - - /* ---------------- - * do nothing before things are initialized - * ---------------- - */ - if (! RelationIsValid(VariableRelation)) - return; - - /* ---------------- - * read the variable page, update the lastXid field and - * force the page back out to disk. - * ---------------- - */ - buf = ReadBuffer(VariableRelation, 0); - - if (! BufferIsValid(buf)) + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (!RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, update the lastXid field and + * force the page back out to disk. + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (!BufferIsValid(buf)) { - SpinRelease(OidGenLockId); - elog(WARN, "VariableRelationPutLastXid: ReadBuffer failed"); + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutLastXid: ReadBuffer failed"); } - - var = (VariableRelationContents) BufferGetBlock(buf); - - TransactionIdStore(xid, &(var->lastXidData)); - - WriteBuffer(buf); + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(xid, &(var->lastXidData)); + + WriteBuffer(buf); } /* -------------------------------- - * VariableRelationGetNextOid + * VariableRelationGetNextOid * -------------------------------- */ static void -VariableRelationGetNextOid(Oid *oid_return) +VariableRelationGetNextOid(Oid * oid_return) { - Buffer buf; - VariableRelationContents var; - - /* ---------------- - * We assume that a spinlock has been acquire to guarantee - * exclusive access to the variable relation. - * ---------------- - */ - - /* ---------------- - * if the variable relation is not initialized, then we - * assume we are running at bootstrap time and so we return - * an invalid object id -- during this time GetNextBootstrapObjectId - * should be called instead.. - * ---------------- - */ - if (! RelationIsValid(VariableRelation)) { + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * if the variable relation is not initialized, then we + * assume we are running at bootstrap time and so we return + * an invalid object id -- during this time GetNextBootstrapObjectId + * should be called instead.. + * ---------------- + */ + if (!RelationIsValid(VariableRelation)) + { + if (PointerIsValid(oid_return)) + (*oid_return) = InvalidOid; + return; + } + + /* ---------------- + * read the variable page, get the the nextOid field and + * release the buffer + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (!BufferIsValid(buf)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); + } + + var = (VariableRelationContents) BufferGetBlock(buf); + if (PointerIsValid(oid_return)) - (*oid_return) = InvalidOid; - return; - } - - /* ---------------- - * read the variable page, get the the nextOid field and - * release the buffer - * ---------------- - */ - buf = ReadBuffer(VariableRelation, 0); - - if (! BufferIsValid(buf)) { - SpinRelease(OidGenLockId); - elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); + + /* ---------------- + * nothing up my sleeve... what's going on here is that this code + * is guaranteed never to be called until all files in data/base/ + * are created, and the template database exists. at that point, + * we want to append a pg_database tuple. the first time we do + * this, the oid stored in pg_variable will be bogus, so we use + * a bootstrap value defined at the top of this file. + * + * this comment no longer holds true. This code is called before + * all of the files in data/base are created and you can't rely + * on system oid's to be less than BootstrapObjectIdData. mer 9/18/91 + * ---------------- + */ + if (OidIsValid(var->nextOid)) + (*oid_return) = var->nextOid; + else + (*oid_return) = BootstrapObjectIdData; } - - var = (VariableRelationContents) BufferGetBlock(buf); - - if (PointerIsValid(oid_return)) { - - /* ---------------- - * nothing up my sleeve... what's going on here is that this code - * is guaranteed never to be called until all files in data/base/ - * are created, and the template database exists. at that point, - * we want to append a pg_database tuple. the first time we do - * this, the oid stored in pg_variable will be bogus, so we use - * a bootstrap value defined at the top of this file. - * - * this comment no longer holds true. This code is called before - * all of the files in data/base are created and you can't rely - * on system oid's to be less than BootstrapObjectIdData. mer 9/18/91 - * ---------------- - */ - if (OidIsValid(var->nextOid)) - (*oid_return) = var->nextOid; - else - (*oid_return) = BootstrapObjectIdData; - } - - ReleaseBuffer(buf); + + ReleaseBuffer(buf); } /* -------------------------------- - * VariableRelationPutNextOid + * VariableRelationPutNextOid * -------------------------------- */ static void -VariableRelationPutNextOid(Oid *oidP) +VariableRelationPutNextOid(Oid * oidP) { - Buffer buf; - VariableRelationContents var; - - /* ---------------- - * We assume that a spinlock has been acquire to guarantee - * exclusive access to the variable relation. - * ---------------- - */ - - /* ---------------- - * do nothing before things are initialized - * ---------------- - */ - if (! RelationIsValid(VariableRelation)) - return; - - /* ---------------- - * sanity check - * ---------------- - */ - if (! PointerIsValid(oidP)) + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (!RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * sanity check + * ---------------- + */ + if (!PointerIsValid(oidP)) { - SpinRelease(OidGenLockId); - elog(WARN, "VariableRelationPutNextOid: invalid oid pointer"); + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutNextOid: invalid oid pointer"); } - - /* ---------------- - * read the variable page, update the nextXid field and - * write the page back out to disk. - * ---------------- - */ - buf = ReadBuffer(VariableRelation, 0); - - if (! BufferIsValid(buf)) + + /* ---------------- + * read the variable page, update the nextXid field and + * write the page back out to disk. + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (!BufferIsValid(buf)) { - SpinRelease(OidGenLockId); - elog(WARN, "VariableRelationPutNextOid: ReadBuffer failed"); + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutNextOid: ReadBuffer failed"); } - - var = (VariableRelationContents) BufferGetBlock(buf); - - var->nextOid = (*oidP); - - WriteBuffer(buf); + + var = (VariableRelationContents) BufferGetBlock(buf); + + var->nextOid = (*oidP); + + WriteBuffer(buf); } /* ---------------------------------------------------------------- - * transaction id generation support + * transaction id generation support * ---------------------------------------------------------------- */ /* ---------------- - * GetNewTransactionId + * GetNewTransactionId * - * In the version 2 transaction system, transaction id's are - * restricted in several ways. + * In the version 2 transaction system, transaction id's are + * restricted in several ways. * - * First, all transaction id's are even numbers (4, 88, 121342, etc). - * This means the binary representation of the number will never - * have the least significent bit set. This bit is reserved to - * indicate that the transaction id does not in fact hold an XID, - * but rather a commit time. This makes it possible for the - * vaccuum daemon to disgard information from the log and time - * relations for committed tuples. This is important when archiving - * tuples to an optical disk because tuples with commit times - * stored in their xid fields will not need to consult the log - * and time relations. + * First, all transaction id's are even numbers (4, 88, 121342, etc). + * This means the binary representation of the number will never + * have the least significent bit set. This bit is reserved to + * indicate that the transaction id does not in fact hold an XID, + * but rather a commit time. This makes it possible for the + * vaccuum daemon to disgard information from the log and time + * relations for committed tuples. This is important when archiving + * tuples to an optical disk because tuples with commit times + * stored in their xid fields will not need to consult the log + * and time relations. * - * Second, since we may someday preform compression of the data - * in the log and time relations, we cause the numbering of the - * transaction ids to begin at 512. This means that some space - * on the page of the log and time relations corresponding to - * transaction id's 0 - 510 will never be used. This space is - * in fact used to store the version number of the postgres - * transaction log and will someday store compression information - * about the log. + * Second, since we may someday preform compression of the data + * in the log and time relations, we cause the numbering of the + * transaction ids to begin at 512. This means that some space + * on the page of the log and time relations corresponding to + * transaction id's 0 - 510 will never be used. This space is + * in fact used to store the version number of the postgres + * transaction log and will someday store compression information + * about the log. * - * Lastly, rather then access the variable relation each time - * a backend requests a new transction id, we "prefetch" 32 - * transaction id's by incrementing the nextXid stored in the - * var relation by 64 (remember only even xid's are legal) and then - * returning these id's one at a time until they are exhausted. - * This means we reduce the number of accesses to the variable - * relation by 32 for each backend. + * Lastly, rather then access the variable relation each time + * a backend requests a new transction id, we "prefetch" 32 + * transaction id's by incrementing the nextXid stored in the + * var relation by 64 (remember only even xid's are legal) and then + * returning these id's one at a time until they are exhausted. + * This means we reduce the number of accesses to the variable + * relation by 32 for each backend. * - * Note: 32 has no special significance. We don't want the - * number to be too large because if when the backend - * terminates, we lose the xid's we cached. + * Note: 32 has no special significance. We don't want the + * number to be too large because if when the backend + * terminates, we lose the xid's we cached. * * ---------------- */ -#define VAR_XID_PREFETCH 32 +#define VAR_XID_PREFETCH 32 -static int prefetched_xid_count = 0; +static int prefetched_xid_count = 0; static TransactionId next_prefetched_xid; void -GetNewTransactionId(TransactionId *xid) +GetNewTransactionId(TransactionId * xid) { - TransactionId nextid; - - /* ---------------- - * during bootstrap initialization, we return the special - * bootstrap transaction id. - * ---------------- - */ - if (AMI_OVERRIDE) { - TransactionIdStore(AmiTransactionId, xid); - return; - } - - /* ---------------- - * if we run out of prefetched xids, then we get some - * more before handing them out to the caller. - * ---------------- - */ - - if (prefetched_xid_count == 0) { - /* ---------------- - * obtain exclusive access to the variable relation page - * - * get the "next" xid from the variable relation - * and save it in the prefetched id. + TransactionId nextid; + + /* ---------------- + * during bootstrap initialization, we return the special + * bootstrap transaction id. * ---------------- */ - SpinAcquire(OidGenLockId); - VariableRelationGetNextXid(&nextid); - TransactionIdStore(nextid, &next_prefetched_xid); - + if (AMI_OVERRIDE) + { + TransactionIdStore(AmiTransactionId, xid); + return; + } + /* ---------------- - * now increment the variable relation's next xid - * and reset the prefetched_xid_count. We multiply - * the id by two because our xid's are always even. + * if we run out of prefetched xids, then we get some + * more before handing them out to the caller. * ---------------- */ - prefetched_xid_count = VAR_XID_PREFETCH; - TransactionIdAdd(&nextid, prefetched_xid_count); - VariableRelationPutNextXid(nextid); - SpinRelease(OidGenLockId); - } - - /* ---------------- - * return the next prefetched xid in the pointer passed by - * the user and decrement the prefetch count. We add two - * to id we return the next time this is called because our - * transaction ids are always even. - * - * XXX Transaction Ids used to be even as the low order bit was - * used to determine commit status. This is no long true so - * we now use even and odd transaction ids. -mer 5/26/92 - * ---------------- - */ - TransactionIdStore(next_prefetched_xid, xid); - TransactionIdAdd(&next_prefetched_xid, 1); - prefetched_xid_count--; + + if (prefetched_xid_count == 0) + { + /* ---------------- + * obtain exclusive access to the variable relation page + * + * get the "next" xid from the variable relation + * and save it in the prefetched id. + * ---------------- + */ + SpinAcquire(OidGenLockId); + VariableRelationGetNextXid(&nextid); + TransactionIdStore(nextid, &next_prefetched_xid); + + /* ---------------- + * now increment the variable relation's next xid + * and reset the prefetched_xid_count. We multiply + * the id by two because our xid's are always even. + * ---------------- + */ + prefetched_xid_count = VAR_XID_PREFETCH; + TransactionIdAdd(&nextid, prefetched_xid_count); + VariableRelationPutNextXid(nextid); + SpinRelease(OidGenLockId); + } + + /* ---------------- + * return the next prefetched xid in the pointer passed by + * the user and decrement the prefetch count. We add two + * to id we return the next time this is called because our + * transaction ids are always even. + * + * XXX Transaction Ids used to be even as the low order bit was + * used to determine commit status. This is no long true so + * we now use even and odd transaction ids. -mer 5/26/92 + * ---------------- + */ + TransactionIdStore(next_prefetched_xid, xid); + TransactionIdAdd(&next_prefetched_xid, 1); + prefetched_xid_count--; } /* ---------------- - * UpdateLastCommittedXid + * UpdateLastCommittedXid * ---------------- */ void UpdateLastCommittedXid(TransactionId xid) { - TransactionId lastid; - - - /* we assume that spinlock OidGenLockId has been acquired - * prior to entering this function - */ - - /* ---------------- - * get the "last committed" transaction id from - * the variable relation page. - * ---------------- - */ - VariableRelationGetLastXid(&lastid); - - /* ---------------- - * if the transaction id is greater than the last committed - * transaction then we update the last committed transaction - * in the variable relation. - * ---------------- - */ - if (TransactionIdIsLessThan(lastid, xid)) - VariableRelationPutLastXid(xid); - + TransactionId lastid; + + + /* + * we assume that spinlock OidGenLockId has been acquired prior to + * entering this function + */ + + /* ---------------- + * get the "last committed" transaction id from + * the variable relation page. + * ---------------- + */ + VariableRelationGetLastXid(&lastid); + + /* ---------------- + * if the transaction id is greater than the last committed + * transaction then we update the last committed transaction + * in the variable relation. + * ---------------- + */ + if (TransactionIdIsLessThan(lastid, xid)) + VariableRelationPutLastXid(xid); + } /* ---------------------------------------------------------------- - * object id generation support + * object id generation support * ---------------------------------------------------------------- */ /* ---------------- - * GetNewObjectIdBlock + * GetNewObjectIdBlock * - * This support function is used to allocate a block of object ids - * of the given size. applications wishing to do their own object - * id assignments should use this + * This support function is used to allocate a block of object ids + * of the given size. applications wishing to do their own object + * id assignments should use this * ---------------- */ static void -GetNewObjectIdBlock(Oid *oid_return, /* place to return the new object id */ - int oid_block_size) /* number of oids desired */ +GetNewObjectIdBlock(Oid * oid_return, /* place to return the new object + * id */ + int oid_block_size) /* number of oids desired */ { - Oid nextoid; - - /* ---------------- - * SOMEDAY obtain exclusive access to the variable relation page - * That someday is today -mer 6 Aug 1992 - * ---------------- - */ - SpinAcquire(OidGenLockId); - - /* ---------------- - * get the "next" oid from the variable relation - * and give it to the caller. - * ---------------- - */ - VariableRelationGetNextOid(&nextoid); - if (PointerIsValid(oid_return)) - (*oid_return) = nextoid; - - /* ---------------- - * now increment the variable relation's next oid - * field by the size of the oid block requested. - * ---------------- - */ - nextoid += oid_block_size; - VariableRelationPutNextOid(&nextoid); - - /* ---------------- - * SOMEDAY relinquish our lock on the variable relation page - * That someday is today -mer 6 Apr 1992 - * ---------------- - */ - SpinRelease(OidGenLockId); + Oid nextoid; + + /* ---------------- + * SOMEDAY obtain exclusive access to the variable relation page + * That someday is today -mer 6 Aug 1992 + * ---------------- + */ + SpinAcquire(OidGenLockId); + + /* ---------------- + * get the "next" oid from the variable relation + * and give it to the caller. + * ---------------- + */ + VariableRelationGetNextOid(&nextoid); + if (PointerIsValid(oid_return)) + (*oid_return) = nextoid; + + /* ---------------- + * now increment the variable relation's next oid + * field by the size of the oid block requested. + * ---------------- + */ + nextoid += oid_block_size; + VariableRelationPutNextOid(&nextoid); + + /* ---------------- + * SOMEDAY relinquish our lock on the variable relation page + * That someday is today -mer 6 Apr 1992 + * ---------------- + */ + SpinRelease(OidGenLockId); } /* ---------------- - * GetNewObjectId + * GetNewObjectId * - * This function allocates and parses out object ids. Like - * GetNewTransactionId(), it "prefetches" 32 object ids by - * incrementing the nextOid stored in the var relation by 32 and then - * returning these id's one at a time until they are exhausted. - * This means we reduce the number of accesses to the variable - * relation by 32 for each backend. + * This function allocates and parses out object ids. Like + * GetNewTransactionId(), it "prefetches" 32 object ids by + * incrementing the nextOid stored in the var relation by 32 and then + * returning these id's one at a time until they are exhausted. + * This means we reduce the number of accesses to the variable + * relation by 32 for each backend. * - * Note: 32 has no special significance. We don't want the - * number to be too large because if when the backend - * terminates, we lose the oids we cached. + * Note: 32 has no special significance. We don't want the + * number to be too large because if when the backend + * terminates, we lose the oids we cached. * * ---------------- */ -#define VAR_OID_PREFETCH 32 +#define VAR_OID_PREFETCH 32 -static int prefetched_oid_count = 0; -static Oid next_prefetched_oid; +static int prefetched_oid_count = 0; +static Oid next_prefetched_oid; void -GetNewObjectId(Oid *oid_return) /* place to return the new object id */ +GetNewObjectId(Oid * oid_return)/* place to return the new object id */ { - /* ---------------- - * if we run out of prefetched oids, then we get some - * more before handing them out to the caller. - * ---------------- - */ - - if (prefetched_oid_count == 0) { - int oid_block_size = VAR_OID_PREFETCH; - - /* ---------------- - * during bootstrap time, we want to allocate oids - * one at a time. Otherwise there might be some - * bootstrap oid's left in the block we prefetch which - * would be passed out after the variable relation was - * initialized. This would be bad. - * ---------------- - */ - if (! RelationIsValid(VariableRelation)) - VariableRelation = heap_openr(VariableRelationName); - - /* ---------------- - * get a new block of prefetched object ids. - * ---------------- - */ - GetNewObjectIdBlock(&next_prefetched_oid, oid_block_size); - - /* ---------------- - * now reset the prefetched_oid_count. - * ---------------- - */ - prefetched_oid_count = oid_block_size; - } - - /* ---------------- - * return the next prefetched oid in the pointer passed by - * the user and decrement the prefetch count. - * ---------------- - */ - if (PointerIsValid(oid_return)) - (*oid_return) = next_prefetched_oid; - - next_prefetched_oid++; - prefetched_oid_count--; + /* ---------------- + * if we run out of prefetched oids, then we get some + * more before handing them out to the caller. + * ---------------- + */ + + if (prefetched_oid_count == 0) + { + int oid_block_size = VAR_OID_PREFETCH; + + /* ---------------- + * during bootstrap time, we want to allocate oids + * one at a time. Otherwise there might be some + * bootstrap oid's left in the block we prefetch which + * would be passed out after the variable relation was + * initialized. This would be bad. + * ---------------- + */ + if (!RelationIsValid(VariableRelation)) + VariableRelation = heap_openr(VariableRelationName); + + /* ---------------- + * get a new block of prefetched object ids. + * ---------------- + */ + GetNewObjectIdBlock(&next_prefetched_oid, oid_block_size); + + /* ---------------- + * now reset the prefetched_oid_count. + * ---------------- + */ + prefetched_oid_count = oid_block_size; + } + + /* ---------------- + * return the next prefetched oid in the pointer passed by + * the user and decrement the prefetch count. + * ---------------- + */ + if (PointerIsValid(oid_return)) + (*oid_return) = next_prefetched_oid; + + next_prefetched_oid++; + prefetched_oid_count--; } void CheckMaxObjectId(Oid assigned_oid) { -Oid pass_oid; - - - if (prefetched_oid_count == 0) /* make sure next/max is set, or reload */ - GetNewObjectId(&pass_oid); - - /* ---------------- - * If we are below prefetched limits, do nothing - * ---------------- - */ - - if (assigned_oid < next_prefetched_oid) - return; - - /* ---------------- - * If we are here, we are coming from a 'copy from' with oid's - * - * If we are in the prefetched oid range, just bump it up - * - * ---------------- - */ - - if (assigned_oid <= next_prefetched_oid + prefetched_oid_count - 1) - { - prefetched_oid_count -= assigned_oid - next_prefetched_oid + 1; - next_prefetched_oid = assigned_oid + 1; - return; - } - - /* ---------------- - * We have exceeded the prefetch oid range - * - * We should lock the database and kill all other backends - * but we are loading oid's that we can not guarantee are unique - * anyway, so we must rely on the user - * - * - * We now: - * set the variable relation with the new max oid - * force the backend to reload its oid cache - * - * We use the oid cache so we don't have to update the variable - * relation every time - * - * ---------------- - */ - - pass_oid = assigned_oid; - VariableRelationPutNextOid(&pass_oid); /* not modified */ - prefetched_oid_count = 0; /* force reload */ - pass_oid = assigned_oid; - GetNewObjectId(&pass_oid); /* throw away returned oid */ + Oid pass_oid; -} + if (prefetched_oid_count == 0) /* make sure next/max is set, or + * reload */ + GetNewObjectId(&pass_oid); + + /* ---------------- + * If we are below prefetched limits, do nothing + * ---------------- + */ + + if (assigned_oid < next_prefetched_oid) + return; + + /* ---------------- + * If we are here, we are coming from a 'copy from' with oid's + * + * If we are in the prefetched oid range, just bump it up + * + * ---------------- + */ + + if (assigned_oid <= next_prefetched_oid + prefetched_oid_count - 1) + { + prefetched_oid_count -= assigned_oid - next_prefetched_oid + 1; + next_prefetched_oid = assigned_oid + 1; + return; + } + + /* ---------------- + * We have exceeded the prefetch oid range + * + * We should lock the database and kill all other backends + * but we are loading oid's that we can not guarantee are unique + * anyway, so we must rely on the user + * + * + * We now: + * set the variable relation with the new max oid + * force the backend to reload its oid cache + * + * We use the oid cache so we don't have to update the variable + * relation every time + * + * ---------------- + */ + + pass_oid = assigned_oid; + VariableRelationPutNextOid(&pass_oid); /* not modified */ + prefetched_oid_count = 0; /* force reload */ + pass_oid = assigned_oid; + GetNewObjectId(&pass_oid); /* throw away returned oid */ + +} diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 5ac4a42c7c7..da32570d87b 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -1,138 +1,138 @@ /*------------------------------------------------------------------------- * * xact.c-- - * top level transaction system support routines + * top level transaction system support routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.13 1997/08/29 09:02:11 vadim Exp $ - * + * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.14 1997/09/07 04:39:38 momjian Exp $ + * * NOTES - * Transaction aborts can now occur two ways: + * Transaction aborts can now occur two ways: * - * 1) system dies from some internal cause (Assert, etc..) - * 2) user types abort + * 1) system dies from some internal cause (Assert, etc..) + * 2) user types abort * - * These two cases used to be treated identically, but now - * we need to distinguish them. Why? consider the following - * two situatuons: + * These two cases used to be treated identically, but now + * we need to distinguish them. Why? consider the following + * two situatuons: * - * case 1 case 2 - * ------ ------ - * 1) user types BEGIN 1) user types BEGIN - * 2) user does something 2) user does something - * 3) user does not like what 3) system aborts for some reason - * she shes and types ABORT + * case 1 case 2 + * ------ ------ + * 1) user types BEGIN 1) user types BEGIN + * 2) user does something 2) user does something + * 3) user does not like what 3) system aborts for some reason + * she shes and types ABORT * - * In case 1, we want to abort the transaction and return to the - * default state. In case 2, there may be more commands coming - * our way which are part of the same transaction block and we have - * to ignore these commands until we see an END transaction. - * (or an ABORT! --djm) + * In case 1, we want to abort the transaction and return to the + * default state. In case 2, there may be more commands coming + * our way which are part of the same transaction block and we have + * to ignore these commands until we see an END transaction. + * (or an ABORT! --djm) * - * Internal aborts are now handled by AbortTransactionBlock(), just as - * they always have been, and user aborts are now handled by - * UserAbortTransactionBlock(). Both of them rely on AbortTransaction() - * to do all the real work. The only difference is what state we - * enter after AbortTransaction() does it's work: - * - * * AbortTransactionBlock() leaves us in TBLOCK_ABORT and - * * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT - * - * NOTES - * This file is an attempt at a redesign of the upper layer - * of the V1 transaction system which was too poorly thought - * out to describe. This new system hopes to be both simpler - * in design, simpler to extend and needs to contain added - * functionality to solve problems beyond the scope of the V1 - * system. (In particuler, communication of transaction - * information between parallel backends has to be supported) + * Internal aborts are now handled by AbortTransactionBlock(), just as + * they always have been, and user aborts are now handled by + * UserAbortTransactionBlock(). Both of them rely on AbortTransaction() + * to do all the real work. The only difference is what state we + * enter after AbortTransaction() does it's work: * - * The essential aspects of the transaction system are: + * * AbortTransactionBlock() leaves us in TBLOCK_ABORT and + * * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT * - * o transaction id generation - * o transaction log updating - * o memory cleanup - * o cache invalidation - * o lock cleanup + * NOTES + * This file is an attempt at a redesign of the upper layer + * of the V1 transaction system which was too poorly thought + * out to describe. This new system hopes to be both simpler + * in design, simpler to extend and needs to contain added + * functionality to solve problems beyond the scope of the V1 + * system. (In particuler, communication of transaction + * information between parallel backends has to be supported) * - * Hence, the functional division of the transaction code is - * based on what of the above things need to be done during - * a start/commit/abort transaction. For instance, the - * routine AtCommit_Memory() takes care of all the memory - * cleanup stuff done at commit time. + * The essential aspects of the transaction system are: * - * The code is layered as follows: + * o transaction id generation + * o transaction log updating + * o memory cleanup + * o cache invalidation + * o lock cleanup * - * StartTransaction - * CommitTransaction - * AbortTransaction - * UserAbortTransaction + * Hence, the functional division of the transaction code is + * based on what of the above things need to be done during + * a start/commit/abort transaction. For instance, the + * routine AtCommit_Memory() takes care of all the memory + * cleanup stuff done at commit time. * - * are provided to do the lower level work like recording - * the transaction status in the log and doing memory cleanup. - * above these routines are another set of functions: + * The code is layered as follows: * - * StartTransactionCommand - * CommitTransactionCommand - * AbortCurrentTransaction + * StartTransaction + * CommitTransaction + * AbortTransaction + * UserAbortTransaction * - * These are the routines used in the postgres main processing - * loop. They are sensitive to the current transaction block state - * and make calls to the lower level routines appropriately. + * are provided to do the lower level work like recording + * the transaction status in the log and doing memory cleanup. + * above these routines are another set of functions: * - * Support for transaction blocks is provided via the functions: + * StartTransactionCommand + * CommitTransactionCommand + * AbortCurrentTransaction * - * StartTransactionBlock - * CommitTransactionBlock - * AbortTransactionBlock + * These are the routines used in the postgres main processing + * loop. They are sensitive to the current transaction block state + * and make calls to the lower level routines appropriately. + * + * Support for transaction blocks is provided via the functions: + * + * StartTransactionBlock + * CommitTransactionBlock + * AbortTransactionBlock * - * These are invoked only in responce to a user "BEGIN", "END", - * or "ABORT" command. The tricky part about these functions - * is that they are called within the postgres main loop, in between - * the StartTransactionCommand() and CommitTransactionCommand(). + * These are invoked only in responce to a user "BEGIN", "END", + * or "ABORT" command. The tricky part about these functions + * is that they are called within the postgres main loop, in between + * the StartTransactionCommand() and CommitTransactionCommand(). * - * For example, consider the following sequence of user commands: + * For example, consider the following sequence of user commands: * - * 1) begin - * 2) retrieve (foo.all) - * 3) append foo (bar = baz) - * 4) end + * 1) begin + * 2) retrieve (foo.all) + * 3) append foo (bar = baz) + * 4) end * - * in the main processing loop, this results in the following - * transaction sequence: + * in the main processing loop, this results in the following + * transaction sequence: * - * / StartTransactionCommand(); - * 1) / ProcessUtility(); << begin - * \ StartTransactionBlock(); - * \ CommitTransactionCommand(); + * / StartTransactionCommand(); + * 1) / ProcessUtility(); << begin + * \ StartTransactionBlock(); + * \ CommitTransactionCommand(); * - * / StartTransactionCommand(); - * 2) < ProcessQuery(); << retrieve (foo.all) - * \ CommitTransactionCommand(); + * / StartTransactionCommand(); + * 2) < ProcessQuery(); << retrieve (foo.all) + * \ CommitTransactionCommand(); * - * / StartTransactionCommand(); - * 3) < ProcessQuery(); << append foo (bar = baz) - * \ CommitTransactionCommand(); + * / StartTransactionCommand(); + * 3) < ProcessQuery(); << append foo (bar = baz) + * \ CommitTransactionCommand(); * - * / StartTransactionCommand(); - * 4) / ProcessUtility(); << end - * \ CommitTransactionBlock(); - * \ CommitTransactionCommand(); + * / StartTransactionCommand(); + * 4) / ProcessUtility(); << end + * \ CommitTransactionBlock(); + * \ CommitTransactionCommand(); * - * The point of this example is to demonstrate the need for - * StartTransactionCommand() and CommitTransactionCommand() to - * be state smart -- they should do nothing in between the calls - * to StartTransactionBlock() and EndTransactionBlock() and - * outside these calls they need to do normal start/commit - * processing. + * The point of this example is to demonstrate the need for + * StartTransactionCommand() and CommitTransactionCommand() to + * be state smart -- they should do nothing in between the calls + * to StartTransactionBlock() and EndTransactionBlock() and + * outside these calls they need to do normal start/commit + * processing. * - * Furthermore, suppose the "retrieve (foo.all)" caused an abort - * condition. We would then want to abort the transaction and - * ignore all subsequent commands up to the "end". - * -cim 3/23/90 + * Furthermore, suppose the "retrieve (foo.all)" caused an abort + * condition. We would then want to abort the transaction and + * ignore all subsequent commands up to the "end". + * -cim 3/23/90 * *------------------------------------------------------------------------- */ @@ -142,7 +142,7 @@ #include <access/xact.h> #include <utils/inval.h> #include <utils/portal.h> -#include <access/transam.h> +#include <access/transam.h> #include <storage/proc.h> #include <utils/mcxt.h> #include <catalog/heap.h> @@ -151,683 +151,700 @@ #include <commands/async.h> #include <commands/sequence.h> -static void AbortTransaction(void); -static void AtAbort_Cache(void); -static void AtAbort_Locks(void); -static void AtAbort_Memory(void); -static void AtCommit_Cache(void); -static void AtCommit_Locks(void); -static void AtCommit_Memory(void); -static void AtStart_Cache(void); -static void AtStart_Locks(void); -static void AtStart_Memory(void); -static void CommitTransaction(void); -static void RecordTransactionAbort(void); -static void RecordTransactionCommit(void); -static void StartTransaction(void); +static void AbortTransaction(void); +static void AtAbort_Cache(void); +static void AtAbort_Locks(void); +static void AtAbort_Memory(void); +static void AtCommit_Cache(void); +static void AtCommit_Locks(void); +static void AtCommit_Memory(void); +static void AtStart_Cache(void); +static void AtStart_Locks(void); +static void AtStart_Memory(void); +static void CommitTransaction(void); +static void RecordTransactionAbort(void); +static void RecordTransactionCommit(void); +static void StartTransaction(void); /* ---------------- - * global variables holding the current transaction state. + * global variables holding the current transaction state. * - * Note: when we are running several slave processes, the - * current transaction state data is copied into shared memory - * and the CurrentTransactionState pointer changed to - * point to the shared copy. All this occurrs in slaves.c + * Note: when we are running several slave processes, the + * current transaction state data is copied into shared memory + * and the CurrentTransactionState pointer changed to + * point to the shared copy. All this occurrs in slaves.c * ---------------- */ TransactionStateData CurrentTransactionStateData = { - 0, /* transaction id */ - FirstCommandId, /* command id */ - 0x0, /* start time */ - TRANS_DEFAULT, /* transaction state */ - TBLOCK_DEFAULT /* transaction block state */ - }; + 0, /* transaction id */ + FirstCommandId, /* command id */ + 0x0, /* start time */ + TRANS_DEFAULT, /* transaction state */ + TBLOCK_DEFAULT /* transaction block state */ +}; TransactionState CurrentTransactionState = - &CurrentTransactionStateData; +&CurrentTransactionStateData; /* ---------------- - * info returned when the system is disabled + * info returned when the system is disabled * * Apparently a lot of this code is inherited from other prototype systems. * For DisabledStartTime, use a symbolic value to make the relationships clearer. * The old value of 1073741823 corresponds to a date in y2004, which is coming closer - * every day. It appears that if we return a value guaranteed larger than - * any real time associated with a transaction then comparisons in other - * modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97 + * every day. It appears that if we return a value guaranteed larger than + * any real time associated with a transaction then comparisons in other + * modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97 * - * Note: I have no idea what the significance of the - * 1073741823 in DisabledStartTime.. I just carried - * this over when converting things from the old - * V1 transaction system. -cim 3/18/90 + * Note: I have no idea what the significance of the + * 1073741823 in DisabledStartTime.. I just carried + * this over when converting things from the old + * V1 transaction system. -cim 3/18/90 * ---------------- */ -TransactionId DisabledTransactionId = (TransactionId)-1; - -CommandId DisabledCommandId = (CommandId) -1; - -AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */ - +TransactionId DisabledTransactionId = (TransactionId) - 1; + +CommandId DisabledCommandId = (CommandId) - 1; + +AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */ + /* ---------------- - * overflow flag + * overflow flag * ---------------- */ -bool CommandIdCounterOverflowFlag; - +bool CommandIdCounterOverflowFlag; + /* ---------------- - * catalog creation transaction bootstrapping flag. - * This should be eliminated and added to the transaction - * state stuff. -cim 3/19/90 + * catalog creation transaction bootstrapping flag. + * This should be eliminated and added to the transaction + * state stuff. -cim 3/19/90 * ---------------- */ -bool AMI_OVERRIDE = false; - +bool AMI_OVERRIDE = false; + /* ---------------------------------------------------------------- - * transaction state accessors + * transaction state accessors * ---------------------------------------------------------------- */ - + /* -------------------------------- - * TranactionFlushEnabled() - * SetTranactionFlushEnabled() + * TranactionFlushEnabled() + * SetTranactionFlushEnabled() * - * These are used to test and set the "TransactionFlushState" - * varable. If this variable is true (the default), then - * the system will flush all dirty buffers to disk at the end - * of each transaction. If false then we are assuming the - * buffer pool resides in stable main memory, in which case we - * only do writes as necessary. + * These are used to test and set the "TransactionFlushState" + * varable. If this variable is true (the default), then + * the system will flush all dirty buffers to disk at the end + * of each transaction. If false then we are assuming the + * buffer pool resides in stable main memory, in which case we + * only do writes as necessary. * -------------------------------- */ -static int TransactionFlushState = 1; +static int TransactionFlushState = 1; int TransactionFlushEnabled(void) -{ - return TransactionFlushState; +{ + return TransactionFlushState; } #ifdef NOT_USED void SetTransactionFlushEnabled(bool state) -{ - TransactionFlushState = (state == true); +{ + TransactionFlushState = (state == true); } + #endif /* -------------------------------- - * IsTransactionState + * IsTransactionState * - * This returns true if we are currently running a query - * within an executing transaction. + * This returns true if we are currently running a query + * within an executing transaction. * -------------------------------- */ bool IsTransactionState(void) { - TransactionState s = CurrentTransactionState; - - switch (s->state) { - case TRANS_DEFAULT: return false; - case TRANS_START: return true; - case TRANS_INPROGRESS: return true; - case TRANS_COMMIT: return true; - case TRANS_ABORT: return true; - case TRANS_DISABLED: return false; - } - /* - * Shouldn't get here, but lint is not happy with this... - */ - return(false); + TransactionState s = CurrentTransactionState; + + switch (s->state) + { + case TRANS_DEFAULT: + return false; + case TRANS_START: + return true; + case TRANS_INPROGRESS: + return true; + case TRANS_COMMIT: + return true; + case TRANS_ABORT: + return true; + case TRANS_DISABLED: + return false; + } + + /* + * Shouldn't get here, but lint is not happy with this... + */ + return (false); } /* -------------------------------- - * IsAbortedTransactionBlockState + * IsAbortedTransactionBlockState * - * This returns true if we are currently running a query - * within an aborted transaction block. + * This returns true if we are currently running a query + * within an aborted transaction block. * -------------------------------- */ bool IsAbortedTransactionBlockState() { - TransactionState s = CurrentTransactionState; - - if (s->blockState == TBLOCK_ABORT) - return true; - - return false; + TransactionState s = CurrentTransactionState; + + if (s->blockState == TBLOCK_ABORT) + return true; + + return false; } /* -------------------------------- - * OverrideTransactionSystem + * OverrideTransactionSystem * - * This is used to temporarily disable the transaction - * processing system in order to do initialization of - * the transaction system data structures and relations - * themselves. + * This is used to temporarily disable the transaction + * processing system in order to do initialization of + * the transaction system data structures and relations + * themselves. * -------------------------------- */ -int SavedTransactionState; +int SavedTransactionState; void OverrideTransactionSystem(bool flag) { - TransactionState s = CurrentTransactionState; - - if (flag == true) { - if (s->state == TRANS_DISABLED) - return; - - SavedTransactionState = s->state; - s->state = TRANS_DISABLED; - } else { - if (s->state != TRANS_DISABLED) - return; - - s->state = SavedTransactionState; - } + TransactionState s = CurrentTransactionState; + + if (flag == true) + { + if (s->state == TRANS_DISABLED) + return; + + SavedTransactionState = s->state; + s->state = TRANS_DISABLED; + } + else + { + if (s->state != TRANS_DISABLED) + return; + + s->state = SavedTransactionState; + } } /* -------------------------------- - * GetCurrentTransactionId + * GetCurrentTransactionId * - * This returns the id of the current transaction, or - * the id of the "disabled" transaction. + * This returns the id of the current transaction, or + * the id of the "disabled" transaction. * -------------------------------- */ TransactionId GetCurrentTransactionId() { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * if the transaction system is disabled, we return - * the special "disabled" transaction id. - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return (TransactionId) DisabledTransactionId; - - /* ---------------- - * otherwise return the current transaction id. - * ---------------- - */ - return (TransactionId) s->transactionIdData; + TransactionState s = CurrentTransactionState; + + /* ---------------- + * if the transaction system is disabled, we return + * the special "disabled" transaction id. + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return (TransactionId) DisabledTransactionId; + + /* ---------------- + * otherwise return the current transaction id. + * ---------------- + */ + return (TransactionId) s->transactionIdData; } /* -------------------------------- - * GetCurrentCommandId + * GetCurrentCommandId * -------------------------------- */ CommandId GetCurrentCommandId() { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * if the transaction system is disabled, we return - * the special "disabled" command id. - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return (CommandId) DisabledCommandId; - - return s->commandId; + TransactionState s = CurrentTransactionState; + + /* ---------------- + * if the transaction system is disabled, we return + * the special "disabled" command id. + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return (CommandId) DisabledCommandId; + + return s->commandId; } CommandId GetScanCommandId() { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * if the transaction system is disabled, we return - * the special "disabled" command id. - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return (CommandId) DisabledCommandId; - - return s->scanCommandId; + TransactionState s = CurrentTransactionState; + + /* ---------------- + * if the transaction system is disabled, we return + * the special "disabled" command id. + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return (CommandId) DisabledCommandId; + + return s->scanCommandId; } /* -------------------------------- - * GetCurrentTransactionStartTime + * GetCurrentTransactionStartTime * -------------------------------- */ AbsoluteTime GetCurrentTransactionStartTime() { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * if the transaction system is disabled, we return - * the special "disabled" starting time. - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return (AbsoluteTime) DisabledStartTime; - - return s->startTime; + TransactionState s = CurrentTransactionState; + + /* ---------------- + * if the transaction system is disabled, we return + * the special "disabled" starting time. + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return (AbsoluteTime) DisabledStartTime; + + return s->startTime; } /* -------------------------------- - * TransactionIdIsCurrentTransactionId + * TransactionIdIsCurrentTransactionId * -------------------------------- */ bool TransactionIdIsCurrentTransactionId(TransactionId xid) { - TransactionState s = CurrentTransactionState; - - if (AMI_OVERRIDE) - return false; - - return (bool) - TransactionIdEquals(xid, s->transactionIdData); + TransactionState s = CurrentTransactionState; + + if (AMI_OVERRIDE) + return false; + + return (bool) + TransactionIdEquals(xid, s->transactionIdData); } /* -------------------------------- - * CommandIdIsCurrentCommandId + * CommandIdIsCurrentCommandId * -------------------------------- */ bool CommandIdIsCurrentCommandId(CommandId cid) { - TransactionState s = CurrentTransactionState; - - if (AMI_OVERRIDE) - return false; - - return - (cid == s->commandId) ? true : false; + TransactionState s = CurrentTransactionState; + + if (AMI_OVERRIDE) + return false; + + return + (cid == s->commandId) ? true : false; } bool CommandIdGEScanCommandId(CommandId cid) { - TransactionState s = CurrentTransactionState; - - if (AMI_OVERRIDE) - return false; - - return - (cid >= s->scanCommandId) ? true : false; + TransactionState s = CurrentTransactionState; + + if (AMI_OVERRIDE) + return false; + + return + (cid >= s->scanCommandId) ? true : false; } /* -------------------------------- - * ClearCommandIdCounterOverflowFlag + * ClearCommandIdCounterOverflowFlag * -------------------------------- */ #ifdef NOT_USED void ClearCommandIdCounterOverflowFlag() { - CommandIdCounterOverflowFlag = false; + CommandIdCounterOverflowFlag = false; } + #endif /* -------------------------------- - * CommandCounterIncrement + * CommandCounterIncrement * -------------------------------- */ void CommandCounterIncrement() { - CurrentTransactionStateData.commandId += 1; - if (CurrentTransactionStateData.commandId == FirstCommandId) { - CommandIdCounterOverflowFlag = true; - elog(WARN, "You may only have 65535 commands per transaction"); - } - - CurrentTransactionStateData.scanCommandId = - CurrentTransactionStateData.commandId; - - /* make cache changes visible to me */ - AtCommit_Cache(); - AtStart_Cache(); + CurrentTransactionStateData.commandId += 1; + if (CurrentTransactionStateData.commandId == FirstCommandId) + { + CommandIdCounterOverflowFlag = true; + elog(WARN, "You may only have 65535 commands per transaction"); + } + + CurrentTransactionStateData.scanCommandId = + CurrentTransactionStateData.commandId; + + /* make cache changes visible to me */ + AtCommit_Cache(); + AtStart_Cache(); } -void -SetScanCommandId (CommandId savedId) +void +SetScanCommandId(CommandId savedId) { - CurrentTransactionStateData.scanCommandId = savedId; - + CurrentTransactionStateData.scanCommandId = savedId; + } /* ---------------------------------------------------------------- - * initialization stuff + * initialization stuff * ---------------------------------------------------------------- */ void InitializeTransactionSystem() { - InitializeTransactionLog(); + InitializeTransactionLog(); } /* ---------------------------------------------------------------- - * StartTransaction stuff + * StartTransaction stuff * ---------------------------------------------------------------- */ /* -------------------------------- - * AtStart_Cache + * AtStart_Cache * -------------------------------- */ static void -AtStart_Cache() +AtStart_Cache() { - DiscardInvalid(); + DiscardInvalid(); } /* -------------------------------- - * AtStart_Locks + * AtStart_Locks * -------------------------------- */ static void -AtStart_Locks() +AtStart_Locks() { - /* - * at present, it is unknown to me what belongs here -cim 3/18/90 - * - * There isn't anything to do at the start of a xact for locks. - * -mer 5/24/92 - */ + + /* + * at present, it is unknown to me what belongs here -cim 3/18/90 + * + * There isn't anything to do at the start of a xact for locks. -mer + * 5/24/92 + */ } /* -------------------------------- - * AtStart_Memory + * AtStart_Memory * -------------------------------- */ static void -AtStart_Memory() +AtStart_Memory() { - Portal portal; - MemoryContext portalContext; - - /* ---------------- - * get the blank portal and its memory context - * ---------------- - */ - portal = GetPortalByName(NULL); - portalContext = (MemoryContext) PortalGetHeapMemory(portal); - - /* ---------------- - * tell system to allocate in the blank portal context - * ---------------- - */ - MemoryContextSwitchTo(portalContext); - StartPortalAllocMode(DefaultAllocMode, 0); + Portal portal; + MemoryContext portalContext; + + /* ---------------- + * get the blank portal and its memory context + * ---------------- + */ + portal = GetPortalByName(NULL); + portalContext = (MemoryContext) PortalGetHeapMemory(portal); + + /* ---------------- + * tell system to allocate in the blank portal context + * ---------------- + */ + MemoryContextSwitchTo(portalContext); + StartPortalAllocMode(DefaultAllocMode, 0); } /* ---------------------------------------------------------------- - * CommitTransaction stuff + * CommitTransaction stuff * ---------------------------------------------------------------- */ /* -------------------------------- - * RecordTransactionCommit + * RecordTransactionCommit * - * Note: the two calls to BufferManagerFlush() exist to ensure - * that data pages are written before log pages. These - * explicit calls should be replaced by a more efficient - * ordered page write scheme in the buffer manager - * -cim 3/18/90 + * Note: the two calls to BufferManagerFlush() exist to ensure + * that data pages are written before log pages. These + * explicit calls should be replaced by a more efficient + * ordered page write scheme in the buffer manager + * -cim 3/18/90 * -------------------------------- */ static void -RecordTransactionCommit() +RecordTransactionCommit() { - TransactionId xid; - int leak; - - /* ---------------- - * get the current transaction id - * ---------------- - */ - xid = GetCurrentTransactionId(); - - /* ---------------- - * flush the buffer manager pages. Note: if we have stable - * main memory, dirty shared buffers are not flushed - * plai 8/7/90 - * ---------------- - */ - leak = BufferPoolCheckLeak(); - FlushBufferPool(!TransactionFlushEnabled()); - if (leak) ResetBufferPool(); - - /* ---------------- - * have the transaction access methods record the status - * of this transaction id in the pg_log / pg_time relations. - * ---------------- - */ - TransactionIdCommit(xid); - - /* ---------------- - * Now write the log/time info to the disk too. - * ---------------- - */ - leak = BufferPoolCheckLeak(); - FlushBufferPool(!TransactionFlushEnabled()); - if (leak) ResetBufferPool(); + TransactionId xid; + int leak; + + /* ---------------- + * get the current transaction id + * ---------------- + */ + xid = GetCurrentTransactionId(); + + /* ---------------- + * flush the buffer manager pages. Note: if we have stable + * main memory, dirty shared buffers are not flushed + * plai 8/7/90 + * ---------------- + */ + leak = BufferPoolCheckLeak(); + FlushBufferPool(!TransactionFlushEnabled()); + if (leak) + ResetBufferPool(); + + /* ---------------- + * have the transaction access methods record the status + * of this transaction id in the pg_log / pg_time relations. + * ---------------- + */ + TransactionIdCommit(xid); + + /* ---------------- + * Now write the log/time info to the disk too. + * ---------------- + */ + leak = BufferPoolCheckLeak(); + FlushBufferPool(!TransactionFlushEnabled()); + if (leak) + ResetBufferPool(); } /* -------------------------------- - * AtCommit_Cache + * AtCommit_Cache * -------------------------------- */ static void AtCommit_Cache() { - /* ---------------- - * Make catalog changes visible to me for the next command. - * Other backends will not process my invalidation messages until - * after I commit and free my locks--though they will do - * unnecessary work if I abort. - * ---------------- - */ - RegisterInvalid(true); + /* ---------------- + * Make catalog changes visible to me for the next command. + * Other backends will not process my invalidation messages until + * after I commit and free my locks--though they will do + * unnecessary work if I abort. + * ---------------- + */ + RegisterInvalid(true); } /* -------------------------------- - * AtCommit_Locks + * AtCommit_Locks * -------------------------------- */ static void -AtCommit_Locks() +AtCommit_Locks() { - /* ---------------- - * XXX What if ProcReleaseLocks fails? (race condition?) - * - * Then you're up a creek! -mer 5/24/92 - * ---------------- - */ - ProcReleaseLocks(); + /* ---------------- + * XXX What if ProcReleaseLocks fails? (race condition?) + * + * Then you're up a creek! -mer 5/24/92 + * ---------------- + */ + ProcReleaseLocks(); } /* -------------------------------- - * AtCommit_Memory + * AtCommit_Memory * -------------------------------- */ static void -AtCommit_Memory() +AtCommit_Memory() { - /* ---------------- - * now that we're "out" of a transaction, have the - * system allocate things in the top memory context instead - * of the blank portal memory context. - * ---------------- - */ - EndPortalAllocMode(); - MemoryContextSwitchTo(TopMemoryContext); + /* ---------------- + * now that we're "out" of a transaction, have the + * system allocate things in the top memory context instead + * of the blank portal memory context. + * ---------------- + */ + EndPortalAllocMode(); + MemoryContextSwitchTo(TopMemoryContext); } /* ---------------------------------------------------------------- - * AbortTransaction stuff + * AbortTransaction stuff * ---------------------------------------------------------------- */ /* -------------------------------- - * RecordTransactionAbort + * RecordTransactionAbort * -------------------------------- */ static void -RecordTransactionAbort() +RecordTransactionAbort() { - TransactionId xid; - - /* ---------------- - * get the current transaction id - * ---------------- - */ - xid = GetCurrentTransactionId(); - - /* ---------------- - * have the transaction access methods record the status - * of this transaction id in the pg_log / pg_time relations. - * ---------------- - */ - TransactionIdAbort(xid); - - /* ---------------- - * flush the buffer manager pages. Note: if we have stable - * main memory, dirty shared buffers are not flushed - * plai 8/7/90 - * ---------------- - */ - ResetBufferPool(); + TransactionId xid; + + /* ---------------- + * get the current transaction id + * ---------------- + */ + xid = GetCurrentTransactionId(); + + /* ---------------- + * have the transaction access methods record the status + * of this transaction id in the pg_log / pg_time relations. + * ---------------- + */ + TransactionIdAbort(xid); + + /* ---------------- + * flush the buffer manager pages. Note: if we have stable + * main memory, dirty shared buffers are not flushed + * plai 8/7/90 + * ---------------- + */ + ResetBufferPool(); } /* -------------------------------- - * AtAbort_Cache + * AtAbort_Cache * -------------------------------- */ static void -AtAbort_Cache() +AtAbort_Cache() { - RegisterInvalid(false); + RegisterInvalid(false); } /* -------------------------------- - * AtAbort_Locks + * AtAbort_Locks * -------------------------------- */ static void -AtAbort_Locks() +AtAbort_Locks() { - /* ---------------- - * XXX What if ProcReleaseLocks() fails? (race condition?) - * - * Then you're up a creek without a paddle! -mer - * ---------------- - */ - ProcReleaseLocks(); + /* ---------------- + * XXX What if ProcReleaseLocks() fails? (race condition?) + * + * Then you're up a creek without a paddle! -mer + * ---------------- + */ + ProcReleaseLocks(); } /* -------------------------------- - * AtAbort_Memory + * AtAbort_Memory * -------------------------------- */ static void -AtAbort_Memory() +AtAbort_Memory() { - /* ---------------- - * after doing an abort transaction, make certain the - * system uses the top memory context rather then the - * portal memory context (until the next transaction). - * ---------------- - */ - MemoryContextSwitchTo(TopMemoryContext); + /* ---------------- + * after doing an abort transaction, make certain the + * system uses the top memory context rather then the + * portal memory context (until the next transaction). + * ---------------- + */ + MemoryContextSwitchTo(TopMemoryContext); } /* ---------------------------------------------------------------- - * interface routines + * interface routines * ---------------------------------------------------------------- */ /* -------------------------------- - * StartTransaction + * StartTransaction * * -------------------------------- */ static void StartTransaction() { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * Check the current transaction state. If the transaction system - * is switched off, or if we're already in a transaction, do nothing. - * We're already in a transaction when the monitor sends a null - * command to the backend to flush the comm channel. This is a - * hacky fix to a communications problem, and we keep having to - * deal with it here. We should fix the comm channel code. mao 080891 - * ---------------- - */ - if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS) - return; - - /* ---------------- - * set the current transaction state information - * appropriately during start processing - * ---------------- - */ - s->state = TRANS_START; - - /* ---------------- - * generate a new transaction id - * ---------------- - */ - GetNewTransactionId(&(s->transactionIdData)); - - /* ---------------- - * initialize current transaction state fields - * ---------------- - */ - s->commandId = FirstCommandId; - s->scanCommandId = FirstCommandId; - s->startTime = GetCurrentAbsoluteTime(); - - /* ---------------- - * initialize the various transaction subsystems - * ---------------- - */ - AtStart_Cache(); - AtStart_Locks(); - AtStart_Memory(); - - /* -------------- - initialize temporary relations list - the tempRelList is a list of temporary relations that - are created in the course of the transactions - they need to be destroyed properly at the end of the transactions - */ - InitTempRelList(); - - /* ---------------- - * done with start processing, set current transaction - * state to "in progress" - * ---------------- - */ - s->state = TRANS_INPROGRESS; - - /* - * Let others to know about current transaction is in progress - * - vadim 11/26/96 - */ - if ( MyProc != (PROC*) NULL ) - MyProc->xid = s->transactionIdData; + TransactionState s = CurrentTransactionState; + + /* ---------------- + * Check the current transaction state. If the transaction system + * is switched off, or if we're already in a transaction, do nothing. + * We're already in a transaction when the monitor sends a null + * command to the backend to flush the comm channel. This is a + * hacky fix to a communications problem, and we keep having to + * deal with it here. We should fix the comm channel code. mao 080891 + * ---------------- + */ + if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS) + return; + + /* ---------------- + * set the current transaction state information + * appropriately during start processing + * ---------------- + */ + s->state = TRANS_START; + + /* ---------------- + * generate a new transaction id + * ---------------- + */ + GetNewTransactionId(&(s->transactionIdData)); + + /* ---------------- + * initialize current transaction state fields + * ---------------- + */ + s->commandId = FirstCommandId; + s->scanCommandId = FirstCommandId; + s->startTime = GetCurrentAbsoluteTime(); + + /* ---------------- + * initialize the various transaction subsystems + * ---------------- + */ + AtStart_Cache(); + AtStart_Locks(); + AtStart_Memory(); + + /* -------------- + initialize temporary relations list + the tempRelList is a list of temporary relations that + are created in the course of the transactions + they need to be destroyed properly at the end of the transactions + */ + InitTempRelList(); + + /* ---------------- + * done with start processing, set current transaction + * state to "in progress" + * ---------------- + */ + s->state = TRANS_INPROGRESS; + + /* + * Let others to know about current transaction is in progress - vadim + * 11/26/96 + */ + if (MyProc != (PROC *) NULL) + MyProc->xid = s->transactionIdData; } @@ -838,591 +855,604 @@ StartTransaction() bool CurrentXactInProgress() { - return (CurrentTransactionState->state == TRANS_INPROGRESS); + return (CurrentTransactionState->state == TRANS_INPROGRESS); } /* -------------------------------- - * CommitTransaction + * CommitTransaction * * -------------------------------- */ static void CommitTransaction() { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * check the current transaction state - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return; - - if (s->state != TRANS_INPROGRESS) - elog(NOTICE, "CommitTransaction and not in in-progress state "); - - /* ---------------- - * set the current transaction state information - * appropriately during the abort processing - * ---------------- - */ - s->state = TRANS_COMMIT; - - /* ---------------- - * do commit processing - * ---------------- - */ - CloseSequences (); - DestroyTempRels(); - AtEOXact_portals(); - RecordTransactionCommit(); - RelationPurgeLocalRelation(true); - AtCommit_Cache(); - AtCommit_Locks(); - AtCommit_Memory(); - - /* ---------------- - * done with commit processing, set current transaction - * state back to default - * ---------------- - */ - s->state = TRANS_DEFAULT; - { /* want this after commit */ - if (IsNormalProcessingMode()) - Async_NotifyAtCommit(); - } - - /* - * Let others to know about no transaction in progress - * - vadim 11/26/96 - */ - if ( MyProc != (PROC*) NULL ) - MyProc->xid = InvalidTransactionId; -} + TransactionState s = CurrentTransactionState; -/* -------------------------------- - * AbortTransaction - * - * -------------------------------- - */ -static void -AbortTransaction() -{ - TransactionState s = CurrentTransactionState; - - /* - * Let others to know about no transaction in progress - * - vadim 11/26/96 - */ - if ( MyProc != (PROC*) NULL ) - MyProc->xid = InvalidTransactionId; - - /* ---------------- - * check the current transaction state - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return; - - if (s->state != TRANS_INPROGRESS) - elog(NOTICE, "AbortTransaction and not in in-progress state "); - - /* ---------------- - * set the current transaction state information - * appropriately during the abort processing - * ---------------- - */ - s->state = TRANS_ABORT; - - /* ---------------- - * do abort processing - * ---------------- - */ - CloseSequences (); - AtEOXact_portals(); - RecordTransactionAbort(); - RelationPurgeLocalRelation(false); - DestroyTempRels(); - AtAbort_Cache(); - AtAbort_Locks(); - AtAbort_Memory(); - - /* ---------------- - * done with abort processing, set current transaction - * state back to default - * ---------------- - */ - s->state = TRANS_DEFAULT; - { - /* We need to do this in case another process notified us while - we are in the middle of an aborted transaction. We need to - notify our frontend after we finish the current transaction. - -- jw, 1/3/94 - */ - if (IsNormalProcessingMode()) - Async_NotifyAtAbort(); - } -} - -/* -------------------------------- - * StartTransactionCommand - * -------------------------------- - */ -void -StartTransactionCommand() -{ - TransactionState s = CurrentTransactionState; - - switch(s->blockState) { /* ---------------- - * if we aren't in a transaction block, we - * just do our usual start transaction. + * check the current transaction state * ---------------- */ - case TBLOCK_DEFAULT: - StartTransaction(); - break; - - /* ---------------- - * We should never experience this -- if we do it - * means the BEGIN state was not changed in the previous - * CommitTransactionCommand(). If we get it, we print - * a warning and change to the in-progress state. - * ---------------- - */ - case TBLOCK_BEGIN: - elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN"); - s->blockState = TBLOCK_INPROGRESS; - break; - + if (s->state == TRANS_DISABLED) + return; + + if (s->state != TRANS_INPROGRESS) + elog(NOTICE, "CommitTransaction and not in in-progress state "); + /* ---------------- - * This is the case when are somewhere in a transaction - * block and about to start a new command. For now we - * do nothing but someday we may do command-local resource - * initialization. + * set the current transaction state information + * appropriately during the abort processing * ---------------- */ - case TBLOCK_INPROGRESS: - break; - + s->state = TRANS_COMMIT; + /* ---------------- - * As with BEGIN, we should never experience this -- - * if we do it means the END state was not changed in the - * previous CommitTransactionCommand(). If we get it, we - * print a warning, commit the transaction, start a new - * transaction and change to the default state. + * do commit processing * ---------------- */ - case TBLOCK_END: - elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END"); - s->blockState = TBLOCK_DEFAULT; - CommitTransaction(); - StartTransaction(); - break; - + CloseSequences(); + DestroyTempRels(); + AtEOXact_portals(); + RecordTransactionCommit(); + RelationPurgeLocalRelation(true); + AtCommit_Cache(); + AtCommit_Locks(); + AtCommit_Memory(); + /* ---------------- - * Here we are in the middle of a transaction block but - * one of the commands caused an abort so we do nothing - * but remain in the abort state. Eventually we will get - * to the "END TRANSACTION" which will set things straight. + * done with commit processing, set current transaction + * state back to default * ---------------- */ - case TBLOCK_ABORT: - break; - - /* ---------------- - * This means we somehow aborted and the last call to - * CommitTransactionCommand() didn't clear the state so - * we remain in the ENDABORT state and mabey next time - * we get to CommitTransactionCommand() the state will - * get reset to default. - * ---------------- + s->state = TRANS_DEFAULT; + { /* want this after commit */ + if (IsNormalProcessingMode()) + Async_NotifyAtCommit(); + } + + /* + * Let others to know about no transaction in progress - vadim + * 11/26/96 */ - case TBLOCK_ENDABORT: - elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT"); - break; - } + if (MyProc != (PROC *) NULL) + MyProc->xid = InvalidTransactionId; } + /* -------------------------------- - * CommitTransactionCommand + * AbortTransaction + * * -------------------------------- */ -void -CommitTransactionCommand() +static void +AbortTransaction() { - TransactionState s = CurrentTransactionState; - - switch(s->blockState) { - /* ---------------- - * if we aren't in a transaction block, we - * just do our usual transaction commit - * ---------------- - */ - case TBLOCK_DEFAULT: - CommitTransaction(); - break; - - /* ---------------- - * This is the case right after we get a "BEGIN TRANSACTION" - * command, but the user hasn't done anything else yet, so - * we change to the "transaction block in progress" state - * and return. - * ---------------- + TransactionState s = CurrentTransactionState; + + /* + * Let others to know about no transaction in progress - vadim + * 11/26/96 */ - case TBLOCK_BEGIN: - s->blockState = TBLOCK_INPROGRESS; - break; - + if (MyProc != (PROC *) NULL) + MyProc->xid = InvalidTransactionId; + /* ---------------- - * This is the case when we have finished executing a command - * someplace within a transaction block. We increment the - * command counter and return. Someday we may free resources - * local to the command. - * - * That someday is today, at least for memory allocated by - * command in the BlankPortal' HeapMemory context. - * - vadim 03/25/97 + * check the current transaction state * ---------------- */ - case TBLOCK_INPROGRESS: - CommandCounterIncrement(); -#ifdef TBL_FREE_CMD_MEMORY - EndPortalAllocMode (); - StartPortalAllocMode (DefaultAllocMode, 0); -#endif - break; - + if (s->state == TRANS_DISABLED) + return; + + if (s->state != TRANS_INPROGRESS) + elog(NOTICE, "AbortTransaction and not in in-progress state "); + /* ---------------- - * This is the case when we just got the "END TRANSACTION" - * statement, so we go back to the default state and - * commit the transaction. + * set the current transaction state information + * appropriately during the abort processing * ---------------- */ - case TBLOCK_END: - s->blockState = TBLOCK_DEFAULT; - CommitTransaction(); - break; - + s->state = TRANS_ABORT; + /* ---------------- - * Here we are in the middle of a transaction block but - * one of the commands caused an abort so we do nothing - * but remain in the abort state. Eventually we will get - * to the "END TRANSACTION" which will set things straight. + * do abort processing * ---------------- */ - case TBLOCK_ABORT: - break; - + CloseSequences(); + AtEOXact_portals(); + RecordTransactionAbort(); + RelationPurgeLocalRelation(false); + DestroyTempRels(); + AtAbort_Cache(); + AtAbort_Locks(); + AtAbort_Memory(); + /* ---------------- - * Here we were in an aborted transaction block which - * just processed the "END TRANSACTION" command from the - * user, so now we return the to default state. + * done with abort processing, set current transaction + * state back to default * ---------------- */ - case TBLOCK_ENDABORT: - s->blockState = TBLOCK_DEFAULT; - break; - } + s->state = TRANS_DEFAULT; + { + + /* + * We need to do this in case another process notified us while we + * are in the middle of an aborted transaction. We need to notify + * our frontend after we finish the current transaction. -- jw, + * 1/3/94 + */ + if (IsNormalProcessingMode()) + Async_NotifyAtAbort(); + } +} + +/* -------------------------------- + * StartTransactionCommand + * -------------------------------- + */ +void +StartTransactionCommand() +{ + TransactionState s = CurrentTransactionState; + + switch (s->blockState) + { + /* ---------------- + * if we aren't in a transaction block, we + * just do our usual start transaction. + * ---------------- + */ + case TBLOCK_DEFAULT: + StartTransaction(); + break; + + /* ---------------- + * We should never experience this -- if we do it + * means the BEGIN state was not changed in the previous + * CommitTransactionCommand(). If we get it, we print + * a warning and change to the in-progress state. + * ---------------- + */ + case TBLOCK_BEGIN: + elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN"); + s->blockState = TBLOCK_INPROGRESS; + break; + + /* ---------------- + * This is the case when are somewhere in a transaction + * block and about to start a new command. For now we + * do nothing but someday we may do command-local resource + * initialization. + * ---------------- + */ + case TBLOCK_INPROGRESS: + break; + + /* ---------------- + * As with BEGIN, we should never experience this -- + * if we do it means the END state was not changed in the + * previous CommitTransactionCommand(). If we get it, we + * print a warning, commit the transaction, start a new + * transaction and change to the default state. + * ---------------- + */ + case TBLOCK_END: + elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END"); + s->blockState = TBLOCK_DEFAULT; + CommitTransaction(); + StartTransaction(); + break; + + /* ---------------- + * Here we are in the middle of a transaction block but + * one of the commands caused an abort so we do nothing + * but remain in the abort state. Eventually we will get + * to the "END TRANSACTION" which will set things straight. + * ---------------- + */ + case TBLOCK_ABORT: + break; + + /* ---------------- + * This means we somehow aborted and the last call to + * CommitTransactionCommand() didn't clear the state so + * we remain in the ENDABORT state and mabey next time + * we get to CommitTransactionCommand() the state will + * get reset to default. + * ---------------- + */ + case TBLOCK_ENDABORT: + elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT"); + break; + } +} + +/* -------------------------------- + * CommitTransactionCommand + * -------------------------------- + */ +void +CommitTransactionCommand() +{ + TransactionState s = CurrentTransactionState; + + switch (s->blockState) + { + /* ---------------- + * if we aren't in a transaction block, we + * just do our usual transaction commit + * ---------------- + */ + case TBLOCK_DEFAULT: + CommitTransaction(); + break; + + /* ---------------- + * This is the case right after we get a "BEGIN TRANSACTION" + * command, but the user hasn't done anything else yet, so + * we change to the "transaction block in progress" state + * and return. + * ---------------- + */ + case TBLOCK_BEGIN: + s->blockState = TBLOCK_INPROGRESS; + break; + + /* ---------------- + * This is the case when we have finished executing a command + * someplace within a transaction block. We increment the + * command counter and return. Someday we may free resources + * local to the command. + * + * That someday is today, at least for memory allocated by + * command in the BlankPortal' HeapMemory context. + * - vadim 03/25/97 + * ---------------- + */ + case TBLOCK_INPROGRESS: + CommandCounterIncrement(); +#ifdef TBL_FREE_CMD_MEMORY + EndPortalAllocMode(); + StartPortalAllocMode(DefaultAllocMode, 0); +#endif + break; + + /* ---------------- + * This is the case when we just got the "END TRANSACTION" + * statement, so we go back to the default state and + * commit the transaction. + * ---------------- + */ + case TBLOCK_END: + s->blockState = TBLOCK_DEFAULT; + CommitTransaction(); + break; + + /* ---------------- + * Here we are in the middle of a transaction block but + * one of the commands caused an abort so we do nothing + * but remain in the abort state. Eventually we will get + * to the "END TRANSACTION" which will set things straight. + * ---------------- + */ + case TBLOCK_ABORT: + break; + + /* ---------------- + * Here we were in an aborted transaction block which + * just processed the "END TRANSACTION" command from the + * user, so now we return the to default state. + * ---------------- + */ + case TBLOCK_ENDABORT: + s->blockState = TBLOCK_DEFAULT; + break; + } } /* -------------------------------- - * AbortCurrentTransaction + * AbortCurrentTransaction * -------------------------------- */ void AbortCurrentTransaction() { - TransactionState s = CurrentTransactionState; - - switch(s->blockState) { - /* ---------------- - * if we aren't in a transaction block, we - * just do our usual abort transaction. - * ---------------- - */ - case TBLOCK_DEFAULT: - AbortTransaction(); - break; - - /* ---------------- - * If we are in the TBLOCK_BEGIN it means something - * screwed up right after reading "BEGIN TRANSACTION" - * so we enter the abort state. Eventually an "END - * TRANSACTION" will fix things. - * ---------------- - */ - case TBLOCK_BEGIN: - s->blockState = TBLOCK_ABORT; - AbortTransaction(); - break; - + TransactionState s = CurrentTransactionState; + + switch (s->blockState) + { + /* ---------------- + * if we aren't in a transaction block, we + * just do our usual abort transaction. + * ---------------- + */ + case TBLOCK_DEFAULT: + AbortTransaction(); + break; + + /* ---------------- + * If we are in the TBLOCK_BEGIN it means something + * screwed up right after reading "BEGIN TRANSACTION" + * so we enter the abort state. Eventually an "END + * TRANSACTION" will fix things. + * ---------------- + */ + case TBLOCK_BEGIN: + s->blockState = TBLOCK_ABORT; + AbortTransaction(); + break; + + /* ---------------- + * This is the case when are somewhere in a transaction + * block which aborted so we abort the transaction and + * set the ABORT state. Eventually an "END TRANSACTION" + * will fix things and restore us to a normal state. + * ---------------- + */ + case TBLOCK_INPROGRESS: + s->blockState = TBLOCK_ABORT; + AbortTransaction(); + break; + + /* ---------------- + * Here, the system was fouled up just after the + * user wanted to end the transaction block so we + * abort the transaction and put us back into the + * default state. + * ---------------- + */ + case TBLOCK_END: + s->blockState = TBLOCK_DEFAULT; + AbortTransaction(); + break; + + /* ---------------- + * Here, we are already in an aborted transaction + * state and are waiting for an "END TRANSACTION" to + * come along and lo and behold, we abort again! + * So we just remain in the abort state. + * ---------------- + */ + case TBLOCK_ABORT: + break; + + /* ---------------- + * Here we were in an aborted transaction block which + * just processed the "END TRANSACTION" command but somehow + * aborted again.. since we must have done the abort + * processing, we return to the default state. + * ---------------- + */ + case TBLOCK_ENDABORT: + s->blockState = TBLOCK_DEFAULT; + break; + } +} + +/* ---------------------------------------------------------------- + * transaction block support + * ---------------------------------------------------------------- + */ +/* -------------------------------- + * BeginTransactionBlock + * -------------------------------- + */ +void +BeginTransactionBlock(void) +{ + TransactionState s = CurrentTransactionState; + /* ---------------- - * This is the case when are somewhere in a transaction - * block which aborted so we abort the transaction and - * set the ABORT state. Eventually an "END TRANSACTION" - * will fix things and restore us to a normal state. + * check the current transaction state * ---------------- */ - case TBLOCK_INPROGRESS: - s->blockState = TBLOCK_ABORT; - AbortTransaction(); - break; - + if (s->state == TRANS_DISABLED) + return; + + if (s->blockState != TBLOCK_DEFAULT) + elog(NOTICE, "BeginTransactionBlock and not in default state "); + /* ---------------- - * Here, the system was fouled up just after the - * user wanted to end the transaction block so we - * abort the transaction and put us back into the - * default state. + * set the current transaction block state information + * appropriately during begin processing * ---------------- */ - case TBLOCK_END: - s->blockState = TBLOCK_DEFAULT; - AbortTransaction(); - break; - + s->blockState = TBLOCK_BEGIN; + /* ---------------- - * Here, we are already in an aborted transaction - * state and are waiting for an "END TRANSACTION" to - * come along and lo and behold, we abort again! - * So we just remain in the abort state. + * do begin processing * ---------------- */ - case TBLOCK_ABORT: - break; - + /* ---------------- - * Here we were in an aborted transaction block which - * just processed the "END TRANSACTION" command but somehow - * aborted again.. since we must have done the abort - * processing, we return to the default state. + * done with begin processing, set block state to inprogress * ---------------- */ - case TBLOCK_ENDABORT: - s->blockState = TBLOCK_DEFAULT; - break; - } -} - -/* ---------------------------------------------------------------- - * transaction block support - * ---------------------------------------------------------------- - */ -/* -------------------------------- - * BeginTransactionBlock - * -------------------------------- - */ -void -BeginTransactionBlock(void) -{ - TransactionState s = CurrentTransactionState; - - /* ---------------- - * check the current transaction state - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return; - - if (s->blockState != TBLOCK_DEFAULT) - elog(NOTICE, "BeginTransactionBlock and not in default state "); - - /* ---------------- - * set the current transaction block state information - * appropriately during begin processing - * ---------------- - */ - s->blockState = TBLOCK_BEGIN; - - /* ---------------- - * do begin processing - * ---------------- - */ - - /* ---------------- - * done with begin processing, set block state to inprogress - * ---------------- - */ - s->blockState = TBLOCK_INPROGRESS; + s->blockState = TBLOCK_INPROGRESS; } /* -------------------------------- - * EndTransactionBlock + * EndTransactionBlock * -------------------------------- */ void EndTransactionBlock(void) { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * check the current transaction state - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return; - - if (s->blockState == TBLOCK_INPROGRESS) { + TransactionState s = CurrentTransactionState; + /* ---------------- - * here we are in a transaction block which should commit - * when we get to the upcoming CommitTransactionCommand() - * so we set the state to "END". CommitTransactionCommand() - * will recognize this and commit the transaction and return - * us to the default state + * check the current transaction state * ---------------- */ - s->blockState = TBLOCK_END; - return; - } - - if (s->blockState == TBLOCK_ABORT) { + if (s->state == TRANS_DISABLED) + return; + + if (s->blockState == TBLOCK_INPROGRESS) + { + /* ---------------- + * here we are in a transaction block which should commit + * when we get to the upcoming CommitTransactionCommand() + * so we set the state to "END". CommitTransactionCommand() + * will recognize this and commit the transaction and return + * us to the default state + * ---------------- + */ + s->blockState = TBLOCK_END; + return; + } + + if (s->blockState == TBLOCK_ABORT) + { + /* ---------------- + * here, we are in a transaction block which aborted + * and since the AbortTransaction() was already done, + * we do whatever is needed and change to the special + * "END ABORT" state. The upcoming CommitTransactionCommand() + * will recognise this and then put us back in the default + * state. + * ---------------- + */ + s->blockState = TBLOCK_ENDABORT; + return; + } + /* ---------------- - * here, we are in a transaction block which aborted - * and since the AbortTransaction() was already done, - * we do whatever is needed and change to the special - * "END ABORT" state. The upcoming CommitTransactionCommand() - * will recognise this and then put us back in the default - * state. + * We should not get here, but if we do, we go to the ENDABORT + * state after printing a warning. The upcoming call to + * CommitTransactionCommand() will then put us back into the + * default state. * ---------------- */ + elog(NOTICE, "EndTransactionBlock and not inprogress/abort state "); s->blockState = TBLOCK_ENDABORT; - return; - } - - /* ---------------- - * We should not get here, but if we do, we go to the ENDABORT - * state after printing a warning. The upcoming call to - * CommitTransactionCommand() will then put us back into the - * default state. - * ---------------- - */ - elog(NOTICE, "EndTransactionBlock and not inprogress/abort state "); - s->blockState = TBLOCK_ENDABORT; } /* -------------------------------- - * AbortTransactionBlock + * AbortTransactionBlock * -------------------------------- */ #ifdef NOT_USED static void AbortTransactionBlock(void) { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * check the current transaction state - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return; - - if (s->blockState == TBLOCK_INPROGRESS) { + TransactionState s = CurrentTransactionState; + /* ---------------- - * here we were inside a transaction block something - * screwed up inside the system so we enter the abort state, - * do the abort processing and then return. - * We remain in the abort state until we see the upcoming - * END TRANSACTION command. + * check the current transaction state * ---------------- */ - s->blockState = TBLOCK_ABORT; - + if (s->state == TRANS_DISABLED) + return; + + if (s->blockState == TBLOCK_INPROGRESS) + { + /* ---------------- + * here we were inside a transaction block something + * screwed up inside the system so we enter the abort state, + * do the abort processing and then return. + * We remain in the abort state until we see the upcoming + * END TRANSACTION command. + * ---------------- + */ + s->blockState = TBLOCK_ABORT; + + /* ---------------- + * do abort processing and return + * ---------------- + */ + AbortTransaction(); + return; + } + /* ---------------- - * do abort processing and return + * this case should not be possible, because it would mean + * the user entered an "abort" from outside a transaction block. + * So we print an error message, abort the transaction and + * enter the "ENDABORT" state so we will end up in the default + * state after the upcoming CommitTransactionCommand(). * ---------------- */ + elog(NOTICE, "AbortTransactionBlock and not in in-progress state"); AbortTransaction(); - return; - } - - /* ---------------- - * this case should not be possible, because it would mean - * the user entered an "abort" from outside a transaction block. - * So we print an error message, abort the transaction and - * enter the "ENDABORT" state so we will end up in the default - * state after the upcoming CommitTransactionCommand(). - * ---------------- - */ - elog(NOTICE, "AbortTransactionBlock and not in in-progress state"); - AbortTransaction(); - s->blockState = TBLOCK_ENDABORT; + s->blockState = TBLOCK_ENDABORT; } + #endif /* -------------------------------- - * UserAbortTransactionBlock + * UserAbortTransactionBlock * -------------------------------- */ void UserAbortTransactionBlock() { - TransactionState s = CurrentTransactionState; - - /* ---------------- - * check the current transaction state - * ---------------- - */ - if (s->state == TRANS_DISABLED) - return; - - /* - * if the transaction has already been automatically aborted with an error, - * and the user subsequently types 'abort', allow it. (the behavior is - * the same as if they had typed 'end'.) - */ - if (s->blockState == TBLOCK_ABORT) { - s->blockState = TBLOCK_ENDABORT; - return; - } - - if (s->blockState == TBLOCK_INPROGRESS) { + TransactionState s = CurrentTransactionState; + /* ---------------- - * here we were inside a transaction block and we - * got an abort command from the user, so we move to - * the abort state, do the abort processing and - * then change to the ENDABORT state so we will end up - * in the default state after the upcoming - * CommitTransactionCommand(). + * check the current transaction state * ---------------- */ - s->blockState = TBLOCK_ABORT; - - /* ---------------- - * do abort processing - * ---------------- + if (s->state == TRANS_DISABLED) + return; + + /* + * if the transaction has already been automatically aborted with an + * error, and the user subsequently types 'abort', allow it. (the + * behavior is the same as if they had typed 'end'.) */ - AbortTransaction(); - + if (s->blockState == TBLOCK_ABORT) + { + s->blockState = TBLOCK_ENDABORT; + return; + } + + if (s->blockState == TBLOCK_INPROGRESS) + { + /* ---------------- + * here we were inside a transaction block and we + * got an abort command from the user, so we move to + * the abort state, do the abort processing and + * then change to the ENDABORT state so we will end up + * in the default state after the upcoming + * CommitTransactionCommand(). + * ---------------- + */ + s->blockState = TBLOCK_ABORT; + + /* ---------------- + * do abort processing + * ---------------- + */ + AbortTransaction(); + + /* ---------------- + * change to the end abort state and return + * ---------------- + */ + s->blockState = TBLOCK_ENDABORT; + return; + } + /* ---------------- - * change to the end abort state and return + * this case should not be possible, because it would mean + * the user entered an "abort" from outside a transaction block. + * So we print an error message, abort the transaction and + * enter the "ENDABORT" state so we will end up in the default + * state after the upcoming CommitTransactionCommand(). * ---------------- */ + elog(NOTICE, "UserAbortTransactionBlock and not in in-progress state"); + AbortTransaction(); s->blockState = TBLOCK_ENDABORT; - return; - } - - /* ---------------- - * this case should not be possible, because it would mean - * the user entered an "abort" from outside a transaction block. - * So we print an error message, abort the transaction and - * enter the "ENDABORT" state so we will end up in the default - * state after the upcoming CommitTransactionCommand(). - * ---------------- - */ - elog(NOTICE, "UserAbortTransactionBlock and not in in-progress state"); - AbortTransaction(); - s->blockState = TBLOCK_ENDABORT; } bool IsTransactionBlock() { - TransactionState s = CurrentTransactionState; - - if (s->blockState == TBLOCK_INPROGRESS - || s->blockState == TBLOCK_ENDABORT) { - return (true); - } - - return (false); + TransactionState s = CurrentTransactionState; + + if (s->blockState == TBLOCK_INPROGRESS + || s->blockState == TBLOCK_ENDABORT) + { + return (true); + } + + return (false); } diff --git a/src/backend/access/transam/xid.c b/src/backend/access/transam/xid.c index 16e55e26411..910d6ac7320 100644 --- a/src/backend/access/transam/xid.c +++ b/src/backend/access/transam/xid.c @@ -1,18 +1,18 @@ /*------------------------------------------------------------------------- * * xid.c-- - * POSTGRES transaction identifier code. + * POSTGRES transaction identifier code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.7 1997/08/19 21:30:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.8 1997/09/07 04:39:40 momjian Exp $ * * OLD COMMENTS * XXX WARNING - * Much of this file will change when we change our representation - * of transaction ids -cim 3/23/90 + * Much of this file will change when we change our representation + * of transaction ids -cim 3/23/90 * * It is time to make the switch from 5 byte to 4 byte transaction ids * This file was totally reworked. -mer 5/22/92 @@ -31,127 +31,127 @@ extern TransactionId AmiTransactionId; extern TransactionId FirstTransactionId; /* ---------------------------------------------------------------- - * TransactionIdIsValid + * TransactionIdIsValid * - * Macro-ize me. + * Macro-ize me. * ---------------------------------------------------------------- */ bool TransactionIdIsValid(TransactionId transactionId) { - return ((bool) (transactionId != NullTransactionId) ); + return ((bool) (transactionId != NullTransactionId)); } /* XXX char16 name for catalogs */ TransactionId xidin(char *representation) { - return (atol(representation)); + return (atol(representation)); } /* XXX char16 name for catalogs */ -char* +char * xidout(TransactionId transactionId) { -/* return(TransactionIdFormString(transactionId)); */ - char *representation; - - /* maximum 32 bit unsigned integer representation takes 10 chars */ - representation = palloc(11); - - sprintf(representation, "%u", transactionId); - - return (representation); +/* return(TransactionIdFormString(transactionId)); */ + char *representation; + + /* maximum 32 bit unsigned integer representation takes 10 chars */ + representation = palloc(11); + + sprintf(representation, "%u", transactionId); + + return (representation); } /* ---------------------------------------------------------------- - * StoreInvalidTransactionId + * StoreInvalidTransactionId * - * Maybe do away with Pointer types in these routines. - * Macro-ize this one. + * Maybe do away with Pointer types in these routines. + * Macro-ize this one. * ---------------------------------------------------------------- */ void -StoreInvalidTransactionId(TransactionId *destination) +StoreInvalidTransactionId(TransactionId * destination) { - *destination = NullTransactionId; + *destination = NullTransactionId; } /* ---------------------------------------------------------------- - * TransactionIdStore + * TransactionIdStore * - * Macro-ize this one. + * Macro-ize this one. * ---------------------------------------------------------------- */ void TransactionIdStore(TransactionId transactionId, - TransactionId *destination) + TransactionId * destination) { - *destination = transactionId; + *destination = transactionId; } /* ---------------------------------------------------------------- - * TransactionIdEquals + * TransactionIdEquals * ---------------------------------------------------------------- */ bool TransactionIdEquals(TransactionId id1, TransactionId id2) { - return ((bool) (id1 == id2)); + return ((bool) (id1 == id2)); } /* ---------------------------------------------------------------- - * TransactionIdIsLessThan + * TransactionIdIsLessThan * ---------------------------------------------------------------- */ bool TransactionIdIsLessThan(TransactionId id1, TransactionId id2) { - return ((bool)(id1 < id2)); + return ((bool) (id1 < id2)); } /* ---------------------------------------------------------------- - * xideq + * xideq * ---------------------------------------------------------------- */ /* - * xideq - returns 1, iff xid1 == xid2 - * 0 else; + * xideq - returns 1, iff xid1 == xid2 + * 0 else; */ bool xideq(TransactionId xid1, TransactionId xid2) { - return( (bool) (xid1 == xid2) ); + return ((bool) (xid1 == xid2)); } /* ---------------------------------------------------------------- - * TransactionIdIncrement + * TransactionIdIncrement * ---------------------------------------------------------------- */ #ifdef NOT_USED void -TransactionIdIncrement(TransactionId *transactionId) +TransactionIdIncrement(TransactionId * transactionId) { - - (*transactionId)++; - if (*transactionId == DisabledTransactionId) - elog(FATAL, "TransactionIdIncrement: exhausted XID's"); - return; + + (*transactionId)++; + if (*transactionId == DisabledTransactionId) + elog(FATAL, "TransactionIdIncrement: exhausted XID's"); + return; } + #endif /* ---------------------------------------------------------------- - * TransactionIdAdd + * TransactionIdAdd * ---------------------------------------------------------------- */ void -TransactionIdAdd(TransactionId *xid, int value) +TransactionIdAdd(TransactionId * xid, int value) { - *xid += value; - return; + *xid += value; + return; } - diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 6b27010d3a9..9fd4bf719b3 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1,23 +1,23 @@ /*------------------------------------------------------------------------- * * bootstrap.c-- - * routines to support running postgres in 'bootstrap' mode - * bootstrap mode is used to create the initial template database + * routines to support running postgres in 'bootstrap' mode + * bootstrap mode is used to create the initial template database * * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.21 1997/08/19 21:30:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.22 1997/09/07 04:39:49 momjian Exp $ * *------------------------------------------------------------------------- */ -#include <unistd.h> /* For getopt() */ +#include <unistd.h> /* For getopt() */ #include <time.h> #include <stdio.h> #include <signal.h> #include <setjmp.h> -#define BOOTSTRAP_INCLUDE /* mask out stuff in tcop/tcopprot.h */ +#define BOOTSTRAP_INCLUDE /* mask out stuff in tcop/tcopprot.h */ #include "postgres.h" @@ -41,7 +41,7 @@ #include "utils/nabstime.h" #include "access/htup.h" #include "utils/tqual.h" -#include "storage/buf.h" +#include "storage/buf.h" #include "access/relscan.h" #include "access/heapam.h" @@ -55,7 +55,7 @@ #include "catalog/pg_type.h" -#include "access/itup.h" +#include "access/itup.h" #include "bootstrap/bootstrap.h" #include "tcop/tcopprot.h" @@ -69,7 +69,7 @@ #include "access/xact.h" #ifndef HAVE_MEMMOVE -# include "regex/utils.h" +#include "regex/utils.h" #endif #include <string.h> @@ -100,20 +100,20 @@ #include "utils/palloc.h" -#define ALLOC(t, c) (t *)calloc((unsigned)(c), sizeof(t)) -#define FIRST_TYPE_OID 16 /* OID of the first type */ +#define ALLOC(t, c) (t *)calloc((unsigned)(c), sizeof(t)) +#define FIRST_TYPE_OID 16 /* OID of the first type */ -extern int Int_yyparse (void); +extern int Int_yyparse(void); static hashnode *AddStr(char *str, int strlength, int mderef); static AttributeTupleForm AllocateAttribute(void); -static bool BootstrapAlreadySeen(Oid id); -static int CompHash (char *str, int len); -static hashnode *FindStr (char *str, int length, hashnode *mderef); -static int gettype(char *type); -static void cleanup(void); +static bool BootstrapAlreadySeen(Oid id); +static int CompHash(char *str, int len); +static hashnode *FindStr(char *str, int length, hashnode * mderef); +static int gettype(char *type); +static void cleanup(void); /* ---------------- - * global variables + * global variables * ---------------- */ /* @@ -126,130 +126,138 @@ static void cleanup(void); * position of its string pointer in the array of string pointers. */ -#define STRTABLESIZE 10000 -#define HASHTABLESIZE 503 +#define STRTABLESIZE 10000 +#define HASHTABLESIZE 503 /* Hash function numbers */ -#define NUM 23 -#define NUMSQR 529 +#define NUM 23 +#define NUMSQR 529 #define NUMCUBE 12167 -char *strtable [STRTABLESIZE]; -hashnode *hashtable [HASHTABLESIZE]; +char *strtable[STRTABLESIZE]; +hashnode *hashtable[HASHTABLESIZE]; -static int strtable_end = -1; /* Tells us last occupied string space */ +static int strtable_end = -1; /* Tells us last occupied string + * space */ /*- * Basic information associated with each type. This is used before * pg_type is created. * - * XXX several of these input/output functions do catalog scans - * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some - * order dependencies in the catalog creation process. + * XXX several of these input/output functions do catalog scans + * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some + * order dependencies in the catalog creation process. */ -struct typinfo { - char name[NAMEDATALEN]; - Oid oid; - Oid elem; - int16 len; - Oid inproc; - Oid outproc; +struct typinfo +{ + char name[NAMEDATALEN]; + Oid oid; + Oid elem; + int16 len; + Oid inproc; + Oid outproc; }; static struct typinfo Procid[] = { - { "bool", 16, 0, 1, F_BOOLIN, F_BOOLOUT }, - { "bytea", 17, 0, -1, F_BYTEAIN, F_BYTEAOUT }, - { "char", 18, 0, 1, F_CHARIN, F_CHAROUT }, - { "name", 19, 0, NAMEDATALEN, F_NAMEIN, F_NAMEOUT }, - { "char16", 20, 0, 16, F_CHAR16IN, F_CHAR16OUT}, -/* { "dt", 20, 0, 4, F_DTIN, F_DTOUT}, */ - { "int2", 21, 0, 2, F_INT2IN, F_INT2OUT }, - { "int28", 22, 0, 16, F_INT28IN, F_INT28OUT }, - { "int4", 23, 0, 4, F_INT4IN, F_INT4OUT }, - { "regproc", 24, 0, 4, F_REGPROCIN, F_REGPROCOUT }, - { "text", 25, 0, -1, F_TEXTIN, F_TEXTOUT }, - { "oid", 26, 0, 4, F_INT4IN, F_INT4OUT }, - { "tid", 27, 0, 6, F_TIDIN, F_TIDOUT }, - { "xid", 28, 0, 5, F_XIDIN, F_XIDOUT }, - { "iid", 29, 0, 1, F_CIDIN, F_CIDOUT }, - { "oid8", 30, 0, 32, F_OID8IN, F_OID8OUT }, - { "smgr", 210, 0, 2, F_SMGRIN, F_SMGROUT }, - { "_int4", 1007, 23, -1, F_ARRAY_IN, F_ARRAY_OUT }, - { "_aclitem", 1034, 1033, -1, F_ARRAY_IN, F_ARRAY_OUT } + {"bool", 16, 0, 1, F_BOOLIN, F_BOOLOUT}, + {"bytea", 17, 0, -1, F_BYTEAIN, F_BYTEAOUT}, + {"char", 18, 0, 1, F_CHARIN, F_CHAROUT}, + {"name", 19, 0, NAMEDATALEN, F_NAMEIN, F_NAMEOUT}, + {"char16", 20, 0, 16, F_CHAR16IN, F_CHAR16OUT}, +/* { "dt", 20, 0, 4, F_DTIN, F_DTOUT}, */ + {"int2", 21, 0, 2, F_INT2IN, F_INT2OUT}, + {"int28", 22, 0, 16, F_INT28IN, F_INT28OUT}, + {"int4", 23, 0, 4, F_INT4IN, F_INT4OUT}, + {"regproc", 24, 0, 4, F_REGPROCIN, F_REGPROCOUT}, + {"text", 25, 0, -1, F_TEXTIN, F_TEXTOUT}, + {"oid", 26, 0, 4, F_INT4IN, F_INT4OUT}, + {"tid", 27, 0, 6, F_TIDIN, F_TIDOUT}, + {"xid", 28, 0, 5, F_XIDIN, F_XIDOUT}, + {"iid", 29, 0, 1, F_CIDIN, F_CIDOUT}, + {"oid8", 30, 0, 32, F_OID8IN, F_OID8OUT}, + {"smgr", 210, 0, 2, F_SMGRIN, F_SMGROUT}, + {"_int4", 1007, 23, -1, F_ARRAY_IN, F_ARRAY_OUT}, + {"_aclitem", 1034, 1033, -1, F_ARRAY_IN, F_ARRAY_OUT} }; -static int n_types = sizeof(Procid) / sizeof(struct typinfo); +static int n_types = sizeof(Procid) / sizeof(struct typinfo); -struct typmap { /* a hack */ - Oid am_oid; - TypeTupleFormData am_typ; +struct typmap +{ /* a hack */ + Oid am_oid; + TypeTupleFormData am_typ; }; -static struct typmap **Typ = (struct typmap **)NULL; -static struct typmap *Ap = (struct typmap *)NULL; - -static int Warnings = 0; -static char Blanks[MAXATTR]; - -static char *relname; /* current relation name */ +static struct typmap **Typ = (struct typmap **) NULL; +static struct typmap *Ap = (struct typmap *) NULL; + +static int Warnings = 0; +static char Blanks[MAXATTR]; + +static char *relname; /* current relation name */ -AttributeTupleForm attrtypes[MAXATTR]; /* points to attribute info */ -static char *values[MAXATTR]; /* cooresponding attribute values */ -int numattr; /* number of attributes for cur. rel */ -extern int fsyncOff; /* do not fsync the database */ +AttributeTupleForm attrtypes[MAXATTR]; /* points to attribute info */ +static char *values[MAXATTR];/* cooresponding attribute values */ +int numattr; /* number of attributes for cur. rel */ +extern int fsyncOff; /* do not fsync the database */ #ifndef HAVE_SIGSETJMP -static jmp_buf Warn_restart; -#define sigsetjmp(x,y) setjmp(x) +static jmp_buf Warn_restart; + +#define sigsetjmp(x,y) setjmp(x) #define siglongjmp longjmp #else static sigjmp_buf Warn_restart; + #endif -int DebugMode; -static GlobalMemory nogc = (GlobalMemory) NULL; /* special no-gc mem context */ +int DebugMode; +static GlobalMemory nogc = (GlobalMemory) NULL; /* special no-gc mem + * context */ + +extern int optind; +extern char *optarg; -extern int optind; -extern char *optarg; - /* - * At bootstrap time, we first declare all the indices to be built, and - * then build them. The IndexList structure stores enough information - * to allow us to build the indices after they've been declared. + * At bootstrap time, we first declare all the indices to be built, and + * then build them. The IndexList structure stores enough information + * to allow us to build the indices after they've been declared. */ -typedef struct _IndexList { - char* il_heap; - char* il_ind; - int il_natts; - AttrNumber *il_attnos; - uint16 il_nparams; - Datum * il_params; - FuncIndexInfo *il_finfo; - PredInfo *il_predInfo; - struct _IndexList *il_next; -} IndexList; +typedef struct _IndexList +{ + char *il_heap; + char *il_ind; + int il_natts; + AttrNumber *il_attnos; + uint16 il_nparams; + Datum *il_params; + FuncIndexInfo *il_finfo; + PredInfo *il_predInfo; + struct _IndexList *il_next; +} IndexList; static IndexList *ILHead = (IndexList *) NULL; - -typedef void (*sig_func)(); + +typedef void (*sig_func) (); + + - /* ---------------------------------------------------------------- - * misc functions + * misc functions * ---------------------------------------------------------------- */ /* ---------------- - * error handling / abort routines + * error handling / abort routines * ---------------- */ void err_out(void) { - Warnings++; - cleanup(); + Warnings++; + cleanup(); } /* usage: @@ -258,15 +266,15 @@ err_out(void) static void usage(void) { - fprintf(stderr,"Usage: postgres -boot [-d] [-C] [-F] [-O] [-Q] "); - fprintf(stderr,"[-P portno] [dbName]\n"); - fprintf(stderr," d: debug mode\n"); - fprintf(stderr," C: disable version checking\n"); - fprintf(stderr," F: turn off fsync\n"); - fprintf(stderr," O: set BootstrapProcessing mode\n"); - fprintf(stderr," P portno: specify port number\n"); - - exitpg(1); + fprintf(stderr, "Usage: postgres -boot [-d] [-C] [-F] [-O] [-Q] "); + fprintf(stderr, "[-P portno] [dbName]\n"); + fprintf(stderr, " d: debug mode\n"); + fprintf(stderr, " C: disable version checking\n"); + fprintf(stderr, " F: turn off fsync\n"); + fprintf(stderr, " O: set BootstrapProcessing mode\n"); + fprintf(stderr, " P portno: specify port number\n"); + + exitpg(1); } @@ -274,286 +282,316 @@ usage(void) int BootstrapMain(int argc, char *argv[]) /* ---------------------------------------------------------------- - * The main loop for handling the backend in bootstrap mode - * the bootstrap mode is used to initialize the template database - * the bootstrap backend doesn't speak SQL, but instead expects - * commands in a special bootstrap language. + * The main loop for handling the backend in bootstrap mode + * the bootstrap mode is used to initialize the template database + * the bootstrap backend doesn't speak SQL, but instead expects + * commands in a special bootstrap language. * - * The arguments passed in to BootstrapMain are the run-time arguments - * without the argument '-boot', the caller is required to have - * removed -boot from the run-time args + * The arguments passed in to BootstrapMain are the run-time arguments + * without the argument '-boot', the caller is required to have + * removed -boot from the run-time args * ---------------------------------------------------------------- */ { - int i; - int portFd = -1; - char *dbName; - int flag; - int override = 1; /* use BootstrapProcessing or InitProcessing mode */ - - extern int optind; - extern char *optarg; - - /* ---------------- - * initialize signal handlers - * ---------------- - */ - pqsignal(SIGINT, (sig_func) die); + int i; + int portFd = -1; + char *dbName; + int flag; + int override = 1; /* use BootstrapProcessing or + * InitProcessing mode */ + + extern int optind; + extern char *optarg; + + /* ---------------- + * initialize signal handlers + * ---------------- + */ + pqsignal(SIGINT, (sig_func) die); #ifndef win32 - pqsignal(SIGHUP, (sig_func) die); - pqsignal(SIGTERM, (sig_func) die); -#endif /* win32 */ - - /* -------------------- - * initialize globals - * ------------------- - */ - - MasterPid = getpid(); - - /* ---------------- - * process command arguments - * ---------------- - */ - - /* Set defaults, to be overriden by explicit options below */ - Quiet = 0; - Noversion = 0; - dbName = NULL; - DataDir = getenv("PGDATA"); /* Null if no PGDATA variable */ - - while ((flag = getopt(argc, argv, "D:dCOQP:F")) != EOF) { - switch (flag) { - case 'D': - DataDir = optarg; - break; - case 'd': - DebugMode = 1; /* print out debugging info while parsing */ - break; - case 'C': - Noversion = 1; - break; - case 'F': - fsyncOff = 1; - break; - case 'O': - override = true; - break; - case 'Q': - Quiet = 1; - break; - case 'P':/* specify port */ - portFd = atoi(optarg); - break; - default: - usage(); - break; - } - } /* while */ - - if (argc - optind > 1) { - usage(); - } else - if (argc - optind == 1) { - dbName = argv[optind]; - } - - if (!DataDir) { - fprintf(stderr, "%s does not know where to find the database system " - "data. You must specify the directory that contains the " - "database system either by specifying the -D invocation " - "option or by setting the PGDATA environment variable.\n\n", - argv[0]); - exitpg(1); - } - - if (dbName == NULL) { - dbName = getenv("USER"); - if (dbName == NULL) { - fputs("bootstrap backend: failed, no db name specified\n", stderr); - fputs(" and no USER enviroment variable\n", stderr); - exitpg(1); - } - } - - /* ---------------- - * initialize input fd - * ---------------- - */ - if (IsUnderPostmaster == true && portFd < 0) { - fputs("backend: failed, no -P option with -postmaster opt.\n", stderr); - exitpg(1); - } - + pqsignal(SIGHUP, (sig_func) die); + pqsignal(SIGTERM, (sig_func) die); +#endif /* win32 */ + + /* -------------------- + * initialize globals + * ------------------- + */ + + MasterPid = getpid(); + + /* ---------------- + * process command arguments + * ---------------- + */ + + /* Set defaults, to be overriden by explicit options below */ + Quiet = 0; + Noversion = 0; + dbName = NULL; + DataDir = getenv("PGDATA"); /* Null if no PGDATA variable */ + + while ((flag = getopt(argc, argv, "D:dCOQP:F")) != EOF) + { + switch (flag) + { + case 'D': + DataDir = optarg; + break; + case 'd': + DebugMode = 1; /* print out debugging info while parsing */ + break; + case 'C': + Noversion = 1; + break; + case 'F': + fsyncOff = 1; + break; + case 'O': + override = true; + break; + case 'Q': + Quiet = 1; + break; + case 'P': /* specify port */ + portFd = atoi(optarg); + break; + default: + usage(); + break; + } + } /* while */ + + if (argc - optind > 1) + { + usage(); + } + else if (argc - optind == 1) + { + dbName = argv[optind]; + } + + if (!DataDir) + { + fprintf(stderr, "%s does not know where to find the database system " + "data. You must specify the directory that contains the " + "database system either by specifying the -D invocation " + "option or by setting the PGDATA environment variable.\n\n", + argv[0]); + exitpg(1); + } + + if (dbName == NULL) + { + dbName = getenv("USER"); + if (dbName == NULL) + { + fputs("bootstrap backend: failed, no db name specified\n", stderr); + fputs(" and no USER enviroment variable\n", stderr); + exitpg(1); + } + } + + /* ---------------- + * initialize input fd + * ---------------- + */ + if (IsUnderPostmaster == true && portFd < 0) + { + fputs("backend: failed, no -P option with -postmaster opt.\n", stderr); + exitpg(1); + } + #ifdef win32 - _nt_init(); - _nt_attach(); -#endif /* win32 */ - - - /* ---------------- - * backend initialization - * ---------------- - */ - SetProcessingMode((override) ? BootstrapProcessing : InitProcessing); - InitPostgres(dbName); - LockDisable(true); - - for (i = 0 ; i < MAXATTR; i++) { - attrtypes[i]=(AttributeTupleForm )NULL; - Blanks[i] = ' '; - } - for(i = 0; i < STRTABLESIZE; ++i) - strtable[i] = NULL; - for(i = 0; i < HASHTABLESIZE; ++i) - hashtable[i] = NULL; - - /* ---------------- - * abort processing resumes here - What to do in WIN32? - * ---------------- - */ -#ifndef win32 - pqsignal(SIGHUP, handle_warn); - - if (sigsetjmp(Warn_restart, 1) != 0) { + _nt_init(); + _nt_attach(); +#endif /* win32 */ + + + /* ---------------- + * backend initialization + * ---------------- + */ + SetProcessingMode((override) ? BootstrapProcessing : InitProcessing); + InitPostgres(dbName); + LockDisable(true); + + for (i = 0; i < MAXATTR; i++) + { + attrtypes[i] = (AttributeTupleForm) NULL; + Blanks[i] = ' '; + } + for (i = 0; i < STRTABLESIZE; ++i) + strtable[i] = NULL; + for (i = 0; i < HASHTABLESIZE; ++i) + hashtable[i] = NULL; + + /* ---------------- + * abort processing resumes here - What to do in WIN32? + * ---------------- + */ +#ifndef win32 + pqsignal(SIGHUP, handle_warn); + + if (sigsetjmp(Warn_restart, 1) != 0) + { #else - if (setjmp(Warn_restart) != 0) { -#endif /* win32 */ - Warnings++; - AbortCurrentTransaction(); - } - - /* ---------------- - * process input. - * ---------------- - */ - - /* the sed script boot.sed renamed yyparse to Int_yyparse - for the bootstrap parser to avoid conflicts with the normal SQL - parser */ - Int_yyparse(); - - /* clean up processing */ - StartTransactionCommand(); - cleanup(); - - /* not reached, here to make compiler happy */ - return 0; + if (setjmp(Warn_restart) != 0) + { +#endif /* win32 */ + Warnings++; + AbortCurrentTransaction(); + } + + /* ---------------- + * process input. + * ---------------- + */ + + /* + * the sed script boot.sed renamed yyparse to Int_yyparse for the + * bootstrap parser to avoid conflicts with the normal SQL parser + */ + Int_yyparse(); + + /* clean up processing */ + StartTransactionCommand(); + cleanup(); + + /* not reached, here to make compiler happy */ + return 0; } /* ---------------------------------------------------------------- - * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS + * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS * ---------------------------------------------------------------- */ /* ---------------- - * boot_openrel + * boot_openrel * ---------------- */ void boot_openrel(char *relname) { - int i; - struct typmap **app; - Relation rdesc; - HeapScanDesc sdesc; - HeapTuple tup; - - if (strlen(relname) > 15) - relname[15] ='\000'; - - if (Typ == (struct typmap **)NULL) { - StartPortalAllocMode(DefaultAllocMode, 0); - rdesc = heap_openr(TypeRelationName); - sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL); - for (i=0; PointerIsValid(tup=heap_getnext(sdesc,0,(Buffer *)NULL)); ++i); - heap_endscan(sdesc); - app = Typ = ALLOC(struct typmap *, i + 1); - while (i-- > 0) - *app++ = ALLOC(struct typmap, 1); - *app = (struct typmap *)NULL; - sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL); - app = Typ; - while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL))) { - (*app)->am_oid = tup->t_oid; - memmove((char *)&(*app++)->am_typ, - (char *)GETSTRUCT(tup), - sizeof ((*app)->am_typ)); - } - heap_endscan(sdesc); - heap_close(rdesc); - EndPortalAllocMode(); - } - - if (reldesc != NULL) { - closerel(NULL); - } - - if (!Quiet) - printf("Amopen: relation %s. attrsize %d\n", relname ? relname : "(null)", - (int)ATTRIBUTE_TUPLE_SIZE); - - reldesc = heap_openr(relname); - Assert(reldesc); - numattr = reldesc->rd_rel->relnatts; - for (i = 0; i < numattr; i++) { - if (attrtypes[i] == NULL) { - attrtypes[i] = AllocateAttribute(); - } - memmove((char *)attrtypes[i], - (char *)reldesc->rd_att->attrs[i], - ATTRIBUTE_TUPLE_SIZE); - - /* Some old pg_attribute tuples might not have attisset. */ - /* If the attname is attisset, don't look for it - it may - not be defined yet. - */ - if (namestrcmp(&attrtypes[i]->attname, "attisset") == 0) - attrtypes[i]->attisset = get_attisset(reldesc->rd_id, - attrtypes[i]->attname.data); - else - attrtypes[i]->attisset = false; - - if (DebugMode) { - AttributeTupleForm at = attrtypes[i]; - printf("create attribute %d name %s len %d num %d type %d\n", - i, at->attname.data, at->attlen, at->attnum, - at->atttypid - ); - fflush(stdout); - } - } + int i; + struct typmap **app; + Relation rdesc; + HeapScanDesc sdesc; + HeapTuple tup; + + if (strlen(relname) > 15) + relname[15] = '\000'; + + if (Typ == (struct typmap **) NULL) + { + StartPortalAllocMode(DefaultAllocMode, 0); + rdesc = heap_openr(TypeRelationName); + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey) NULL); + for (i = 0; PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *) NULL)); ++i); + heap_endscan(sdesc); + app = Typ = ALLOC(struct typmap *, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = (struct typmap *) NULL; + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey) NULL); + app = Typ; + while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *) NULL))) + { + (*app)->am_oid = tup->t_oid; + memmove((char *) &(*app++)->am_typ, + (char *) GETSTRUCT(tup), + sizeof((*app)->am_typ)); + } + heap_endscan(sdesc); + heap_close(rdesc); + EndPortalAllocMode(); + } + + if (reldesc != NULL) + { + closerel(NULL); + } + + if (!Quiet) + printf("Amopen: relation %s. attrsize %d\n", relname ? relname : "(null)", + (int) ATTRIBUTE_TUPLE_SIZE); + + reldesc = heap_openr(relname); + Assert(reldesc); + numattr = reldesc->rd_rel->relnatts; + for (i = 0; i < numattr; i++) + { + if (attrtypes[i] == NULL) + { + attrtypes[i] = AllocateAttribute(); + } + memmove((char *) attrtypes[i], + (char *) reldesc->rd_att->attrs[i], + ATTRIBUTE_TUPLE_SIZE); + + /* Some old pg_attribute tuples might not have attisset. */ + + /* + * If the attname is attisset, don't look for it - it may not be + * defined yet. + */ + if (namestrcmp(&attrtypes[i]->attname, "attisset") == 0) + attrtypes[i]->attisset = get_attisset(reldesc->rd_id, + attrtypes[i]->attname.data); + else + attrtypes[i]->attisset = false; + + if (DebugMode) + { + AttributeTupleForm at = attrtypes[i]; + + printf("create attribute %d name %s len %d num %d type %d\n", + i, at->attname.data, at->attlen, at->attnum, + at->atttypid + ); + fflush(stdout); + } + } } /* ---------------- - * closerel + * closerel * ---------------- */ void closerel(char *name) { - if (name) { - if (reldesc) { - if (namestrcmp(RelationGetRelationName(reldesc), name) != 0) - elog(WARN,"closerel: close of '%s' when '%s' was expected", - name, relname ? relname : "(null)"); - } else - elog(WARN,"closerel: close of '%s' before any relation was opened", - name); - - } - - if (reldesc == NULL) { - elog(WARN,"Warning: no opened relation to close.\n"); - } else { - if (!Quiet) printf("Amclose: relation %s.\n", relname ? relname : "(null)"); - heap_close(reldesc); - reldesc = (Relation)NULL; - } + if (name) + { + if (reldesc) + { + if (namestrcmp(RelationGetRelationName(reldesc), name) != 0) + elog(WARN, "closerel: close of '%s' when '%s' was expected", + name, relname ? relname : "(null)"); + } + else + elog(WARN, "closerel: close of '%s' before any relation was opened", + name); + + } + + if (reldesc == NULL) + { + elog(WARN, "Warning: no opened relation to close.\n"); + } + else + { + if (!Quiet) + printf("Amclose: relation %s.\n", relname ? relname : "(null)"); + heap_close(reldesc); + reldesc = (Relation) NULL; + } } - + + /* ---------------- * DEFINEATTR() * @@ -565,563 +603,626 @@ closerel(char *name) void DefineAttr(char *name, char *type, int attnum) { - int attlen; - int t; - - if (reldesc != NULL) { - fputs("Warning: no open relations allowed with 't' command.\n",stderr); - closerel(relname); - } - - t = gettype(type); - if (attrtypes[attnum] == (AttributeTupleForm )NULL) - attrtypes[attnum] = AllocateAttribute(); - if (Typ != (struct typmap **)NULL) { - attrtypes[attnum]->atttypid = Ap->am_oid; - namestrcpy(&attrtypes[attnum]->attname, name); - if (!Quiet) printf("<%s %s> ", attrtypes[attnum]->attname.data, type); - attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ - attlen = attrtypes[attnum]->attlen = Ap->am_typ.typlen; - attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; - } else { - attrtypes[attnum]->atttypid = Procid[t].oid; - namestrcpy(&attrtypes[attnum]->attname,name); - if (!Quiet) printf("<%s %s> ", attrtypes[attnum]->attname.data, type); - attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ - attlen = attrtypes[attnum]->attlen = Procid[t].len; - attrtypes[attnum]->attbyval = (attlen==1) || (attlen==2)||(attlen==4); - } + int attlen; + int t; + + if (reldesc != NULL) + { + fputs("Warning: no open relations allowed with 't' command.\n", stderr); + closerel(relname); + } + + t = gettype(type); + if (attrtypes[attnum] == (AttributeTupleForm) NULL) + attrtypes[attnum] = AllocateAttribute(); + if (Typ != (struct typmap **) NULL) + { + attrtypes[attnum]->atttypid = Ap->am_oid; + namestrcpy(&attrtypes[attnum]->attname, name); + if (!Quiet) + printf("<%s %s> ", attrtypes[attnum]->attname.data, type); + attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ + attlen = attrtypes[attnum]->attlen = Ap->am_typ.typlen; + attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; + } + else + { + attrtypes[attnum]->atttypid = Procid[t].oid; + namestrcpy(&attrtypes[attnum]->attname, name); + if (!Quiet) + printf("<%s %s> ", attrtypes[attnum]->attname.data, type); + attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ + attlen = attrtypes[attnum]->attlen = Procid[t].len; + attrtypes[attnum]->attbyval = (attlen == 1) || (attlen == 2) || (attlen == 4); + } } /* ---------------- - * InsertOneTuple - * assumes that 'oid' will not be zero. + * InsertOneTuple + * assumes that 'oid' will not be zero. * ---------------- */ void InsertOneTuple(Oid objectid) { - HeapTuple tuple; - TupleDesc tupDesc; - - int i; - - if (DebugMode) { - printf("InsertOneTuple oid %d, %d attrs\n", objectid, numattr); - fflush(stdout); - } - - tupDesc = CreateTupleDesc(numattr,attrtypes); - tuple = heap_formtuple(tupDesc,(Datum*)values,Blanks); - pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ - - if(objectid !=(Oid)0) { - tuple->t_oid=objectid; - } - heap_insert(reldesc, tuple); - pfree(tuple); - if (DebugMode) { - printf("End InsertOneTuple, objectid=%d\n", objectid); - fflush(stdout); - } - /* - * Reset blanks for next tuple - */ - for (i = 0; i<numattr; i++) - Blanks[i] = ' '; + HeapTuple tuple; + TupleDesc tupDesc; + + int i; + + if (DebugMode) + { + printf("InsertOneTuple oid %d, %d attrs\n", objectid, numattr); + fflush(stdout); + } + + tupDesc = CreateTupleDesc(numattr, attrtypes); + tuple = heap_formtuple(tupDesc, (Datum *) values, Blanks); + pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ + + if (objectid != (Oid) 0) + { + tuple->t_oid = objectid; + } + heap_insert(reldesc, tuple); + pfree(tuple); + if (DebugMode) + { + printf("End InsertOneTuple, objectid=%d\n", objectid); + fflush(stdout); + } + + /* + * Reset blanks for next tuple + */ + for (i = 0; i < numattr; i++) + Blanks[i] = ' '; } /* ---------------- - * InsertOneValue + * InsertOneValue * ---------------- */ void InsertOneValue(Oid objectid, char *value, int i) { - int typeindex; - char *prt; - struct typmap **app; - - if (DebugMode) - printf("Inserting value: '%s'\n", value); - if (i < 0 || i >= MAXATTR) { - printf("i out of range: %d\n", i); - Assert(0); - } - - if (Typ != (struct typmap **)NULL) { - struct typmap *ap; - if (DebugMode) - puts("Typ != NULL"); - app = Typ; - while (*app && (*app)->am_oid != reldesc->rd_att->attrs[i]->atttypid) - ++app; - ap = *app; - if (ap == NULL) { - printf("Unable to find atttypid in Typ list! %d\n", - reldesc->rd_att->attrs[i]->atttypid - ); - Assert(0); - } - values[i] = fmgr(ap->am_typ.typinput, - value, - ap->am_typ.typelem, - -1); /* shouldn't have char() or varchar() types - during boostrapping but just to be safe */ - prt = fmgr(ap->am_typ.typoutput, values[i], - ap->am_typ.typelem); - if (!Quiet) printf("%s ", prt); - pfree(prt); - } else { - typeindex = attrtypes[i]->atttypid - FIRST_TYPE_OID; - if (DebugMode) - printf("Typ == NULL, typeindex = %d idx = %d\n", typeindex, i); - values[i] = fmgr(Procid[typeindex].inproc, value, - Procid[typeindex].elem, -1); - prt = fmgr(Procid[typeindex].outproc, values[i], - Procid[typeindex].elem); - if (!Quiet) printf("%s ", prt); - pfree(prt); - } - if (DebugMode) { - puts("End InsertValue"); - fflush(stdout); - } + int typeindex; + char *prt; + struct typmap **app; + + if (DebugMode) + printf("Inserting value: '%s'\n", value); + if (i < 0 || i >= MAXATTR) + { + printf("i out of range: %d\n", i); + Assert(0); + } + + if (Typ != (struct typmap **) NULL) + { + struct typmap *ap; + + if (DebugMode) + puts("Typ != NULL"); + app = Typ; + while (*app && (*app)->am_oid != reldesc->rd_att->attrs[i]->atttypid) + ++app; + ap = *app; + if (ap == NULL) + { + printf("Unable to find atttypid in Typ list! %d\n", + reldesc->rd_att->attrs[i]->atttypid + ); + Assert(0); + } + values[i] = fmgr(ap->am_typ.typinput, + value, + ap->am_typ.typelem, + -1); /* shouldn't have char() or varchar() + * types during boostrapping but just to + * be safe */ + prt = fmgr(ap->am_typ.typoutput, values[i], + ap->am_typ.typelem); + if (!Quiet) + printf("%s ", prt); + pfree(prt); + } + else + { + typeindex = attrtypes[i]->atttypid - FIRST_TYPE_OID; + if (DebugMode) + printf("Typ == NULL, typeindex = %d idx = %d\n", typeindex, i); + values[i] = fmgr(Procid[typeindex].inproc, value, + Procid[typeindex].elem, -1); + prt = fmgr(Procid[typeindex].outproc, values[i], + Procid[typeindex].elem); + if (!Quiet) + printf("%s ", prt); + pfree(prt); + } + if (DebugMode) + { + puts("End InsertValue"); + fflush(stdout); + } } /* ---------------- - * InsertOneNull + * InsertOneNull * ---------------- */ void InsertOneNull(int i) { - if (DebugMode) - printf("Inserting null\n"); - if (i < 0 || i >= MAXATTR) { - elog(FATAL, "i out of range (too many attrs): %d\n", i); - } - values[i] = (char *)NULL; - Blanks[i] = 'n'; + if (DebugMode) + printf("Inserting null\n"); + if (i < 0 || i >= MAXATTR) + { + elog(FATAL, "i out of range (too many attrs): %d\n", i); + } + values[i] = (char *) NULL; + Blanks[i] = 'n'; } #define MORE_THAN_THE_NUMBER_OF_CATALOGS 256 -static bool +static bool BootstrapAlreadySeen(Oid id) { - static Oid seenArray[MORE_THAN_THE_NUMBER_OF_CATALOGS]; - static int nseen = 0; - bool seenthis; - int i; - - seenthis = false; - - for (i=0; i < nseen; i++) { - if (seenArray[i] == id) { - seenthis = true; - break; - } - } - if (!seenthis) { - seenArray[nseen] = id; - nseen++; - } - return (seenthis); + static Oid seenArray[MORE_THAN_THE_NUMBER_OF_CATALOGS]; + static int nseen = 0; + bool seenthis; + int i; + + seenthis = false; + + for (i = 0; i < nseen; i++) + { + if (seenArray[i] == id) + { + seenthis = true; + break; + } + } + if (!seenthis) + { + seenArray[nseen] = id; + nseen++; + } + return (seenthis); } /* ---------------- - * cleanup + * cleanup * ---------------- */ static void cleanup() { - static int beenhere = 0; - - if (!beenhere) - beenhere = 1; - else { - elog(FATAL,"Memory manager fault: cleanup called twice.\n", stderr); - exitpg(1); - } - if (reldesc != (Relation)NULL) { - heap_close(reldesc); - } - CommitTransactionCommand(); - exitpg(Warnings); + static int beenhere = 0; + + if (!beenhere) + beenhere = 1; + else + { + elog(FATAL, "Memory manager fault: cleanup called twice.\n", stderr); + exitpg(1); + } + if (reldesc != (Relation) NULL) + { + heap_close(reldesc); + } + CommitTransactionCommand(); + exitpg(Warnings); } /* ---------------- - * gettype + * gettype * ---------------- */ static int gettype(char *type) { - int i; - Relation rdesc; - HeapScanDesc sdesc; - HeapTuple tup; - struct typmap **app; - - if (Typ != (struct typmap **)NULL) { - for (app = Typ; *app != (struct typmap *)NULL; app++) { - if (strncmp((*app)->am_typ.typname.data, type, NAMEDATALEN) == 0) { - Ap = *app; - return((*app)->am_oid); - } - } - } else { - for (i = 0; i <= n_types; i++) { - if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0) { - return(i); - } - } - if (DebugMode) - printf("bootstrap.c: External Type: %s\n", type); - rdesc = heap_openr(TypeRelationName); - sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL); - i = 0; - while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL))) - ++i; - heap_endscan(sdesc); - app = Typ = ALLOC(struct typmap *, i + 1); - while (i-- > 0) - *app++ = ALLOC(struct typmap, 1); - *app = (struct typmap *)NULL; - sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL); - app = Typ; - while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL))) { - (*app)->am_oid = tup->t_oid; - memmove((char *)&(*app++)->am_typ, - (char *)GETSTRUCT(tup), - sizeof ((*app)->am_typ)); - } - heap_endscan(sdesc); - heap_close(rdesc); - return(gettype(type)); - } - elog(WARN, "Error: unknown type '%s'.\n", type); - err_out(); - /* not reached, here to make compiler happy */ - return 0; + int i; + Relation rdesc; + HeapScanDesc sdesc; + HeapTuple tup; + struct typmap **app; + + if (Typ != (struct typmap **) NULL) + { + for (app = Typ; *app != (struct typmap *) NULL; app++) + { + if (strncmp((*app)->am_typ.typname.data, type, NAMEDATALEN) == 0) + { + Ap = *app; + return ((*app)->am_oid); + } + } + } + else + { + for (i = 0; i <= n_types; i++) + { + if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0) + { + return (i); + } + } + if (DebugMode) + printf("bootstrap.c: External Type: %s\n", type); + rdesc = heap_openr(TypeRelationName); + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey) NULL); + i = 0; + while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *) NULL))) + ++i; + heap_endscan(sdesc); + app = Typ = ALLOC(struct typmap *, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = (struct typmap *) NULL; + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey) NULL); + app = Typ; + while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *) NULL))) + { + (*app)->am_oid = tup->t_oid; + memmove((char *) &(*app++)->am_typ, + (char *) GETSTRUCT(tup), + sizeof((*app)->am_typ)); + } + heap_endscan(sdesc); + heap_close(rdesc); + return (gettype(type)); + } + elog(WARN, "Error: unknown type '%s'.\n", type); + err_out(); + /* not reached, here to make compiler happy */ + return 0; } /* ---------------- - * AllocateAttribute + * AllocateAttribute * ---------------- */ -static AttributeTupleForm /* XXX */ +static AttributeTupleForm /* XXX */ AllocateAttribute() { - AttributeTupleForm attribute = - (AttributeTupleForm)malloc(ATTRIBUTE_TUPLE_SIZE); - - if (!PointerIsValid(attribute)) { - elog(FATAL, "AllocateAttribute: malloc failed"); - } - memset(attribute, 0, ATTRIBUTE_TUPLE_SIZE); - - return (attribute); + AttributeTupleForm attribute = + (AttributeTupleForm) malloc(ATTRIBUTE_TUPLE_SIZE); + + if (!PointerIsValid(attribute)) + { + elog(FATAL, "AllocateAttribute: malloc failed"); + } + memset(attribute, 0, ATTRIBUTE_TUPLE_SIZE); + + return (attribute); } /* ---------------- - * MapArrayTypeName + * MapArrayTypeName * XXX arrays of "basetype" are always "_basetype". - * this is an evil hack inherited from rel. 3.1. + * this is an evil hack inherited from rel. 3.1. * XXX array dimension is thrown away because we - * don't support fixed-dimension arrays. again, - * sickness from 3.1. - * - * the string passed in must have a '[' character in it + * don't support fixed-dimension arrays. again, + * sickness from 3.1. + * + * the string passed in must have a '[' character in it * * the string returned is a pointer to static storage and should NOT * be freed by the CALLER. * ---------------- */ -char* +char * MapArrayTypeName(char *s) { - int i, j; - static char newStr[NAMEDATALEN]; /* array type names < NAMEDATALEN long */ + int i, + j; + static char newStr[NAMEDATALEN]; /* array type names < + * NAMEDATALEN long */ - if (s == NULL || s[0] == '\0') - return s; + if (s == NULL || s[0] == '\0') + return s; - j = 1; - newStr[0] = '_'; - for (i=0; i<NAMEDATALEN-1 && s[i] != '['; i++, j++) - newStr[j] = s[i]; - - newStr[j] = '\0'; + j = 1; + newStr[0] = '_'; + for (i = 0; i < NAMEDATALEN - 1 && s[i] != '['; i++, j++) + newStr[j] = s[i]; - return newStr; + newStr[j] = '\0'; + + return newStr; } /* ---------------- - * EnterString - * returns the string table position of the identifier - * passed to it. We add it to the table if we can't find it. + * EnterString + * returns the string table position of the identifier + * passed to it. We add it to the table if we can't find it. * ---------------- */ int -EnterString (char *str) +EnterString(char *str) { - hashnode *node; - int len; - - len= strlen(str); - - node = FindStr(str, len, 0); - if (node) { - return (node->strnum); - } else { - node = AddStr(str, len, 0); - return (node->strnum); - } + hashnode *node; + int len; + + len = strlen(str); + + node = FindStr(str, len, 0); + if (node) + { + return (node->strnum); + } + else + { + node = AddStr(str, len, 0); + return (node->strnum); + } } /* ---------------- - * LexIDStr - * when given an idnum into the 'string-table' return the string - * associated with the idnum + * LexIDStr + * when given an idnum into the 'string-table' return the string + * associated with the idnum * ---------------- */ -char * -LexIDStr(int ident_num) +char * +LexIDStr(int ident_num) { - return(strtable[ident_num]); -} + return (strtable[ident_num]); +} /* ---------------- - * CompHash + * CompHash * - * Compute a hash function for a given string. We look at the first, - * the last, and the middle character of a string to try to get spread - * the strings out. The function is rather arbitrary, except that we - * are mod'ing by a prime number. + * Compute a hash function for a given string. We look at the first, + * the last, and the middle character of a string to try to get spread + * the strings out. The function is rather arbitrary, except that we + * are mod'ing by a prime number. * ---------------- */ static int CompHash(char *str, int len) { - register int result; - - result =(NUM * str[0] + NUMSQR * str[len-1] + NUMCUBE * str[(len-1)/2]); - - return (result % HASHTABLESIZE); - + register int result; + + result = (NUM * str[0] + NUMSQR * str[len - 1] + NUMCUBE * str[(len - 1) / 2]); + + return (result % HASHTABLESIZE); + } /* ---------------- - * FindStr + * FindStr * - * This routine looks for the specified string in the hash - * table. It returns a pointer to the hash node found, - * or NULL if the string is not in the table. + * This routine looks for the specified string in the hash + * table. It returns a pointer to the hash node found, + * or NULL if the string is not in the table. * ---------------- */ static hashnode * -FindStr(char *str, int length, hashnode *mderef) +FindStr(char *str, int length, hashnode * mderef) { - hashnode *node; - node = hashtable [CompHash (str, length)]; - while (node != NULL) { - /* - * We must differentiate between string constants that - * might have the same value as a identifier - * and the identifier itself. - */ - if (!strcmp(str, strtable[node->strnum])) { - return(node); /* no need to check */ - } else { - node = node->next; - } - } - /* Couldn't find it in the list */ - return (NULL); + hashnode *node; + + node = hashtable[CompHash(str, length)]; + while (node != NULL) + { + + /* + * We must differentiate between string constants that might have + * the same value as a identifier and the identifier itself. + */ + if (!strcmp(str, strtable[node->strnum])) + { + return (node); /* no need to check */ + } + else + { + node = node->next; + } + } + /* Couldn't find it in the list */ + return (NULL); } /* ---------------- - * AddStr + * AddStr * - * This function adds the specified string, along with its associated - * data, to the hash table and the string table. We return the node - * so that the calling routine can find out the unique id that AddStr - * has assigned to this string. + * This function adds the specified string, along with its associated + * data, to the hash table and the string table. We return the node + * so that the calling routine can find out the unique id that AddStr + * has assigned to this string. * ---------------- */ static hashnode * AddStr(char *str, int strlength, int mderef) { - hashnode *temp, *trail, *newnode; - int hashresult; - int len; - - if (++strtable_end == STRTABLESIZE) { - /* Error, string table overflow, so we Punt */ - elog(FATAL, - "There are too many string constants and identifiers for the compiler to handle."); - - - } - - /* - * Some of the utilites (eg, define type, create relation) assume - * that the string they're passed is a NAMEDATALEN. We get array bound - * read violations from purify if we don't allocate at least NAMEDATALEN - * bytes for strings of this sort. Because we're lazy, we allocate - * at least NAMEDATALEN bytes all the time. - */ - - if ((len = strlength + 1) < NAMEDATALEN) - len = NAMEDATALEN; - - strtable [strtable_end] = malloc((unsigned) len); - strcpy (strtable[strtable_end], str); - - /* Now put a node in the hash table */ - - newnode = (hashnode*)malloc(sizeof(hashnode)*1); - newnode->strnum = strtable_end; - newnode->next = NULL; - - /* Find out where it goes */ - - hashresult = CompHash (str, strlength); - if (hashtable [hashresult] == NULL) { - hashtable [hashresult] = newnode; - } else { /* There is something in the list */ - trail = hashtable [hashresult]; - temp = trail->next; - while (temp != NULL) { - trail = temp; - temp = temp->next; - } - trail->next = newnode; - } - return (newnode); + hashnode *temp, + *trail, + *newnode; + int hashresult; + int len; + + if (++strtable_end == STRTABLESIZE) + { + /* Error, string table overflow, so we Punt */ + elog(FATAL, + "There are too many string constants and identifiers for the compiler to handle."); + + + } + + /* + * Some of the utilites (eg, define type, create relation) assume that + * the string they're passed is a NAMEDATALEN. We get array bound + * read violations from purify if we don't allocate at least + * NAMEDATALEN bytes for strings of this sort. Because we're lazy, we + * allocate at least NAMEDATALEN bytes all the time. + */ + + if ((len = strlength + 1) < NAMEDATALEN) + len = NAMEDATALEN; + + strtable[strtable_end] = malloc((unsigned) len); + strcpy(strtable[strtable_end], str); + + /* Now put a node in the hash table */ + + newnode = (hashnode *) malloc(sizeof(hashnode) * 1); + newnode->strnum = strtable_end; + newnode->next = NULL; + + /* Find out where it goes */ + + hashresult = CompHash(str, strlength); + if (hashtable[hashresult] == NULL) + { + hashtable[hashresult] = newnode; + } + else + { /* There is something in the list */ + trail = hashtable[hashresult]; + temp = trail->next; + while (temp != NULL) + { + trail = temp; + temp = temp->next; + } + trail->next = newnode; + } + return (newnode); } /* - * index_register() -- record an index that has been set up for building - * later. + * index_register() -- record an index that has been set up for building + * later. * - * At bootstrap time, we define a bunch of indices on system catalogs. - * We postpone actually building the indices until just before we're - * finished with initialization, however. This is because more classes - * and indices may be defined, and we want to be sure that all of them - * are present in the index. + * At bootstrap time, we define a bunch of indices on system catalogs. + * We postpone actually building the indices until just before we're + * finished with initialization, however. This is because more classes + * and indices may be defined, and we want to be sure that all of them + * are present in the index. */ void index_register(char *heap, - char *ind, - int natts, - AttrNumber *attnos, - uint16 nparams, - Datum *params, - FuncIndexInfo *finfo, - PredInfo *predInfo) + char *ind, + int natts, + AttrNumber * attnos, + uint16 nparams, + Datum * params, + FuncIndexInfo * finfo, + PredInfo * predInfo) { - Datum *v; - IndexList *newind; - int len; - MemoryContext oldcxt; - - /* - * XXX mao 10/31/92 -- don't gc index reldescs, associated info - * at bootstrap time. we'll declare the indices now, but want to - * create them later. - */ - - if (nogc == (GlobalMemory) NULL) - nogc = CreateGlobalMemory("BootstrapNoGC"); - - oldcxt = MemoryContextSwitchTo((MemoryContext) nogc); - - newind = (IndexList *) palloc(sizeof(IndexList)); - newind->il_heap = pstrdup(heap); - newind->il_ind = pstrdup(ind); - newind->il_natts = natts; - - if (PointerIsValid(finfo)) - len = FIgetnArgs(finfo) * sizeof(AttrNumber); - else - len = natts * sizeof(AttrNumber); - - newind->il_attnos = (AttrNumber *) palloc(len); - memmove(newind->il_attnos, attnos, len); - - if ((newind->il_nparams = nparams) > 0) { - v = newind->il_params = (Datum *) palloc(2 * nparams * sizeof(Datum)); - nparams *= 2; - while (nparams-- > 0) { - *v = (Datum) palloc(strlen((char *)(*params)) + 1); - strcpy((char *) *v++, (char *) *params++); - } - } else { - newind->il_params = (Datum *) NULL; - } - - if (finfo != (FuncIndexInfo *) NULL) { - newind->il_finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo)); - memmove(newind->il_finfo, finfo, sizeof(FuncIndexInfo)); - } else { - newind->il_finfo = (FuncIndexInfo *) NULL; - } - - if (predInfo != NULL) { - newind->il_predInfo = (PredInfo*)palloc(sizeof(PredInfo)); - newind->il_predInfo->pred = predInfo->pred; - newind->il_predInfo->oldPred = predInfo->oldPred; - } else { - newind->il_predInfo = NULL; - } - - newind->il_next = ILHead; - - ILHead = newind; - - MemoryContextSwitchTo(oldcxt); + Datum *v; + IndexList *newind; + int len; + MemoryContext oldcxt; + + /* + * XXX mao 10/31/92 -- don't gc index reldescs, associated info at + * bootstrap time. we'll declare the indices now, but want to create + * them later. + */ + + if (nogc == (GlobalMemory) NULL) + nogc = CreateGlobalMemory("BootstrapNoGC"); + + oldcxt = MemoryContextSwitchTo((MemoryContext) nogc); + + newind = (IndexList *) palloc(sizeof(IndexList)); + newind->il_heap = pstrdup(heap); + newind->il_ind = pstrdup(ind); + newind->il_natts = natts; + + if (PointerIsValid(finfo)) + len = FIgetnArgs(finfo) * sizeof(AttrNumber); + else + len = natts * sizeof(AttrNumber); + + newind->il_attnos = (AttrNumber *) palloc(len); + memmove(newind->il_attnos, attnos, len); + + if ((newind->il_nparams = nparams) > 0) + { + v = newind->il_params = (Datum *) palloc(2 * nparams * sizeof(Datum)); + nparams *= 2; + while (nparams-- > 0) + { + *v = (Datum) palloc(strlen((char *) (*params)) + 1); + strcpy((char *) *v++, (char *) *params++); + } + } + else + { + newind->il_params = (Datum *) NULL; + } + + if (finfo != (FuncIndexInfo *) NULL) + { + newind->il_finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo)); + memmove(newind->il_finfo, finfo, sizeof(FuncIndexInfo)); + } + else + { + newind->il_finfo = (FuncIndexInfo *) NULL; + } + + if (predInfo != NULL) + { + newind->il_predInfo = (PredInfo *) palloc(sizeof(PredInfo)); + newind->il_predInfo->pred = predInfo->pred; + newind->il_predInfo->oldPred = predInfo->oldPred; + } + else + { + newind->il_predInfo = NULL; + } + + newind->il_next = ILHead; + + ILHead = newind; + + MemoryContextSwitchTo(oldcxt); } void build_indices() { - Relation heap; - Relation ind; - - for ( ; ILHead != (IndexList *) NULL; ILHead = ILHead->il_next) { - heap = heap_openr(ILHead->il_heap); - ind = index_openr(ILHead->il_ind); - index_build(heap, ind, ILHead->il_natts, ILHead->il_attnos, - ILHead->il_nparams, ILHead->il_params, ILHead->il_finfo, - ILHead->il_predInfo); - - /* - * All of the rest of this routine is needed only because in bootstrap - * processing we don't increment xact id's. The normal DefineIndex - * code replaces a pg_class tuple with updated info including the - * relhasindex flag (which we need to have updated). Unfortunately, - * there are always two indices defined on each catalog causing us to - * update the same pg_class tuple twice for each catalog getting an - * index during bootstrap resulting in the ghost tuple problem (see - * heap_replace). To get around this we change the relhasindex - * field ourselves in this routine keeping track of what catalogs we - * already changed so that we don't modify those tuples twice. The - * normal mechanism for updating pg_class is disabled during bootstrap. - * - * -mer - */ - heap = heap_openr(ILHead->il_heap); - - if (!BootstrapAlreadySeen(heap->rd_id)) - UpdateStats(heap->rd_id, 0, true); - } + Relation heap; + Relation ind; + + for (; ILHead != (IndexList *) NULL; ILHead = ILHead->il_next) + { + heap = heap_openr(ILHead->il_heap); + ind = index_openr(ILHead->il_ind); + index_build(heap, ind, ILHead->il_natts, ILHead->il_attnos, + ILHead->il_nparams, ILHead->il_params, ILHead->il_finfo, + ILHead->il_predInfo); + + /* + * All of the rest of this routine is needed only because in + * bootstrap processing we don't increment xact id's. The normal + * DefineIndex code replaces a pg_class tuple with updated info + * including the relhasindex flag (which we need to have updated). + * Unfortunately, there are always two indices defined on each + * catalog causing us to update the same pg_class tuple twice for + * each catalog getting an index during bootstrap resulting in the + * ghost tuple problem (see heap_replace). To get around this we + * change the relhasindex field ourselves in this routine keeping + * track of what catalogs we already changed so that we don't + * modify those tuples twice. The normal mechanism for updating + * pg_class is disabled during bootstrap. + * + * -mer + */ + heap = heap_openr(ILHead->il_heap); + + if (!BootstrapAlreadySeen(heap->rd_id)) + UpdateStats(heap->rd_id, 0, true); + } } - diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 90e6dedc18d..a8abbb01eee 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * catalog.c-- - * + * * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/catalog.c,v 1.7 1997/08/18 20:51:59 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/catalog.c,v 1.8 1997/09/07 04:40:00 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,7 +15,7 @@ #include <postgres.h> -#include <miscadmin.h> /* for DataDir */ +#include <miscadmin.h> /* for DataDir */ #include <utils/syscache.h> #include <catalog/catname.h> /* NameIs{,Shared}SystemRelationName */ #include <catalog/pg_type.h> @@ -23,175 +23,188 @@ #include <access/transam.h> /* - * relpath - path to the relation - * Perhaps this should be in-line code in relopen(). + * relpath - path to the relation + * Perhaps this should be in-line code in relopen(). */ -char * +char * relpath(char relname[]) { - char *path; - - if (IsSharedSystemRelationName(relname)) { - path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2); - sprintf(path, "%s/%s", DataDir, relname); - return (path); - } - return(relname); + char *path; + + if (IsSharedSystemRelationName(relname)) + { + path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2); + sprintf(path, "%s/%s", DataDir, relname); + return (path); + } + return (relname); } #ifdef NOT_USED /* - * issystem - returns non-zero iff relname is a system catalog + * issystem - returns non-zero iff relname is a system catalog * - * We now make a new requirement where system catalog relns must begin - * with pg_ while user relns are forbidden to do so. Make the test - * trivial and instantaneous. + * We now make a new requirement where system catalog relns must begin + * with pg_ while user relns are forbidden to do so. Make the test + * trivial and instantaneous. * - * XXX this is way bogus. -- pma + * XXX this is way bogus. -- pma */ bool issystem(char relname[]) { - if (relname[0] && relname[1] && relname[2]) - return (relname[0] == 'p' && - relname[1] == 'g' && - relname[2] == '_'); - else - return FALSE; + if (relname[0] && relname[1] && relname[2]) + return (relname[0] == 'p' && + relname[1] == 'g' && + relname[2] == '_'); + else + return FALSE; } + #endif /* * IsSystemRelationName -- - * True iff name is the name of a system catalog relation. + * True iff name is the name of a system catalog relation. * - * We now make a new requirement where system catalog relns must begin - * with pg_ while user relns are forbidden to do so. Make the test - * trivial and instantaneous. + * We now make a new requirement where system catalog relns must begin + * with pg_ while user relns are forbidden to do so. Make the test + * trivial and instantaneous. * - * XXX this is way bogus. -- pma + * XXX this is way bogus. -- pma */ bool IsSystemRelationName(char *relname) { - if (relname[0] && relname[1] && relname[2]) - return (relname[0] == 'p' && - relname[1] == 'g' && - relname[2] == '_'); - else - return FALSE; + if (relname[0] && relname[1] && relname[2]) + return (relname[0] == 'p' && + relname[1] == 'g' && + relname[2] == '_'); + else + return FALSE; } /* * IsSharedSystemRelationName -- - * True iff name is the name of a shared system catalog relation. + * True iff name is the name of a shared system catalog relation. */ bool IsSharedSystemRelationName(char *relname) { - int i; - - /* - * Quick out: if it's not a system relation, it can't be a shared - * system relation. - */ - if (!IsSystemRelationName(relname)) + int i; + + /* + * Quick out: if it's not a system relation, it can't be a shared + * system relation. + */ + if (!IsSystemRelationName(relname)) + return FALSE; + + i = 0; + while (SharedSystemRelationNames[i] != NULL) + { + if (strcmp(SharedSystemRelationNames[i], relname) == 0) + return TRUE; + i++; + } return FALSE; - - i = 0; - while ( SharedSystemRelationNames[i] != NULL) { - if (strcmp(SharedSystemRelationNames[i],relname) == 0) - return TRUE; - i++; - } - return FALSE; } /* - * newoid - returns a unique identifier across all catalogs. + * newoid - returns a unique identifier across all catalogs. * - * Object Id allocation is now done by GetNewObjectID in - * access/transam/varsup.c. oids are now allocated correctly. + * Object Id allocation is now done by GetNewObjectID in + * access/transam/varsup.c. oids are now allocated correctly. * * old comments: - * This needs to change soon, it fails if there are too many more - * than one call per second when postgres restarts after it dies. + * This needs to change soon, it fails if there are too many more + * than one call per second when postgres restarts after it dies. * - * The distribution of OID's should be done by the POSTMASTER. - * Also there needs to be a facility to preallocate OID's. Ie., - * for a block of OID's to be declared as invalid ones to allow - * user programs to use them for temporary object identifiers. + * The distribution of OID's should be done by the POSTMASTER. + * Also there needs to be a facility to preallocate OID's. Ie., + * for a block of OID's to be declared as invalid ones to allow + * user programs to use them for temporary object identifiers. */ -Oid newoid() +Oid +newoid() { - Oid lastoid; - - GetNewObjectId(&lastoid); - if (! OidIsValid(lastoid)) - elog(WARN, "newoid: GetNewObjectId returns invalid oid"); - return lastoid; + Oid lastoid; + + GetNewObjectId(&lastoid); + if (!OidIsValid(lastoid)) + elog(WARN, "newoid: GetNewObjectId returns invalid oid"); + return lastoid; } /* - * fillatt - fills the ATTRIBUTE relation fields from the TYP + * fillatt - fills the ATTRIBUTE relation fields from the TYP * - * Expects that the atttypid domain is set for each att[]. - * Returns with the attnum, and attlen domains set. - * attnum, attproc, atttyparg, ... should be set by the user. + * Expects that the atttypid domain is set for each att[]. + * Returns with the attnum, and attlen domains set. + * attnum, attproc, atttyparg, ... should be set by the user. * - * In the future, attnum may not be set?!? or may be passed as an arg?!? + * In the future, attnum may not be set?!? or may be passed as an arg?!? * - * Current implementation is very inefficient--should cashe the - * information if this is at all possible. + * Current implementation is very inefficient--should cashe the + * information if this is at all possible. * - * Check to see if this is really needed, and especially in the case - * of index tuples. + * Check to see if this is really needed, and especially in the case + * of index tuples. */ void fillatt(TupleDesc tupleDesc) { - AttributeTupleForm *attributeP; - register TypeTupleForm typp; - HeapTuple tuple; - int i; - int natts = tupleDesc->natts; - AttributeTupleForm *att = tupleDesc->attrs; - - if (natts < 0 || natts > MaxHeapAttributeNumber) - elog(WARN, "fillatt: %d attributes is too large", natts); - if (natts == 0) { - elog(DEBUG, "fillatt: called with natts == 0"); - return; - } - - attributeP = &att[0]; - - for (i = 0; i < natts;) { - tuple = SearchSysCacheTuple(TYPOID, - Int32GetDatum((*attributeP)->atttypid), - 0,0,0); - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "fillatt: unknown atttypid %ld", - (*attributeP)->atttypid); - } else { - (*attributeP)->attnum = (int16) ++i; - /* Check if the attr is a set before messing with the length - and byval, since those were already set in - TupleDescInitEntry. In fact, this seems redundant - here, but who knows what I'll break if I take it out... - - same for char() and varchar() stuff. I share the same - sentiments. This function is poorly written anyway. -ay 6/95 - */ - if (!(*attributeP)->attisset && - (*attributeP)->atttypid!=BPCHAROID && - (*attributeP)->atttypid!=VARCHAROID) { - - typp = (TypeTupleForm) GETSTRUCT(tuple); /* XXX */ - (*attributeP)->attlen = typp->typlen; - (*attributeP)->attbyval = typp->typbyval; - } + AttributeTupleForm *attributeP; + register TypeTupleForm typp; + HeapTuple tuple; + int i; + int natts = tupleDesc->natts; + AttributeTupleForm *att = tupleDesc->attrs; + + if (natts < 0 || natts > MaxHeapAttributeNumber) + elog(WARN, "fillatt: %d attributes is too large", natts); + if (natts == 0) + { + elog(DEBUG, "fillatt: called with natts == 0"); + return; + } + + attributeP = &att[0]; + + for (i = 0; i < natts;) + { + tuple = SearchSysCacheTuple(TYPOID, + Int32GetDatum((*attributeP)->atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "fillatt: unknown atttypid %ld", + (*attributeP)->atttypid); + } + else + { + (*attributeP)->attnum = (int16)++ i; + + /* + * Check if the attr is a set before messing with the length + * and byval, since those were already set in + * TupleDescInitEntry. In fact, this seems redundant here, + * but who knows what I'll break if I take it out... + * + * same for char() and varchar() stuff. I share the same + * sentiments. This function is poorly written anyway. -ay + * 6/95 + */ + if (!(*attributeP)->attisset && + (*attributeP)->atttypid != BPCHAROID && + (*attributeP)->atttypid != VARCHAROID) + { + + typp = (TypeTupleForm) GETSTRUCT(tuple); /* XXX */ + (*attributeP)->attlen = typp->typlen; + (*attributeP)->attbyval = typp->typbyval; + } + } + attributeP += 1; } - attributeP += 1; - } } diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index cbbbd9df004..c80ddb9727e 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -1,26 +1,26 @@ /*------------------------------------------------------------------------- * * heap.c-- - * code to create and destroy POSTGRES heap relations + * code to create and destroy POSTGRES heap relations * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.24 1997/09/05 18:13:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.25 1997/09/07 04:40:10 momjian Exp $ * * INTERFACE ROUTINES - * heap_creatr() - Create an uncataloged heap relation - * heap_create() - Create a cataloged relation - * heap_destroy() - Removes named relation from catalogs + * heap_creatr() - Create an uncataloged heap relation + * heap_create() - Create a cataloged relation + * heap_destroy() - Removes named relation from catalogs * * NOTES - * this code taken from access/heap/create.c, which contains - * the old heap_creater, amcreate, and amdestroy. those routines - * will soon call these routines using the function manager, - * just like the poorly named "NewXXX" routines do. The - * "New" routines are all going to die soon, once and for all! - * -cim 1/13/91 + * this code taken from access/heap/create.c, which contains + * the old heap_creater, amcreate, and amdestroy. those routines + * will soon call these routines using the function manager, + * just like the poorly named "NewXXX" routines do. The + * "New" routines are all going to die soon, once and for all! + * -cim 1/13/91 * *------------------------------------------------------------------------- */ @@ -53,103 +53,104 @@ #include <utils/relcache.h> #include <nodes/plannodes.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static void AddPgRelationTuple(Relation pg_class_desc, - Relation new_rel_desc, Oid new_rel_oid, int arch, unsigned natts); -static void AddToTempRelList(Relation r); -static void DeletePgAttributeTuples(Relation rdesc); -static void DeletePgRelationTuple(Relation rdesc); -static void DeletePgTypeTuple(Relation rdesc); -static int RelationAlreadyExists(Relation pg_class_desc, char relname[]); -static void RelationRemoveIndexes(Relation relation); -static void RelationRemoveInheritance(Relation relation); -static void RemoveFromTempRelList(Relation r); -static void addNewRelationType(char *typeName, Oid new_rel_oid); -static void StoreConstraints (Relation rel); -static void RemoveConstraints (Relation rel); +static void +AddPgRelationTuple(Relation pg_class_desc, + Relation new_rel_desc, Oid new_rel_oid, int arch, unsigned natts); +static void AddToTempRelList(Relation r); +static void DeletePgAttributeTuples(Relation rdesc); +static void DeletePgRelationTuple(Relation rdesc); +static void DeletePgTypeTuple(Relation rdesc); +static int RelationAlreadyExists(Relation pg_class_desc, char relname[]); +static void RelationRemoveIndexes(Relation relation); +static void RelationRemoveInheritance(Relation relation); +static void RemoveFromTempRelList(Relation r); +static void addNewRelationType(char *typeName, Oid new_rel_oid); +static void StoreConstraints(Relation rel); +static void RemoveConstraints(Relation rel); /* ---------------------------------------------------------------- - * XXX UGLY HARD CODED BADNESS FOLLOWS XXX + * XXX UGLY HARD CODED BADNESS FOLLOWS XXX * - * these should all be moved to someplace in the lib/catalog - * module, if not obliterated first. + * these should all be moved to someplace in the lib/catalog + * module, if not obliterated first. * ---------------------------------------------------------------- */ /* * Note: - * Should the executor special case these attributes in the future? - * Advantage: consume 1/2 the space in the ATTRIBUTE relation. - * Disadvantage: having rules to compute values in these tuples may - * be more difficult if not impossible. + * Should the executor special case these attributes in the future? + * Advantage: consume 1/2 the space in the ATTRIBUTE relation. + * Disadvantage: having rules to compute values in these tuples may + * be more difficult if not impossible. */ -static FormData_pg_attribute a1 = { - 0xffffffff, {"ctid"}, 27l, 0l, sizeof (ItemPointerData), - SelfItemPointerAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a1 = { + 0xffffffff, {"ctid"}, 27l, 0l, sizeof(ItemPointerData), + SelfItemPointerAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a2 = { - 0xffffffff, {"oid"}, 26l, 0l, sizeof(Oid), - ObjectIdAttributeNumber, 0, -1, '\001', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a2 = { + 0xffffffff, {"oid"}, 26l, 0l, sizeof(Oid), + ObjectIdAttributeNumber, 0, -1, '\001', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a3 = { - 0xffffffff, {"xmin"}, 28l, 0l, sizeof (TransactionId), - MinTransactionIdAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a3 = { + 0xffffffff, {"xmin"}, 28l, 0l, sizeof(TransactionId), + MinTransactionIdAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a4 = { - 0xffffffff, {"cmin"}, 29l, 0l, sizeof (CommandId), - MinCommandIdAttributeNumber, 0, -1, '\001', '\0', 's', '\0', '\0' +static FormData_pg_attribute a4 = { + 0xffffffff, {"cmin"}, 29l, 0l, sizeof(CommandId), + MinCommandIdAttributeNumber, 0, -1, '\001', '\0', 's', '\0', '\0' }; -static FormData_pg_attribute a5 = { - 0xffffffff, {"xmax"}, 28l, 0l, sizeof (TransactionId), - MaxTransactionIdAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a5 = { + 0xffffffff, {"xmax"}, 28l, 0l, sizeof(TransactionId), + MaxTransactionIdAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a6 = { - 0xffffffff, {"cmax"}, 29l, 0l, sizeof (CommandId), - MaxCommandIdAttributeNumber, 0, -1, '\001', '\0', 's', '\0', '\0' +static FormData_pg_attribute a6 = { + 0xffffffff, {"cmax"}, 29l, 0l, sizeof(CommandId), + MaxCommandIdAttributeNumber, 0, -1, '\001', '\0', 's', '\0', '\0' }; -static FormData_pg_attribute a7 = { - 0xffffffff, {"chain"}, 27l, 0l, sizeof (ItemPointerData), - ChainItemPointerAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a7 = { + 0xffffffff, {"chain"}, 27l, 0l, sizeof(ItemPointerData), + ChainItemPointerAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a8 = { - 0xffffffff, {"anchor"}, 27l, 0l, sizeof (ItemPointerData), - AnchorItemPointerAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a8 = { + 0xffffffff, {"anchor"}, 27l, 0l, sizeof(ItemPointerData), + AnchorItemPointerAttributeNumber, 0, -1, '\0', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a9 = { - 0xffffffff, {"tmin"}, 702l, 0l, sizeof (AbsoluteTime), - MinAbsoluteTimeAttributeNumber, 0, -1, '\001', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a9 = { + 0xffffffff, {"tmin"}, 702l, 0l, sizeof(AbsoluteTime), + MinAbsoluteTimeAttributeNumber, 0, -1, '\001', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a10 = { - 0xffffffff, {"tmax"}, 702l, 0l, sizeof (AbsoluteTime), - MaxAbsoluteTimeAttributeNumber, 0, -1, '\001', '\0', 'i', '\0', '\0' +static FormData_pg_attribute a10 = { + 0xffffffff, {"tmax"}, 702l, 0l, sizeof(AbsoluteTime), + MaxAbsoluteTimeAttributeNumber, 0, -1, '\001', '\0', 'i', '\0', '\0' }; -static FormData_pg_attribute a11 = { - 0xffffffff, {"vtype"}, 18l, 0l, sizeof (char), - VersionTypeAttributeNumber, 0, -1, '\001', '\0', 'c', '\0', '\0' +static FormData_pg_attribute a11 = { + 0xffffffff, {"vtype"}, 18l, 0l, sizeof(char), + VersionTypeAttributeNumber, 0, -1, '\001', '\0', 'c', '\0', '\0' }; -static AttributeTupleForm HeapAtt[] = -{ &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11 }; +static AttributeTupleForm HeapAtt[] = +{&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11}; /* ---------------------------------------------------------------- - * XXX END OF UGLY HARD CODED BADNESS XXX + * XXX END OF UGLY HARD CODED BADNESS XXX * ---------------------------------------------------------------- */ @@ -157,1227 +158,1261 @@ static AttributeTupleForm HeapAtt[] = the list of temporary uncatalogued relations that are created. these relations should be destroyed at the end of transactions */ -typedef struct tempRelList { - Relation *rels; /* array of relation descriptors */ - int num; /* number of temporary relations */ - int size; /* size of space allocated for the rels array */ -} TempRelList; +typedef struct tempRelList +{ + Relation *rels; /* array of relation descriptors */ + int num; /* number of temporary relations */ + int size; /* size of space allocated for the rels + * array */ +} TempRelList; -#define TEMP_REL_LIST_SIZE 32 +#define TEMP_REL_LIST_SIZE 32 -static TempRelList *tempRels = NULL; +static TempRelList *tempRels = NULL; /* ---------------------------------------------------------------- - * heap_creatr - Create an uncataloged heap relation + * heap_creatr - Create an uncataloged heap relation * - * Fields relpages, reltuples, reltuples, relkeys, relhistory, - * relisindexed, and relkind of rdesc->rd_rel are initialized - * to all zeros, as are rd_last and rd_hook. Rd_refcnt is set to 1. + * Fields relpages, reltuples, reltuples, relkeys, relhistory, + * relisindexed, and relkind of rdesc->rd_rel are initialized + * to all zeros, as are rd_last and rd_hook. Rd_refcnt is set to 1. * - * Remove the system relation specific code to elsewhere eventually. + * Remove the system relation specific code to elsewhere eventually. + * + * Eventually, must place information about this temporary relation + * into the transaction context block. * - * Eventually, must place information about this temporary relation - * into the transaction context block. * - * * if heap_creatr is called with "" as the name, then heap_creatr will create a - * temporary name "temp_$RELOID" for the relation + * temporary name "temp_$RELOID" for the relation * ---------------------------------------------------------------- */ Relation -heap_creatr(char *name, - unsigned smgr, - TupleDesc tupDesc) +heap_creatr(char *name, + unsigned smgr, + TupleDesc tupDesc) { - register unsigned i; - Oid relid; - Relation rdesc; - int len; - bool nailme = false; - char* relname = name; - char tempname[40]; - int isTemp = 0; - int natts = tupDesc->natts; -/* AttributeTupleForm *att = tupDesc->attrs; */ - - extern GlobalMemory CacheCxt; - MemoryContext oldcxt; - - /* ---------------- - * sanity checks - * ---------------- - */ - AssertArg(natts > 0); - - if (IsSystemRelationName(relname) && IsNormalProcessingMode()) + register unsigned i; + Oid relid; + Relation rdesc; + int len; + bool nailme = false; + char *relname = name; + char tempname[40]; + int isTemp = 0; + int natts = tupDesc->natts; + +/* AttributeTupleForm *att = tupDesc->attrs; */ + + extern GlobalMemory CacheCxt; + MemoryContext oldcxt; + + /* ---------------- + * sanity checks + * ---------------- + */ + AssertArg(natts > 0); + + if (IsSystemRelationName(relname) && IsNormalProcessingMode()) { - elog(WARN, + elog(WARN, "Illegal class name: %s -- pg_ is reserved for system catalogs", - relname); + relname); + } + + /* ---------------- + * switch to the cache context so that we don't lose + * allocations at the end of this transaction, I guess. + * -cim 6/14/90 + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + + /* ---------------- + * real ugly stuff to assign the proper relid in the relation + * descriptor follows. + * ---------------- + */ + if (!strcmp(RelationRelationName, relname)) + { + relid = RelOid_pg_class; + nailme = true; } - - /* ---------------- - * switch to the cache context so that we don't lose - * allocations at the end of this transaction, I guess. - * -cim 6/14/90 - * ---------------- - */ - if (!CacheCxt) - CacheCxt = CreateGlobalMemory("Cache"); - - oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); - - /* ---------------- - * real ugly stuff to assign the proper relid in the relation - * descriptor follows. - * ---------------- - */ - if (! strcmp(RelationRelationName,relname)) + else if (!strcmp(AttributeRelationName, relname)) { - relid = RelOid_pg_class; - nailme = true; + relid = RelOid_pg_attribute; + nailme = true; } - else if (! strcmp(AttributeRelationName,relname)) + else if (!strcmp(ProcedureRelationName, relname)) { - relid = RelOid_pg_attribute; - nailme = true; + relid = RelOid_pg_proc; + nailme = true; } - else if (! strcmp(ProcedureRelationName, relname)) + else if (!strcmp(TypeRelationName, relname)) { - relid = RelOid_pg_proc; - nailme = true; + relid = RelOid_pg_type; + nailme = true; } - else if (! strcmp(TypeRelationName,relname)) + else { - relid = RelOid_pg_type; - nailme = true; + relid = newoid(); + + if (name[0] == '\0') + { + sprintf(tempname, "temp_%d", relid); + relname = tempname; + isTemp = 1; + } } - else - { - relid = newoid(); - - if (name[0] == '\0') - { - sprintf(tempname, "temp_%d", relid); - relname = tempname; - isTemp = 1; - } - } - - /* ---------------- - * allocate a new relation descriptor. - * - * XXX the length computation may be incorrect, handle elsewhere - * ---------------- - */ - len = sizeof(RelationData); - - rdesc = (Relation) palloc(len); - memset((char *)rdesc, 0,len); - - /* ---------- - create a new tuple descriptor from the one passed in - */ - rdesc->rd_att = CreateTupleDescCopyConstr(tupDesc); - - /* ---------------- - * initialize the fields of our new relation descriptor - * ---------------- - */ - - /* ---------------- - * nail the reldesc if this is a bootstrap create reln and - * we may need it in the cache later on in the bootstrap - * process so we don't ever want it kicked out. e.g. pg_attribute!!! - * ---------------- - */ - if (nailme) - rdesc->rd_isnailed = true; - - RelationSetReferenceCount(rdesc, 1); - - rdesc->rd_rel = (Form_pg_class)palloc(sizeof *rdesc->rd_rel); - - memset((char *)rdesc->rd_rel, 0, - sizeof *rdesc->rd_rel); - namestrcpy(&(rdesc->rd_rel->relname), relname); - rdesc->rd_rel->relkind = RELKIND_UNCATALOGED; - rdesc->rd_rel->relnatts = natts; - rdesc->rd_rel->relsmgr = smgr; - if ( tupDesc->constr ) - rdesc->rd_rel->relchecks = tupDesc->constr->num_check; - - for (i = 0; i < natts; i++) { - rdesc->rd_att->attrs[i]->attrelid = relid; - } - - rdesc->rd_id = relid; - - if (nailme) { - /* for system relations, set the reltype field here */ - rdesc->rd_rel->reltype = relid; - } - - /* ---------------- - * remember if this is a temp relation - * ---------------- - */ - - rdesc->rd_istemp = isTemp; - - /* ---------------- - * have the storage manager create the relation. - * ---------------- - */ - - rdesc->rd_tmpunlinked = TRUE; /* change once table is created */ - rdesc->rd_fd = (File)smgrcreate(smgr, rdesc); - rdesc->rd_tmpunlinked = FALSE; - - RelationRegisterRelation(rdesc); - - MemoryContextSwitchTo(oldcxt); - - /* add all temporary relations to the tempRels list - so they can be properly disposed of at the end of transaction - */ - if (isTemp) - AddToTempRelList(rdesc); - - return (rdesc); + + /* ---------------- + * allocate a new relation descriptor. + * + * XXX the length computation may be incorrect, handle elsewhere + * ---------------- + */ + len = sizeof(RelationData); + + rdesc = (Relation) palloc(len); + memset((char *) rdesc, 0, len); + + /* ---------- + create a new tuple descriptor from the one passed in + */ + rdesc->rd_att = CreateTupleDescCopyConstr(tupDesc); + + /* ---------------- + * initialize the fields of our new relation descriptor + * ---------------- + */ + + /* ---------------- + * nail the reldesc if this is a bootstrap create reln and + * we may need it in the cache later on in the bootstrap + * process so we don't ever want it kicked out. e.g. pg_attribute!!! + * ---------------- + */ + if (nailme) + rdesc->rd_isnailed = true; + + RelationSetReferenceCount(rdesc, 1); + + rdesc->rd_rel = (Form_pg_class) palloc(sizeof *rdesc->rd_rel); + + memset((char *) rdesc->rd_rel, 0, + sizeof *rdesc->rd_rel); + namestrcpy(&(rdesc->rd_rel->relname), relname); + rdesc->rd_rel->relkind = RELKIND_UNCATALOGED; + rdesc->rd_rel->relnatts = natts; + rdesc->rd_rel->relsmgr = smgr; + if (tupDesc->constr) + rdesc->rd_rel->relchecks = tupDesc->constr->num_check; + + for (i = 0; i < natts; i++) + { + rdesc->rd_att->attrs[i]->attrelid = relid; + } + + rdesc->rd_id = relid; + + if (nailme) + { + /* for system relations, set the reltype field here */ + rdesc->rd_rel->reltype = relid; + } + + /* ---------------- + * remember if this is a temp relation + * ---------------- + */ + + rdesc->rd_istemp = isTemp; + + /* ---------------- + * have the storage manager create the relation. + * ---------------- + */ + + rdesc->rd_tmpunlinked = TRUE; /* change once table is created */ + rdesc->rd_fd = (File) smgrcreate(smgr, rdesc); + rdesc->rd_tmpunlinked = FALSE; + + RelationRegisterRelation(rdesc); + + MemoryContextSwitchTo(oldcxt); + + /* + * add all temporary relations to the tempRels list so they can be + * properly disposed of at the end of transaction + */ + if (isTemp) + AddToTempRelList(rdesc); + + return (rdesc); } /* ---------------------------------------------------------------- - * heap_create - Create a cataloged relation + * heap_create - Create a cataloged relation * - * this is done in 6 steps: + * this is done in 6 steps: * - * 1) CheckAttributeNames() is used to make certain the tuple - * descriptor contains a valid set of attribute names + * 1) CheckAttributeNames() is used to make certain the tuple + * descriptor contains a valid set of attribute names * - * 2) pg_class is opened and RelationAlreadyExists() - * preforms a scan to ensure that no relation with the - * same name already exists. + * 2) pg_class is opened and RelationAlreadyExists() + * preforms a scan to ensure that no relation with the + * same name already exists. * - * 3) heap_creater() is called to create the new relation on - * disk. + * 3) heap_creater() is called to create the new relation on + * disk. * - * 4) TypeDefine() is called to define a new type corresponding - * to the new relation. + * 4) TypeDefine() is called to define a new type corresponding + * to the new relation. * - * 5) AddNewAttributeTuples() is called to register the - * new relation's schema in pg_attribute. + * 5) AddNewAttributeTuples() is called to register the + * new relation's schema in pg_attribute. * - * 6) AddPgRelationTuple() is called to register the - * relation itself in the catalogs. - * - * 7) StoreConstraints is called () - vadim 08/22/97 + * 6) AddPgRelationTuple() is called to register the + * relation itself in the catalogs. * - * 8) the relations are closed and the new relation's oid - * is returned. + * 7) StoreConstraints is called () - vadim 08/22/97 + * + * 8) the relations are closed and the new relation's oid + * is returned. * * old comments: - * A new relation is inserted into the RELATION relation - * with the specified attribute(s) (newly inserted into - * the ATTRIBUTE relation). How does concurrency control - * work? Is it automatic now? Expects the caller to have - * attname, atttypid, atttyparg, attproc, and attlen domains filled. - * Create fills the attnum domains sequentually from zero, - * fills the attdisbursion domains with zeros, and fills the - * attrelid fields with the relid. + * A new relation is inserted into the RELATION relation + * with the specified attribute(s) (newly inserted into + * the ATTRIBUTE relation). How does concurrency control + * work? Is it automatic now? Expects the caller to have + * attname, atttypid, atttyparg, attproc, and attlen domains filled. + * Create fills the attnum domains sequentually from zero, + * fills the attdisbursion domains with zeros, and fills the + * attrelid fields with the relid. + * + * scan relation catalog for name conflict + * scan type catalog for typids (if not arg) + * create and insert attribute(s) into attribute catalog + * create new relation + * insert new relation into attribute catalog * - * scan relation catalog for name conflict - * scan type catalog for typids (if not arg) - * create and insert attribute(s) into attribute catalog - * create new relation - * insert new relation into attribute catalog + * Should coordinate with heap_creater(). Either it should + * not be called or there should be a way to prevent + * the relation from being removed at the end of the + * transaction if it is successful ('u'/'r' may be enough). + * Also, if the transaction does not commit, then the + * relation should be removed. * - * Should coordinate with heap_creater(). Either it should - * not be called or there should be a way to prevent - * the relation from being removed at the end of the - * transaction if it is successful ('u'/'r' may be enough). - * Also, if the transaction does not commit, then the - * relation should be removed. + * XXX amcreate ignores "off" when inserting (for now). + * XXX amcreate (like the other utilities) needs to understand indexes. * - * XXX amcreate ignores "off" when inserting (for now). - * XXX amcreate (like the other utilities) needs to understand indexes. - * * ---------------------------------------------------------------- */ /* -------------------------------- - * CheckAttributeNames + * CheckAttributeNames * - * this is used to make certain the tuple descriptor contains a - * valid set of attribute names. a problem simply generates - * elog(WARN) which aborts the current transaction. + * this is used to make certain the tuple descriptor contains a + * valid set of attribute names. a problem simply generates + * elog(WARN) which aborts the current transaction. * -------------------------------- */ static void CheckAttributeNames(TupleDesc tupdesc) { - unsigned i; - unsigned j; - int natts = tupdesc->natts; - - /* ---------------- - * first check for collision with system attribute names - * ---------------- - * - * also, warn user if attribute to be created has - * an unknown typid (usually as a result of a 'retrieve into' - * - jolly - */ - for (i = 0; i < natts; i += 1) { - for (j = 0; j < sizeof HeapAtt / sizeof HeapAtt[0]; j += 1) { - if (nameeq(&(HeapAtt[j]->attname), - &(tupdesc->attrs[i]->attname))) { - elog(WARN, - "create: system attribute named \"%s\"", - HeapAtt[j]->attname.data); - } + unsigned i; + unsigned j; + int natts = tupdesc->natts; + + /* ---------------- + * first check for collision with system attribute names + * ---------------- + * + * also, warn user if attribute to be created has + * an unknown typid (usually as a result of a 'retrieve into' + * - jolly + */ + for (i = 0; i < natts; i += 1) + { + for (j = 0; j < sizeof HeapAtt / sizeof HeapAtt[0]; j += 1) + { + if (nameeq(&(HeapAtt[j]->attname), + &(tupdesc->attrs[i]->attname))) + { + elog(WARN, + "create: system attribute named \"%s\"", + HeapAtt[j]->attname.data); + } + } + if (tupdesc->attrs[i]->atttypid == UNKNOWNOID) + { + elog(NOTICE, + "create: attribute named \"%s\" has an unknown type", + tupdesc->attrs[i]->attname.data); + } } - if (tupdesc->attrs[i]->atttypid == UNKNOWNOID) - { - elog(NOTICE, - "create: attribute named \"%s\" has an unknown type", - tupdesc->attrs[i]->attname.data); - } - } - - /* ---------------- - * next check for repeated attribute names - * ---------------- - */ - for (i = 1; i < natts; i += 1) { - for (j = 0; j < i; j += 1) { - if (nameeq(&(tupdesc->attrs[j]->attname), - &(tupdesc->attrs[i]->attname))) { - elog(WARN, - "create: repeated attribute \"%s\"", - tupdesc->attrs[j]->attname.data); - } + + /* ---------------- + * next check for repeated attribute names + * ---------------- + */ + for (i = 1; i < natts; i += 1) + { + for (j = 0; j < i; j += 1) + { + if (nameeq(&(tupdesc->attrs[j]->attname), + &(tupdesc->attrs[i]->attname))) + { + elog(WARN, + "create: repeated attribute \"%s\"", + tupdesc->attrs[j]->attname.data); + } + } } - } } /* -------------------------------- - * RelationAlreadyExists + * RelationAlreadyExists * - * this preforms a scan of pg_class to ensure that - * no relation with the same name already exists. The caller - * has to open pg_class and pass an open descriptor. + * this preforms a scan of pg_class to ensure that + * no relation with the same name already exists. The caller + * has to open pg_class and pass an open descriptor. * -------------------------------- */ static int RelationAlreadyExists(Relation pg_class_desc, char relname[]) { - ScanKeyData key; - HeapScanDesc pg_class_scan; - HeapTuple tup; - - /* - * If this is not bootstrap (initdb) time, use the catalog index - * on pg_class. - */ - - if (!IsBootstrapProcessingMode()) { - tup = ClassNameIndexScan(pg_class_desc, relname); - if (HeapTupleIsValid(tup)) { - pfree(tup); - return ((int) true); - } else - return ((int) false); - } - - /* ---------------- - * At bootstrap time, we have to do this the hard way. Form the - * scan key. - * ---------------- - */ - ScanKeyEntryInitialize(&key, - 0, - (AttrNumber)Anum_pg_class_relname, - (RegProcedure)NameEqualRegProcedure, - (Datum) relname); - - /* ---------------- - * begin the scan - * ---------------- - */ - pg_class_scan = heap_beginscan(pg_class_desc, - 0, - NowTimeQual, - 1, - &key); - - /* ---------------- - * get a tuple. if the tuple is NULL then it means we - * didn't find an existing relation. - * ---------------- - */ - tup = heap_getnext(pg_class_scan, 0, (Buffer *)NULL); - - /* ---------------- - * end the scan and return existance of relation. - * ---------------- - */ - heap_endscan(pg_class_scan); - - return - (PointerIsValid(tup) == true); + ScanKeyData key; + HeapScanDesc pg_class_scan; + HeapTuple tup; + + /* + * If this is not bootstrap (initdb) time, use the catalog index on + * pg_class. + */ + + if (!IsBootstrapProcessingMode()) + { + tup = ClassNameIndexScan(pg_class_desc, relname); + if (HeapTupleIsValid(tup)) + { + pfree(tup); + return ((int) true); + } + else + return ((int) false); + } + + /* ---------------- + * At bootstrap time, we have to do this the hard way. Form the + * scan key. + * ---------------- + */ + ScanKeyEntryInitialize(&key, + 0, + (AttrNumber) Anum_pg_class_relname, + (RegProcedure) NameEqualRegProcedure, + (Datum) relname); + + /* ---------------- + * begin the scan + * ---------------- + */ + pg_class_scan = heap_beginscan(pg_class_desc, + 0, + NowTimeQual, + 1, + &key); + + /* ---------------- + * get a tuple. if the tuple is NULL then it means we + * didn't find an existing relation. + * ---------------- + */ + tup = heap_getnext(pg_class_scan, 0, (Buffer *) NULL); + + /* ---------------- + * end the scan and return existance of relation. + * ---------------- + */ + heap_endscan(pg_class_scan); + + return + (PointerIsValid(tup) == true); } /* -------------------------------- - * AddNewAttributeTuples + * AddNewAttributeTuples * - * this registers the new relation's schema by adding - * tuples to pg_attribute. + * this registers the new relation's schema by adding + * tuples to pg_attribute. * -------------------------------- */ static void AddNewAttributeTuples(Oid new_rel_oid, - TupleDesc tupdesc) + TupleDesc tupdesc) { - AttributeTupleForm *dpp; - unsigned i; - HeapTuple tup; - Relation rdesc; - bool hasindex; - Relation idescs[Num_pg_attr_indices]; - int natts = tupdesc->natts; - - /* ---------------- - * open pg_attribute - * ---------------- - */ - rdesc = heap_openr(AttributeRelationName); - - /* ----------------- - * Check if we have any indices defined on pg_attribute. - * ----------------- - */ - Assert(rdesc); - Assert(rdesc->rd_rel); - hasindex = RelationGetRelationTupleForm(rdesc)->relhasindex; - if (hasindex) - CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); - - /* ---------------- - * initialize tuple descriptor. Note we use setheapoverride() - * so that we can see the effects of our TypeDefine() done - * previously. - * ---------------- - */ - setheapoverride(true); - fillatt(tupdesc); - setheapoverride(false); - - /* ---------------- - * first we add the user attributes.. - * ---------------- - */ - dpp = tupdesc->attrs; - for (i = 0; i < natts; i++) { - (*dpp)->attrelid = new_rel_oid; - (*dpp)->attdisbursion = 0; - - tup = heap_addheader(Natts_pg_attribute, - ATTRIBUTE_TUPLE_SIZE, - (char *) *dpp); - - heap_insert(rdesc, tup); + AttributeTupleForm *dpp; + unsigned i; + HeapTuple tup; + Relation rdesc; + bool hasindex; + Relation idescs[Num_pg_attr_indices]; + int natts = tupdesc->natts; + + /* ---------------- + * open pg_attribute + * ---------------- + */ + rdesc = heap_openr(AttributeRelationName); + + /* ----------------- + * Check if we have any indices defined on pg_attribute. + * ----------------- + */ + Assert(rdesc); + Assert(rdesc->rd_rel); + hasindex = RelationGetRelationTupleForm(rdesc)->relhasindex; if (hasindex) - CatalogIndexInsert(idescs, Num_pg_attr_indices, rdesc, tup); - - pfree(tup); - dpp++; - } - - /* ---------------- - * next we add the system attributes.. - * ---------------- - */ - dpp = HeapAtt; - for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++) { - (*dpp)->attrelid = new_rel_oid; - /* (*dpp)->attdisbursion = 0; unneeded */ - - tup = heap_addheader(Natts_pg_attribute, - ATTRIBUTE_TUPLE_SIZE, - (char *)*dpp); - - heap_insert(rdesc, tup); - + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); + + /* ---------------- + * initialize tuple descriptor. Note we use setheapoverride() + * so that we can see the effects of our TypeDefine() done + * previously. + * ---------------- + */ + setheapoverride(true); + fillatt(tupdesc); + setheapoverride(false); + + /* ---------------- + * first we add the user attributes.. + * ---------------- + */ + dpp = tupdesc->attrs; + for (i = 0; i < natts; i++) + { + (*dpp)->attrelid = new_rel_oid; + (*dpp)->attdisbursion = 0; + + tup = heap_addheader(Natts_pg_attribute, + ATTRIBUTE_TUPLE_SIZE, + (char *) *dpp); + + heap_insert(rdesc, tup); + if (hasindex) + CatalogIndexInsert(idescs, Num_pg_attr_indices, rdesc, tup); + + pfree(tup); + dpp++; + } + + /* ---------------- + * next we add the system attributes.. + * ---------------- + */ + dpp = HeapAtt; + for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++) + { + (*dpp)->attrelid = new_rel_oid; + /* (*dpp)->attdisbursion = 0; unneeded */ + + tup = heap_addheader(Natts_pg_attribute, + ATTRIBUTE_TUPLE_SIZE, + (char *) *dpp); + + heap_insert(rdesc, tup); + + if (hasindex) + CatalogIndexInsert(idescs, Num_pg_attr_indices, rdesc, tup); + + pfree(tup); + dpp++; + } + + heap_close(rdesc); + + /* + * close pg_attribute indices + */ if (hasindex) - CatalogIndexInsert(idescs, Num_pg_attr_indices, rdesc, tup); - - pfree(tup); - dpp++; - } - - heap_close(rdesc); - - /* - * close pg_attribute indices - */ - if (hasindex) - CatalogCloseIndices(Num_pg_attr_indices, idescs); + CatalogCloseIndices(Num_pg_attr_indices, idescs); } /* -------------------------------- - * AddPgRelationTuple + * AddPgRelationTuple * - * this registers the new relation in the catalogs by - * adding a tuple to pg_class. + * this registers the new relation in the catalogs by + * adding a tuple to pg_class. * -------------------------------- */ static void AddPgRelationTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, - int arch, - unsigned natts) + Relation new_rel_desc, + Oid new_rel_oid, + int arch, + unsigned natts) { - Form_pg_class new_rel_reltup; - HeapTuple tup; - Relation idescs[Num_pg_class_indices]; - bool isBootstrap; - extern bool ItsSequenceCreation; /* It's hack, I know... - * - vadim 03/28/97 */ - /* ---------------- - * first we munge some of the information in our - * uncataloged relation's relation descriptor. - * ---------------- - */ - new_rel_reltup = new_rel_desc->rd_rel; - - /* CHECK should get new_rel_oid first via an insert then use XXX */ - /* new_rel_reltup->reltuples = 1; */ /* XXX */ - - new_rel_reltup->relowner = GetUserId(); - if ( ItsSequenceCreation ) - new_rel_reltup->relkind = RELKIND_SEQUENCE; - else - new_rel_reltup->relkind = RELKIND_RELATION; - new_rel_reltup->relarch = arch; - new_rel_reltup->relnatts = natts; - - /* ---------------- - * now form a tuple to add to pg_class - * XXX Natts_pg_class_fixed is a hack - see pg_class.h - * ---------------- - */ - tup = heap_addheader(Natts_pg_class_fixed, - CLASS_TUPLE_SIZE, - (char *) new_rel_reltup); - tup->t_oid = new_rel_oid; - - /* ---------------- - * finally insert the new tuple and free it. - * - * Note: I have no idea why we do a - * SetProcessingMode(BootstrapProcessing); - * here -cim 6/14/90 - * ---------------- - */ - isBootstrap = IsBootstrapProcessingMode() ? true : false; - - SetProcessingMode(BootstrapProcessing); - - heap_insert(pg_class_desc, tup); - - if (! isBootstrap) { - /* - * First, open the catalog indices and insert index tuples for - * the new relation. - */ - - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class_desc, tup); - CatalogCloseIndices(Num_pg_class_indices, idescs); - - /* now restore processing mode */ - SetProcessingMode(NormalProcessing); - } - - pfree(tup); + Form_pg_class new_rel_reltup; + HeapTuple tup; + Relation idescs[Num_pg_class_indices]; + bool isBootstrap; + extern bool ItsSequenceCreation; /* It's hack, I know... - + * vadim 03/28/97 */ + + /* ---------------- + * first we munge some of the information in our + * uncataloged relation's relation descriptor. + * ---------------- + */ + new_rel_reltup = new_rel_desc->rd_rel; + + /* CHECK should get new_rel_oid first via an insert then use XXX */ + /* new_rel_reltup->reltuples = 1; *//* XXX */ + + new_rel_reltup->relowner = GetUserId(); + if (ItsSequenceCreation) + new_rel_reltup->relkind = RELKIND_SEQUENCE; + else + new_rel_reltup->relkind = RELKIND_RELATION; + new_rel_reltup->relarch = arch; + new_rel_reltup->relnatts = natts; + + /* ---------------- + * now form a tuple to add to pg_class + * XXX Natts_pg_class_fixed is a hack - see pg_class.h + * ---------------- + */ + tup = heap_addheader(Natts_pg_class_fixed, + CLASS_TUPLE_SIZE, + (char *) new_rel_reltup); + tup->t_oid = new_rel_oid; + + /* ---------------- + * finally insert the new tuple and free it. + * + * Note: I have no idea why we do a + * SetProcessingMode(BootstrapProcessing); + * here -cim 6/14/90 + * ---------------- + */ + isBootstrap = IsBootstrapProcessingMode() ? true : false; + + SetProcessingMode(BootstrapProcessing); + + heap_insert(pg_class_desc, tup); + + if (!isBootstrap) + { + + /* + * First, open the catalog indices and insert index tuples for the + * new relation. + */ + + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class_desc, tup); + CatalogCloseIndices(Num_pg_class_indices, idescs); + + /* now restore processing mode */ + SetProcessingMode(NormalProcessing); + } + + pfree(tup); } /* -------------------------------- - * addNewRelationType - + * addNewRelationType - * - * define a complex type corresponding to the new relation + * define a complex type corresponding to the new relation * -------------------------------- */ static void addNewRelationType(char *typeName, Oid new_rel_oid) { - Oid new_type_oid; - - /* The sizes are set to oid size because it makes implementing sets MUCH - * easier, and no one (we hope) uses these fields to figure out - * how much space to allocate for the type. - * An oid is the type used for a set definition. When a user - * requests a set, what they actually get is the oid of a tuple in - * the pg_proc catalog, so the size of the "set" is the size - * of an oid. - * Similarly, byval being true makes sets much easier, and - * it isn't used by anything else. - * Note the assumption that OIDs are the same size as int4s. - */ - new_type_oid = TypeCreate(typeName, /* type name */ - new_rel_oid, /* relation oid */ - tlen(type("oid")), /* internal size */ - tlen(type("oid")), /* external size */ - 'c', /* type-type (catalog) */ - ',', /* default array delimiter */ - "int4in", /* input procedure */ - "int4out", /* output procedure */ - "int4in", /* send procedure */ - "int4out", /* receive procedure */ - NULL, /* array element type - irrelevent */ - "-", /* default type value */ - (bool) 1, /* passed by value */ - 'i'); /* default alignment */ + Oid new_type_oid; + + /* + * The sizes are set to oid size because it makes implementing sets + * MUCH easier, and no one (we hope) uses these fields to figure out + * how much space to allocate for the type. An oid is the type used + * for a set definition. When a user requests a set, what they + * actually get is the oid of a tuple in the pg_proc catalog, so the + * size of the "set" is the size of an oid. Similarly, byval being + * true makes sets much easier, and it isn't used by anything else. + * Note the assumption that OIDs are the same size as int4s. + */ + new_type_oid = TypeCreate(typeName, /* type name */ + new_rel_oid, /* relation oid */ + tlen(type("oid")), /* internal size */ + tlen(type("oid")), /* external size */ + 'c', /* type-type (catalog) */ + ',', /* default array delimiter */ + "int4in", /* input procedure */ + "int4out", /* output procedure */ + "int4in", /* send procedure */ + "int4out", /* receive procedure */ + NULL, /* array element type - irrelevent */ + "-", /* default type value */ + (bool) 1, /* passed by value */ + 'i'); /* default alignment */ } /* -------------------------------- - * heap_create + * heap_create * - * creates a new cataloged relation. see comments above. + * creates a new cataloged relation. see comments above. * -------------------------------- */ Oid heap_create(char relname[], - char *typename, /* not used currently */ - int arch, - unsigned smgr, - TupleDesc tupdesc) + char *typename, /* not used currently */ + int arch, + unsigned smgr, + TupleDesc tupdesc) { - Relation pg_class_desc; - Relation new_rel_desc; - Oid new_rel_oid; -/* NameData typeNameData; */ - int natts = tupdesc->natts; - - /* ---------------- - * sanity checks - * ---------------- - */ - AssertState(IsNormalProcessingMode() || IsBootstrapProcessingMode()); - if (natts == 0 || natts > MaxHeapAttributeNumber) - elog(WARN, "amcreate: from 1 to %d attributes must be specified", - MaxHeapAttributeNumber); - - CheckAttributeNames(tupdesc); - - /* ---------------- - * open pg_class and see that the relation doesn't - * already exist. - * ---------------- - */ - pg_class_desc = heap_openr(RelationRelationName); - - if (RelationAlreadyExists(pg_class_desc, relname)) { + Relation pg_class_desc; + Relation new_rel_desc; + Oid new_rel_oid; + +/* NameData typeNameData; */ + int natts = tupdesc->natts; + + /* ---------------- + * sanity checks + * ---------------- + */ + AssertState(IsNormalProcessingMode() || IsBootstrapProcessingMode()); + if (natts == 0 || natts > MaxHeapAttributeNumber) + elog(WARN, "amcreate: from 1 to %d attributes must be specified", + MaxHeapAttributeNumber); + + CheckAttributeNames(tupdesc); + + /* ---------------- + * open pg_class and see that the relation doesn't + * already exist. + * ---------------- + */ + pg_class_desc = heap_openr(RelationRelationName); + + if (RelationAlreadyExists(pg_class_desc, relname)) + { + heap_close(pg_class_desc); + elog(WARN, "amcreate: %s relation already exists", relname); + } + + /* ---------------- + * ok, relation does not already exist so now we + * create an uncataloged relation and pull its relation oid + * from the newly formed relation descriptor. + * + * Note: The call to heap_creatr() does all the "real" work + * of creating the disk file for the relation. + * ---------------- + */ + new_rel_desc = heap_creatr(relname, smgr, tupdesc); + new_rel_oid = new_rel_desc->rd_att->attrs[0]->attrelid; + + /* ---------------- + * since defining a relation also defines a complex type, + * we add a new system type corresponding to the new relation. + * ---------------- + */ +/* namestrcpy(&typeNameData, relname);*/ +/* addNewRelationType(&typeNameData, new_rel_oid);*/ + addNewRelationType(relname, new_rel_oid); + + /* ---------------- + * now add tuples to pg_attribute for the attributes in + * our new relation. + * ---------------- + */ + AddNewAttributeTuples(new_rel_oid, tupdesc); + + /* ---------------- + * now update the information in pg_class. + * ---------------- + */ + AddPgRelationTuple(pg_class_desc, + new_rel_desc, + new_rel_oid, + arch, + natts); + + StoreConstraints(new_rel_desc); + + /* ---------------- + * ok, the relation has been cataloged, so close our relations + * and return the oid of the newly created relation. + * + * SOMEDAY: fill the STATISTIC relation properly. + * ---------------- + */ + heap_close(new_rel_desc); heap_close(pg_class_desc); - elog(WARN, "amcreate: %s relation already exists", relname); - } - - /* ---------------- - * ok, relation does not already exist so now we - * create an uncataloged relation and pull its relation oid - * from the newly formed relation descriptor. - * - * Note: The call to heap_creatr() does all the "real" work - * of creating the disk file for the relation. - * ---------------- - */ - new_rel_desc = heap_creatr(relname, smgr, tupdesc); - new_rel_oid = new_rel_desc->rd_att->attrs[0]->attrelid; - - /* ---------------- - * since defining a relation also defines a complex type, - * we add a new system type corresponding to the new relation. - * ---------------- - */ -/* namestrcpy(&typeNameData, relname);*/ -/* addNewRelationType(&typeNameData, new_rel_oid);*/ - addNewRelationType(relname, new_rel_oid); - - /* ---------------- - * now add tuples to pg_attribute for the attributes in - * our new relation. - * ---------------- - */ - AddNewAttributeTuples(new_rel_oid, tupdesc); - - /* ---------------- - * now update the information in pg_class. - * ---------------- - */ - AddPgRelationTuple(pg_class_desc, - new_rel_desc, - new_rel_oid, - arch, - natts); - - StoreConstraints (new_rel_desc); - - /* ---------------- - * ok, the relation has been cataloged, so close our relations - * and return the oid of the newly created relation. - * - * SOMEDAY: fill the STATISTIC relation properly. - * ---------------- - */ - heap_close(new_rel_desc); - heap_close(pg_class_desc); - - return new_rel_oid; + + return new_rel_oid; } /* ---------------------------------------------------------------- - * heap_destroy - removes all record of named relation from catalogs + * heap_destroy - removes all record of named relation from catalogs * - * 1) open relation, check for existence, etc. - * 2) remove inheritance information - * 3) remove indexes - * 4) remove pg_class tuple - * 5) remove pg_attribute tuples - * 6) remove pg_type tuples - * 7) RemoveConstraints () - * 8) unlink relation + * 1) open relation, check for existence, etc. + * 2) remove inheritance information + * 3) remove indexes + * 4) remove pg_class tuple + * 5) remove pg_attribute tuples + * 6) remove pg_type tuples + * 7) RemoveConstraints () + * 8) unlink relation * * old comments - * Except for vital relations, removes relation from - * relation catalog, and related attributes from - * attribute catalog (needed?). (Anything else?) + * Except for vital relations, removes relation from + * relation catalog, and related attributes from + * attribute catalog (needed?). (Anything else?) * - * get proper relation from relation catalog (if not arg) - * check if relation is vital (strcmp()/reltype?) - * scan attribute catalog deleting attributes of reldesc - * (necessary?) - * delete relation from relation catalog - * (How are the tuples of the relation discarded?) + * get proper relation from relation catalog (if not arg) + * check if relation is vital (strcmp()/reltype?) + * scan attribute catalog deleting attributes of reldesc + * (necessary?) + * delete relation from relation catalog + * (How are the tuples of the relation discarded?) * - * XXX Must fix to work with indexes. - * There may be a better order for doing things. - * Problems with destroying a deleted database--cannot create - * a struct reldesc without having an open file descriptor. + * XXX Must fix to work with indexes. + * There may be a better order for doing things. + * Problems with destroying a deleted database--cannot create + * a struct reldesc without having an open file descriptor. * ---------------------------------------------------------------- */ /* -------------------------------- - * RelationRemoveInheritance + * RelationRemoveInheritance * - * Note: for now, we cause an exception if relation is a - * superclass. Someday, we may want to allow this and merge - * the type info into subclass procedures.... this seems like - * lots of work. + * Note: for now, we cause an exception if relation is a + * superclass. Someday, we may want to allow this and merge + * the type info into subclass procedures.... this seems like + * lots of work. * -------------------------------- */ static void RelationRemoveInheritance(Relation relation) { - Relation catalogRelation; - HeapTuple tuple; - HeapScanDesc scan; - ScanKeyData entry; - - /* ---------------- - * open pg_inherits - * ---------------- - */ - catalogRelation = heap_openr(InheritsRelationName); - - /* ---------------- - * form a scan key for the subclasses of this class - * and begin scanning - * ---------------- - */ - ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(RelationGetRelationId(relation))); - - scan = heap_beginscan(catalogRelation, - false, - NowTimeQual, - 1, - &entry); - - /* ---------------- - * if any subclasses exist, then we disallow the deletion. - * ---------------- - */ - tuple = heap_getnext(scan, 0, (Buffer *)NULL); - if (HeapTupleIsValid(tuple)) { + Relation catalogRelation; + HeapTuple tuple; + HeapScanDesc scan; + ScanKeyData entry; + + /* ---------------- + * open pg_inherits + * ---------------- + */ + catalogRelation = heap_openr(InheritsRelationName); + + /* ---------------- + * form a scan key for the subclasses of this class + * and begin scanning + * ---------------- + */ + ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(RelationGetRelationId(relation))); + + scan = heap_beginscan(catalogRelation, + false, + NowTimeQual, + 1, + &entry); + + /* ---------------- + * if any subclasses exist, then we disallow the deletion. + * ---------------- + */ + tuple = heap_getnext(scan, 0, (Buffer *) NULL); + if (HeapTupleIsValid(tuple)) + { + heap_endscan(scan); + heap_close(catalogRelation); + + elog(WARN, "relation <%d> inherits \"%s\"", + ((InheritsTupleForm) GETSTRUCT(tuple))->inhrel, + RelationGetRelationName(relation)); + } + + /* ---------------- + * If we get here, it means the relation has no subclasses + * so we can trash it. First we remove dead INHERITS tuples. + * ---------------- + */ + entry.sk_attno = Anum_pg_inherits_inhrel; + + scan = heap_beginscan(catalogRelation, + false, + NowTimeQual, + 1, + &entry); + + for (;;) + { + tuple = heap_getnext(scan, 0, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + break; + } + heap_delete(catalogRelation, &tuple->t_ctid); + } + heap_endscan(scan); heap_close(catalogRelation); - - elog(WARN, "relation <%d> inherits \"%s\"", - ((InheritsTupleForm) GETSTRUCT(tuple))->inhrel, - RelationGetRelationName(relation)); - } - - /* ---------------- - * If we get here, it means the relation has no subclasses - * so we can trash it. First we remove dead INHERITS tuples. - * ---------------- - */ - entry.sk_attno = Anum_pg_inherits_inhrel; - - scan = heap_beginscan(catalogRelation, - false, - NowTimeQual, - 1, - &entry); - - for (;;) { - tuple = heap_getnext(scan, 0, (Buffer *)NULL); - if (!HeapTupleIsValid(tuple)) { - break; - } - heap_delete(catalogRelation, &tuple->t_ctid); - } - - heap_endscan(scan); - heap_close(catalogRelation); - - /* ---------------- - * now remove dead IPL tuples - * ---------------- - */ - catalogRelation = - heap_openr(InheritancePrecidenceListRelationName); - - entry.sk_attno = Anum_pg_ipl_iplrel; - - scan = heap_beginscan(catalogRelation, - false, - NowTimeQual, - 1, - &entry); - - for (;;) { - tuple = heap_getnext(scan, 0, (Buffer *)NULL); - if (!HeapTupleIsValid(tuple)) { - break; + + /* ---------------- + * now remove dead IPL tuples + * ---------------- + */ + catalogRelation = + heap_openr(InheritancePrecidenceListRelationName); + + entry.sk_attno = Anum_pg_ipl_iplrel; + + scan = heap_beginscan(catalogRelation, + false, + NowTimeQual, + 1, + &entry); + + for (;;) + { + tuple = heap_getnext(scan, 0, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + break; + } + heap_delete(catalogRelation, &tuple->t_ctid); } - heap_delete(catalogRelation, &tuple->t_ctid); - } - - heap_endscan(scan); - heap_close(catalogRelation); + + heap_endscan(scan); + heap_close(catalogRelation); } /* -------------------------------- - * RelationRemoveIndexes - * + * RelationRemoveIndexes + * * -------------------------------- */ static void RelationRemoveIndexes(Relation relation) { - Relation indexRelation; - HeapTuple tuple; - HeapScanDesc scan; - ScanKeyData entry; - - indexRelation = heap_openr(IndexRelationName); - - ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indrelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(RelationGetRelationId(relation))); - - scan = heap_beginscan(indexRelation, - false, - NowTimeQual, - 1, - &entry); - - for (;;) { - tuple = heap_getnext(scan, 0, (Buffer *)NULL); - if (!HeapTupleIsValid(tuple)) { - break; + Relation indexRelation; + HeapTuple tuple; + HeapScanDesc scan; + ScanKeyData entry; + + indexRelation = heap_openr(IndexRelationName); + + ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(RelationGetRelationId(relation))); + + scan = heap_beginscan(indexRelation, + false, + NowTimeQual, + 1, + &entry); + + for (;;) + { + tuple = heap_getnext(scan, 0, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + break; + } + + index_destroy(((IndexTupleForm) GETSTRUCT(tuple))->indexrelid); } - - index_destroy(((IndexTupleForm)GETSTRUCT(tuple))->indexrelid); - } - - heap_endscan(scan); - heap_close(indexRelation); + + heap_endscan(scan); + heap_close(indexRelation); } /* -------------------------------- - * DeletePgRelationTuple + * DeletePgRelationTuple * * -------------------------------- */ static void DeletePgRelationTuple(Relation rdesc) { - Relation pg_class_desc; - HeapScanDesc pg_class_scan; - ScanKeyData key; - HeapTuple tup; - - /* ---------------- - * open pg_class - * ---------------- - */ - pg_class_desc = heap_openr(RelationRelationName); - - /* ---------------- - * create a scan key to locate the relation oid of the - * relation to delete - * ---------------- - */ - ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber, - F_INT4EQ, rdesc->rd_att->attrs[0]->attrelid); - - pg_class_scan = heap_beginscan(pg_class_desc, - 0, - NowTimeQual, - 1, - &key); - - /* ---------------- - * use heap_getnext() to fetch the pg_class tuple. If this - * tuple is not valid then something's wrong. - * ---------------- - */ - tup = heap_getnext(pg_class_scan, 0, (Buffer *) NULL); - - if (! PointerIsValid(tup)) { + Relation pg_class_desc; + HeapScanDesc pg_class_scan; + ScanKeyData key; + HeapTuple tup; + + /* ---------------- + * open pg_class + * ---------------- + */ + pg_class_desc = heap_openr(RelationRelationName); + + /* ---------------- + * create a scan key to locate the relation oid of the + * relation to delete + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber, + F_INT4EQ, rdesc->rd_att->attrs[0]->attrelid); + + pg_class_scan = heap_beginscan(pg_class_desc, + 0, + NowTimeQual, + 1, + &key); + + /* ---------------- + * use heap_getnext() to fetch the pg_class tuple. If this + * tuple is not valid then something's wrong. + * ---------------- + */ + tup = heap_getnext(pg_class_scan, 0, (Buffer *) NULL); + + if (!PointerIsValid(tup)) + { + heap_endscan(pg_class_scan); + heap_close(pg_class_desc); + elog(WARN, "DeletePgRelationTuple: %s relation nonexistent", + &rdesc->rd_rel->relname); + } + + /* ---------------- + * delete the relation tuple from pg_class, and finish up. + * ---------------- + */ heap_endscan(pg_class_scan); + heap_delete(pg_class_desc, &tup->t_ctid); + heap_close(pg_class_desc); - elog(WARN, "DeletePgRelationTuple: %s relation nonexistent", - &rdesc->rd_rel->relname); - } - - /* ---------------- - * delete the relation tuple from pg_class, and finish up. - * ---------------- - */ - heap_endscan(pg_class_scan); - heap_delete(pg_class_desc, &tup->t_ctid); - - heap_close(pg_class_desc); } /* -------------------------------- - * DeletePgAttributeTuples + * DeletePgAttributeTuples * * -------------------------------- */ static void DeletePgAttributeTuples(Relation rdesc) { - Relation pg_attribute_desc; - HeapScanDesc pg_attribute_scan; - ScanKeyData key; - HeapTuple tup; - - /* ---------------- - * open pg_attribute - * ---------------- - */ - pg_attribute_desc = heap_openr(AttributeRelationName); - - /* ---------------- - * create a scan key to locate the attribute tuples to delete - * and begin the scan. - * ---------------- - */ - ScanKeyEntryInitialize(&key, 0, Anum_pg_attribute_attrelid, - F_INT4EQ, rdesc->rd_att->attrs[0]->attrelid); - - /* ----------------- - * Get a write lock _before_ getting the read lock in the scan - * ---------------- - */ - RelationSetLockForWrite(pg_attribute_desc); - - pg_attribute_scan = heap_beginscan(pg_attribute_desc, - 0, - NowTimeQual, - 1, - &key); - - /* ---------------- - * use heap_getnext() / amdelete() until all attribute tuples - * have been deleted. - * ---------------- - */ - while (tup = heap_getnext(pg_attribute_scan, 0, (Buffer *)NULL), - PointerIsValid(tup)) { - - heap_delete(pg_attribute_desc, &tup->t_ctid); - } - - /* ---------------- - * finish up. - * ---------------- - */ - heap_endscan(pg_attribute_scan); - - /* ---------------- - * Release the write lock - * ---------------- - */ - RelationUnsetLockForWrite(pg_attribute_desc); - heap_close(pg_attribute_desc); + Relation pg_attribute_desc; + HeapScanDesc pg_attribute_scan; + ScanKeyData key; + HeapTuple tup; + + /* ---------------- + * open pg_attribute + * ---------------- + */ + pg_attribute_desc = heap_openr(AttributeRelationName); + + /* ---------------- + * create a scan key to locate the attribute tuples to delete + * and begin the scan. + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, Anum_pg_attribute_attrelid, + F_INT4EQ, rdesc->rd_att->attrs[0]->attrelid); + + /* ----------------- + * Get a write lock _before_ getting the read lock in the scan + * ---------------- + */ + RelationSetLockForWrite(pg_attribute_desc); + + pg_attribute_scan = heap_beginscan(pg_attribute_desc, + 0, + NowTimeQual, + 1, + &key); + + /* ---------------- + * use heap_getnext() / amdelete() until all attribute tuples + * have been deleted. + * ---------------- + */ + while (tup = heap_getnext(pg_attribute_scan, 0, (Buffer *) NULL), + PointerIsValid(tup)) + { + + heap_delete(pg_attribute_desc, &tup->t_ctid); + } + + /* ---------------- + * finish up. + * ---------------- + */ + heap_endscan(pg_attribute_scan); + + /* ---------------- + * Release the write lock + * ---------------- + */ + RelationUnsetLockForWrite(pg_attribute_desc); + heap_close(pg_attribute_desc); } /* -------------------------------- - * DeletePgTypeTuple + * DeletePgTypeTuple * - * If the user attempts to destroy a relation and there - * exists attributes in other relations of type - * "relation we are deleting", then we have to do something - * special. presently we disallow the destroy. + * If the user attempts to destroy a relation and there + * exists attributes in other relations of type + * "relation we are deleting", then we have to do something + * special. presently we disallow the destroy. * -------------------------------- */ static void DeletePgTypeTuple(Relation rdesc) { - Relation pg_type_desc; - HeapScanDesc pg_type_scan; - Relation pg_attribute_desc; - HeapScanDesc pg_attribute_scan; - ScanKeyData key; - ScanKeyData attkey; - HeapTuple tup; - HeapTuple atttup; - Oid typoid; - - /* ---------------- - * open pg_type - * ---------------- - */ - pg_type_desc = heap_openr(TypeRelationName); - - /* ---------------- - * create a scan key to locate the type tuple corresponding - * to this relation. - * ---------------- - */ - ScanKeyEntryInitialize(&key, 0, Anum_pg_type_typrelid, F_INT4EQ, - rdesc->rd_att->attrs[0]->attrelid); - - pg_type_scan = heap_beginscan(pg_type_desc, - 0, - NowTimeQual, - 1, - &key); - - /* ---------------- - * use heap_getnext() to fetch the pg_type tuple. If this - * tuple is not valid then something's wrong. - * ---------------- - */ - tup = heap_getnext(pg_type_scan, 0, (Buffer *)NULL); - - if (! PointerIsValid(tup)) { - heap_endscan(pg_type_scan); - heap_close(pg_type_desc); - elog(WARN, "DeletePgTypeTuple: %s type nonexistent", - &rdesc->rd_rel->relname); - } - - /* ---------------- - * now scan pg_attribute. if any other relations have - * attributes of the type of the relation we are deleteing - * then we have to disallow the deletion. should talk to - * stonebraker about this. -cim 6/19/90 - * ---------------- - */ - typoid = tup->t_oid; - - pg_attribute_desc = heap_openr(AttributeRelationName); - - ScanKeyEntryInitialize(&attkey, - 0, Anum_pg_attribute_atttypid, F_INT4EQ, - typoid); - - pg_attribute_scan = heap_beginscan(pg_attribute_desc, - 0, - NowTimeQual, - 1, - &attkey); - - /* ---------------- - * try and get a pg_attribute tuple. if we succeed it means - * we cant delete the relation because something depends on - * the schema. - * ---------------- - */ - atttup = heap_getnext(pg_attribute_scan, 0, (Buffer *)NULL); - - if (PointerIsValid(atttup)) { - Oid relid = ((AttributeTupleForm) GETSTRUCT(atttup))->attrelid; - - heap_endscan(pg_type_scan); - heap_close(pg_type_desc); + Relation pg_type_desc; + HeapScanDesc pg_type_scan; + Relation pg_attribute_desc; + HeapScanDesc pg_attribute_scan; + ScanKeyData key; + ScanKeyData attkey; + HeapTuple tup; + HeapTuple atttup; + Oid typoid; + + /* ---------------- + * open pg_type + * ---------------- + */ + pg_type_desc = heap_openr(TypeRelationName); + + /* ---------------- + * create a scan key to locate the type tuple corresponding + * to this relation. + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, Anum_pg_type_typrelid, F_INT4EQ, + rdesc->rd_att->attrs[0]->attrelid); + + pg_type_scan = heap_beginscan(pg_type_desc, + 0, + NowTimeQual, + 1, + &key); + + /* ---------------- + * use heap_getnext() to fetch the pg_type tuple. If this + * tuple is not valid then something's wrong. + * ---------------- + */ + tup = heap_getnext(pg_type_scan, 0, (Buffer *) NULL); + + if (!PointerIsValid(tup)) + { + heap_endscan(pg_type_scan); + heap_close(pg_type_desc); + elog(WARN, "DeletePgTypeTuple: %s type nonexistent", + &rdesc->rd_rel->relname); + } + + /* ---------------- + * now scan pg_attribute. if any other relations have + * attributes of the type of the relation we are deleteing + * then we have to disallow the deletion. should talk to + * stonebraker about this. -cim 6/19/90 + * ---------------- + */ + typoid = tup->t_oid; + + pg_attribute_desc = heap_openr(AttributeRelationName); + + ScanKeyEntryInitialize(&attkey, + 0, Anum_pg_attribute_atttypid, F_INT4EQ, + typoid); + + pg_attribute_scan = heap_beginscan(pg_attribute_desc, + 0, + NowTimeQual, + 1, + &attkey); + + /* ---------------- + * try and get a pg_attribute tuple. if we succeed it means + * we cant delete the relation because something depends on + * the schema. + * ---------------- + */ + atttup = heap_getnext(pg_attribute_scan, 0, (Buffer *) NULL); + + if (PointerIsValid(atttup)) + { + Oid relid = ((AttributeTupleForm) GETSTRUCT(atttup))->attrelid; + + heap_endscan(pg_type_scan); + heap_close(pg_type_desc); + heap_endscan(pg_attribute_scan); + heap_close(pg_attribute_desc); + + elog(WARN, "DeletePgTypeTuple: att of type %s exists in relation %d", + &rdesc->rd_rel->relname, relid); + } heap_endscan(pg_attribute_scan); heap_close(pg_attribute_desc); - - elog(WARN, "DeletePgTypeTuple: att of type %s exists in relation %d", - &rdesc->rd_rel->relname, relid); - } - heap_endscan(pg_attribute_scan); - heap_close(pg_attribute_desc); - - /* ---------------- - * Ok, it's safe so we delete the relation tuple - * from pg_type and finish up. But first end the scan so that - * we release the read lock on pg_type. -mer 13 Aug 1991 - * ---------------- - */ - heap_endscan(pg_type_scan); - heap_delete(pg_type_desc, &tup->t_ctid); - - heap_close(pg_type_desc); + + /* ---------------- + * Ok, it's safe so we delete the relation tuple + * from pg_type and finish up. But first end the scan so that + * we release the read lock on pg_type. -mer 13 Aug 1991 + * ---------------- + */ + heap_endscan(pg_type_scan); + heap_delete(pg_type_desc, &tup->t_ctid); + + heap_close(pg_type_desc); } /* -------------------------------- - * heap_destroy + * heap_destroy * * -------------------------------- */ void heap_destroy(char *relname) { - Relation rdesc; - Oid rid; - - /* ---------------- - * first open the relation. if the relation does exist, - * heap_openr() returns NULL. - * ---------------- - */ - rdesc = heap_openr(relname); - if ( rdesc == NULL ) - elog (WARN, "Relation %s Does Not Exist!", relname); - - RelationSetLockForWrite(rdesc); - rid = rdesc->rd_id; - - /* ---------------- - * prevent deletion of system relations - * ---------------- - */ - if (IsSystemRelationName(RelationGetRelationName(rdesc)->data)) - elog(WARN, "amdestroy: cannot destroy %s relation", - &rdesc->rd_rel->relname); - - /* ---------------- - * remove inheritance information - * ---------------- - */ - RelationRemoveInheritance(rdesc); - - /* ---------------- - * remove indexes if necessary - * ---------------- - */ - if (rdesc->rd_rel->relhasindex) { - RelationRemoveIndexes(rdesc); - } - - /* ---------------- - * remove rules if necessary - * ---------------- - */ - if (rdesc->rd_rules != NULL) { - RelationRemoveRules(rid); - } - - /* triggers */ - if ( rdesc->rd_rel->reltriggers > 0 ) - RelationRemoveTriggers (rdesc); - - /* ---------------- - * delete attribute tuples - * ---------------- - */ - DeletePgAttributeTuples(rdesc); - - /* ---------------- - * delete type tuple. here we want to see the effects - * of the deletions we just did, so we use setheapoverride(). - * ---------------- - */ - setheapoverride(true); - DeletePgTypeTuple(rdesc); - setheapoverride(false); - - /* ---------------- - * delete relation tuple - * ---------------- - */ - DeletePgRelationTuple(rdesc); - - /* - * release dirty buffers of this relation - */ - ReleaseRelationBuffers (rdesc); - - /* ---------------- - * flush the relation from the relcache - * ---------------- - * Does nothing!!! Flushing moved below. - vadim 06/04/97 - RelationIdInvalidateRelationCacheByRelationId(rdesc->rd_id); - */ - - RemoveConstraints (rdesc); - - /* ---------------- - * unlink the relation and finish up. - * ---------------- - */ - if ( !(rdesc->rd_istemp) || !(rdesc->rd_tmpunlinked) ) - { - smgrunlink(rdesc->rd_rel->relsmgr, rdesc); - } - rdesc->rd_tmpunlinked = TRUE; - - RelationUnsetLockForWrite(rdesc); - - heap_close(rdesc); - - /* ok - flush the relation from the relcache */ - RelationForgetRelation (rid); + Relation rdesc; + Oid rid; + + /* ---------------- + * first open the relation. if the relation does exist, + * heap_openr() returns NULL. + * ---------------- + */ + rdesc = heap_openr(relname); + if (rdesc == NULL) + elog(WARN, "Relation %s Does Not Exist!", relname); + + RelationSetLockForWrite(rdesc); + rid = rdesc->rd_id; + + /* ---------------- + * prevent deletion of system relations + * ---------------- + */ + if (IsSystemRelationName(RelationGetRelationName(rdesc)->data)) + elog(WARN, "amdestroy: cannot destroy %s relation", + &rdesc->rd_rel->relname); + + /* ---------------- + * remove inheritance information + * ---------------- + */ + RelationRemoveInheritance(rdesc); + + /* ---------------- + * remove indexes if necessary + * ---------------- + */ + if (rdesc->rd_rel->relhasindex) + { + RelationRemoveIndexes(rdesc); + } + + /* ---------------- + * remove rules if necessary + * ---------------- + */ + if (rdesc->rd_rules != NULL) + { + RelationRemoveRules(rid); + } + + /* triggers */ + if (rdesc->rd_rel->reltriggers > 0) + RelationRemoveTriggers(rdesc); + + /* ---------------- + * delete attribute tuples + * ---------------- + */ + DeletePgAttributeTuples(rdesc); + + /* ---------------- + * delete type tuple. here we want to see the effects + * of the deletions we just did, so we use setheapoverride(). + * ---------------- + */ + setheapoverride(true); + DeletePgTypeTuple(rdesc); + setheapoverride(false); + + /* ---------------- + * delete relation tuple + * ---------------- + */ + DeletePgRelationTuple(rdesc); + + /* + * release dirty buffers of this relation + */ + ReleaseRelationBuffers(rdesc); + + /* ---------------- + * flush the relation from the relcache + * ---------------- + * Does nothing!!! Flushing moved below. - vadim 06/04/97 + RelationIdInvalidateRelationCacheByRelationId(rdesc->rd_id); + */ + + RemoveConstraints(rdesc); + + /* ---------------- + * unlink the relation and finish up. + * ---------------- + */ + if (!(rdesc->rd_istemp) || !(rdesc->rd_tmpunlinked)) + { + smgrunlink(rdesc->rd_rel->relsmgr, rdesc); + } + rdesc->rd_tmpunlinked = TRUE; + + RelationUnsetLockForWrite(rdesc); + + heap_close(rdesc); + + /* ok - flush the relation from the relcache */ + RelationForgetRelation(rid); } /* * heap_destroyr - * destroy and close temporary relations + * destroy and close temporary relations * */ -void +void heap_destroyr(Relation rdesc) { - ReleaseRelationBuffers(rdesc); - if ( !(rdesc->rd_istemp) || !(rdesc->rd_tmpunlinked) ) - { - smgrunlink(rdesc->rd_rel->relsmgr, rdesc); - } - rdesc->rd_tmpunlinked = TRUE; - heap_close(rdesc); - RemoveFromTempRelList(rdesc); + ReleaseRelationBuffers(rdesc); + if (!(rdesc->rd_istemp) || !(rdesc->rd_tmpunlinked)) + { + smgrunlink(rdesc->rd_rel->relsmgr, rdesc); + } + rdesc->rd_tmpunlinked = TRUE; + heap_close(rdesc); + RemoveFromTempRelList(rdesc); } /************************************************************** - functions to deal with the list of temporary relations + functions to deal with the list of temporary relations **************************************************************/ /* -------------- @@ -1393,46 +1428,49 @@ heap_destroyr(Relation rdesc) >> NOTE << malloc is used instead of palloc because we KNOW when we are - going to free these things. Keeps us away from the memory context + going to free these things. Keeps us away from the memory context hairyness */ void InitTempRelList(void) { - if (tempRels) { - free(tempRels->rels); - free(tempRels); - } + if (tempRels) + { + free(tempRels->rels); + free(tempRels); + } - tempRels = (TempRelList*)malloc(sizeof(TempRelList)); - tempRels->size = TEMP_REL_LIST_SIZE; - tempRels->rels = (Relation*)malloc(sizeof(Relation) * tempRels->size); - memset(tempRels->rels, 0, sizeof(Relation) * tempRels->size); - tempRels->num = 0; + tempRels = (TempRelList *) malloc(sizeof(TempRelList)); + tempRels->size = TEMP_REL_LIST_SIZE; + tempRels->rels = (Relation *) malloc(sizeof(Relation) * tempRels->size); + memset(tempRels->rels, 0, sizeof(Relation) * tempRels->size); + tempRels->num = 0; } /* removes a relation from the TempRelList MODIFIES the global variable tempRels - we don't really remove it, just mark it as NULL - and DestroyTempRels will look for NULLs + we don't really remove it, just mark it as NULL + and DestroyTempRels will look for NULLs */ static void RemoveFromTempRelList(Relation r) { - int i; + int i; - if (!tempRels) - return; + if (!tempRels) + return; - for (i=0; i<tempRels->num; i++) { - if (tempRels->rels[i] == r) { - tempRels->rels[i] = NULL; - break; + for (i = 0; i < tempRels->num; i++) + { + if (tempRels->rels[i] == r) + { + tempRels->rels[i] = NULL; + break; + } } - } } /* @@ -1443,16 +1481,17 @@ RemoveFromTempRelList(Relation r) static void AddToTempRelList(Relation r) { - if (!tempRels) - return; + if (!tempRels) + return; - if (tempRels->num == tempRels->size) { - tempRels->size += TEMP_REL_LIST_SIZE; - tempRels->rels = realloc(tempRels->rels, - sizeof(Relation) * tempRels->size); - } - tempRels->rels[tempRels->num] = r; - tempRels->num++; + if (tempRels->num == tempRels->size) + { + tempRels->size += TEMP_REL_LIST_SIZE; + tempRels->rels = realloc(tempRels->rels, + sizeof(Relation) * tempRels->size); + } + tempRels->rels[tempRels->num] = r; + tempRels->num++; } /* @@ -1461,251 +1500,253 @@ AddToTempRelList(Relation r) void DestroyTempRels(void) { - int i; - Relation rdesc; + int i; + Relation rdesc; - if (!tempRels) - return; + if (!tempRels) + return; - for (i=0;i<tempRels->num;i++) { - rdesc = tempRels->rels[i]; - /* rdesc may be NULL if it has been removed from the list already */ - if (rdesc) - heap_destroyr(rdesc); - } - free(tempRels->rels); - free(tempRels); - tempRels = NULL; + for (i = 0; i < tempRels->num; i++) + { + rdesc = tempRels->rels[i]; + /* rdesc may be NULL if it has been removed from the list already */ + if (rdesc) + heap_destroyr(rdesc); + } + free(tempRels->rels); + free(tempRels); + tempRels = NULL; } -extern List *flatten_tlist(List *tlist); -extern List *pg_plan(char *query_string, Oid *typev, int nargs, - QueryTreeList **queryListP, CommandDest dest); +extern List *flatten_tlist(List * tlist); +extern List * +pg_plan(char *query_string, Oid * typev, int nargs, + QueryTreeList ** queryListP, CommandDest dest); -static void -StoreAttrDefault (Relation rel, AttrDefault *attrdef) +static void +StoreAttrDefault(Relation rel, AttrDefault * attrdef) { - char str[MAX_PARSE_BUFFER]; - char cast[2*NAMEDATALEN] = {0}; - AttributeTupleForm atp = rel->rd_att->attrs[attrdef->adnum - 1]; - QueryTreeList *queryTree_list; - Query *query; - List *planTree_list; - TargetEntry *te; - Resdom *resdom; - Node *expr; - char *adbin; - MemoryContext oldcxt; - Relation adrel; - Relation idescs[Num_pg_attrdef_indices]; - HeapTuple tuple; - Datum values[4]; - char nulls[4] = {' ', ' ', ' ', ' '}; - extern GlobalMemory CacheCxt; - + char str[MAX_PARSE_BUFFER]; + char cast[2 * NAMEDATALEN] = {0}; + AttributeTupleForm atp = rel->rd_att->attrs[attrdef->adnum - 1]; + QueryTreeList *queryTree_list; + Query *query; + List *planTree_list; + TargetEntry *te; + Resdom *resdom; + Node *expr; + char *adbin; + MemoryContext oldcxt; + Relation adrel; + Relation idescs[Num_pg_attrdef_indices]; + HeapTuple tuple; + Datum values[4]; + char nulls[4] = {' ', ' ', ' ', ' '}; + extern GlobalMemory CacheCxt; + start:; - sprintf (str, "select %s%s from %.*s", attrdef->adsrc, cast, - NAMEDATALEN, rel->rd_rel->relname.data); - setheapoverride(true); - planTree_list = (List*) pg_plan (str, NULL, 0, &queryTree_list, None); - setheapoverride(false); - query = (Query*) (queryTree_list->qtrees[0]); - - if ( length (query->rtable) > 1 || - flatten_tlist (query->targetList) != NIL ) - elog (WARN, "DEFAULT: cannot use attribute(s)"); - te = (TargetEntry *) lfirst (query->targetList); - resdom = te->resdom; - expr = te->expr; - - if ( IsA (expr, Const) ) - { - if ( ((Const*)expr)->consttype != atp->atttypid ) - { - if ( *cast != 0 ) - elog (WARN, "DEFAULT: const type mismatched"); - sprintf (cast, ":: %s", get_id_typname (atp->atttypid)); - goto start; - } - } - else if ( exprType (expr) != atp->atttypid ) - elog (WARN, "DEFAULT: type mismatched"); - - adbin = nodeToString (expr); - oldcxt = MemoryContextSwitchTo ((MemoryContext) CacheCxt); - attrdef->adbin = (char*) palloc (strlen (adbin) + 1); - strcpy (attrdef->adbin, adbin); - (void) MemoryContextSwitchTo (oldcxt); - pfree (adbin); - - values[Anum_pg_attrdef_adrelid - 1] = rel->rd_id; - values[Anum_pg_attrdef_adnum - 1] = attrdef->adnum; - values[Anum_pg_attrdef_adbin - 1] = PointerGetDatum (textin (attrdef->adbin)); - values[Anum_pg_attrdef_adsrc - 1] = PointerGetDatum (textin (attrdef->adsrc)); - adrel = heap_openr (AttrDefaultRelationName); - tuple = heap_formtuple (adrel->rd_att, values, nulls); - CatalogOpenIndices (Num_pg_attrdef_indices, Name_pg_attrdef_indices, idescs); - heap_insert (adrel, tuple); - CatalogIndexInsert (idescs, Num_pg_attrdef_indices, adrel, tuple); - CatalogCloseIndices (Num_pg_attrdef_indices, idescs); - heap_close (adrel); - - pfree (DatumGetPointer(values[Anum_pg_attrdef_adbin - 1])); - pfree (DatumGetPointer(values[Anum_pg_attrdef_adsrc - 1])); - pfree (tuple); + sprintf(str, "select %s%s from %.*s", attrdef->adsrc, cast, + NAMEDATALEN, rel->rd_rel->relname.data); + setheapoverride(true); + planTree_list = (List *) pg_plan(str, NULL, 0, &queryTree_list, None); + setheapoverride(false); + query = (Query *) (queryTree_list->qtrees[0]); + + if (length(query->rtable) > 1 || + flatten_tlist(query->targetList) != NIL) + elog(WARN, "DEFAULT: cannot use attribute(s)"); + te = (TargetEntry *) lfirst(query->targetList); + resdom = te->resdom; + expr = te->expr; + + if (IsA(expr, Const)) + { + if (((Const *) expr)->consttype != atp->atttypid) + { + if (*cast != 0) + elog(WARN, "DEFAULT: const type mismatched"); + sprintf(cast, ":: %s", get_id_typname(atp->atttypid)); + goto start; + } + } + else if (exprType(expr) != atp->atttypid) + elog(WARN, "DEFAULT: type mismatched"); + + adbin = nodeToString(expr); + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + attrdef->adbin = (char *) palloc(strlen(adbin) + 1); + strcpy(attrdef->adbin, adbin); + (void) MemoryContextSwitchTo(oldcxt); + pfree(adbin); + + values[Anum_pg_attrdef_adrelid - 1] = rel->rd_id; + values[Anum_pg_attrdef_adnum - 1] = attrdef->adnum; + values[Anum_pg_attrdef_adbin - 1] = PointerGetDatum(textin(attrdef->adbin)); + values[Anum_pg_attrdef_adsrc - 1] = PointerGetDatum(textin(attrdef->adsrc)); + adrel = heap_openr(AttrDefaultRelationName); + tuple = heap_formtuple(adrel->rd_att, values, nulls); + CatalogOpenIndices(Num_pg_attrdef_indices, Name_pg_attrdef_indices, idescs); + heap_insert(adrel, tuple); + CatalogIndexInsert(idescs, Num_pg_attrdef_indices, adrel, tuple); + CatalogCloseIndices(Num_pg_attrdef_indices, idescs); + heap_close(adrel); + + pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1])); + pfree(DatumGetPointer(values[Anum_pg_attrdef_adsrc - 1])); + pfree(tuple); } -static void -StoreRelCheck (Relation rel, ConstrCheck *check) +static void +StoreRelCheck(Relation rel, ConstrCheck * check) { - char str[MAX_PARSE_BUFFER]; - QueryTreeList *queryTree_list; - Query *query; - List *planTree_list; - Plan *plan; - List *qual; - char *ccbin; - MemoryContext oldcxt; - Relation rcrel; - Relation idescs[Num_pg_relcheck_indices]; - HeapTuple tuple; - Datum values[4]; - char nulls[4] = {' ', ' ', ' ', ' '}; - extern GlobalMemory CacheCxt; - - sprintf (str, "select 1 from %.*s where %s", - NAMEDATALEN, rel->rd_rel->relname.data, check->ccsrc); - setheapoverride(true); - planTree_list = (List*) pg_plan (str, NULL, 0, &queryTree_list, None); - setheapoverride(false); - query = (Query*) (queryTree_list->qtrees[0]); - - if ( length (query->rtable) > 1 ) - elog (WARN, "CHECK: only relation %.*s can be referenced", - NAMEDATALEN, rel->rd_rel->relname.data); - - plan = (Plan*) lfirst(planTree_list); - qual = plan->qual; - - ccbin = nodeToString (qual); - oldcxt = MemoryContextSwitchTo ((MemoryContext) CacheCxt); - check->ccbin = (char*) palloc (strlen (ccbin) + 1); - strcpy (check->ccbin, ccbin); - (void) MemoryContextSwitchTo (oldcxt); - pfree (ccbin); - - values[Anum_pg_relcheck_rcrelid - 1] = rel->rd_id; - values[Anum_pg_relcheck_rcname - 1] = PointerGetDatum (namein (check->ccname)); - values[Anum_pg_relcheck_rcbin - 1] = PointerGetDatum (textin (check->ccbin)); - values[Anum_pg_relcheck_rcsrc - 1] = PointerGetDatum (textin (check->ccsrc)); - rcrel = heap_openr (RelCheckRelationName); - tuple = heap_formtuple (rcrel->rd_att, values, nulls); - CatalogOpenIndices (Num_pg_relcheck_indices, Name_pg_relcheck_indices, idescs); - heap_insert (rcrel, tuple); - CatalogIndexInsert (idescs, Num_pg_relcheck_indices, rcrel, tuple); - CatalogCloseIndices (Num_pg_relcheck_indices, idescs); - heap_close (rcrel); - - pfree (DatumGetPointer(values[Anum_pg_relcheck_rcname - 1])); - pfree (DatumGetPointer(values[Anum_pg_relcheck_rcbin - 1])); - pfree (DatumGetPointer(values[Anum_pg_relcheck_rcsrc - 1])); - pfree (tuple); - - return; + char str[MAX_PARSE_BUFFER]; + QueryTreeList *queryTree_list; + Query *query; + List *planTree_list; + Plan *plan; + List *qual; + char *ccbin; + MemoryContext oldcxt; + Relation rcrel; + Relation idescs[Num_pg_relcheck_indices]; + HeapTuple tuple; + Datum values[4]; + char nulls[4] = {' ', ' ', ' ', ' '}; + extern GlobalMemory CacheCxt; + + sprintf(str, "select 1 from %.*s where %s", + NAMEDATALEN, rel->rd_rel->relname.data, check->ccsrc); + setheapoverride(true); + planTree_list = (List *) pg_plan(str, NULL, 0, &queryTree_list, None); + setheapoverride(false); + query = (Query *) (queryTree_list->qtrees[0]); + + if (length(query->rtable) > 1) + elog(WARN, "CHECK: only relation %.*s can be referenced", + NAMEDATALEN, rel->rd_rel->relname.data); + + plan = (Plan *) lfirst(planTree_list); + qual = plan->qual; + + ccbin = nodeToString(qual); + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + check->ccbin = (char *) palloc(strlen(ccbin) + 1); + strcpy(check->ccbin, ccbin); + (void) MemoryContextSwitchTo(oldcxt); + pfree(ccbin); + + values[Anum_pg_relcheck_rcrelid - 1] = rel->rd_id; + values[Anum_pg_relcheck_rcname - 1] = PointerGetDatum(namein(check->ccname)); + values[Anum_pg_relcheck_rcbin - 1] = PointerGetDatum(textin(check->ccbin)); + values[Anum_pg_relcheck_rcsrc - 1] = PointerGetDatum(textin(check->ccsrc)); + rcrel = heap_openr(RelCheckRelationName); + tuple = heap_formtuple(rcrel->rd_att, values, nulls); + CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices, idescs); + heap_insert(rcrel, tuple); + CatalogIndexInsert(idescs, Num_pg_relcheck_indices, rcrel, tuple); + CatalogCloseIndices(Num_pg_relcheck_indices, idescs); + heap_close(rcrel); + + pfree(DatumGetPointer(values[Anum_pg_relcheck_rcname - 1])); + pfree(DatumGetPointer(values[Anum_pg_relcheck_rcbin - 1])); + pfree(DatumGetPointer(values[Anum_pg_relcheck_rcsrc - 1])); + pfree(tuple); + + return; } -static void -StoreConstraints (Relation rel) +static void +StoreConstraints(Relation rel) { - TupleConstr *constr = rel->rd_att->constr; - int i; - - if ( !constr ) - return; - - if ( constr->num_defval > 0 ) - { - for (i = 0; i < constr->num_defval; i++) - StoreAttrDefault (rel, &(constr->defval[i])); - } - - if ( constr->num_check > 0 ) - { - for (i = 0; i < constr->num_check; i++) - StoreRelCheck (rel, &(constr->check[i])); - } - - return; + TupleConstr *constr = rel->rd_att->constr; + int i; + + if (!constr) + return; + + if (constr->num_defval > 0) + { + for (i = 0; i < constr->num_defval; i++) + StoreAttrDefault(rel, &(constr->defval[i])); + } + + if (constr->num_check > 0) + { + for (i = 0; i < constr->num_check; i++) + StoreRelCheck(rel, &(constr->check[i])); + } + + return; } -static void -RemoveAttrDefault (Relation rel) +static void +RemoveAttrDefault(Relation rel) { - Relation adrel; - HeapScanDesc adscan; - ScanKeyData key; - HeapTuple tup; - - adrel = heap_openr (AttrDefaultRelationName); - - ScanKeyEntryInitialize(&key, 0, Anum_pg_attrdef_adrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - - RelationSetLockForWrite (adrel); - - adscan = heap_beginscan(adrel, 0, NowTimeQual, 1, &key); - - while (tup = heap_getnext (adscan, 0, (Buffer *)NULL), PointerIsValid(tup)) - heap_delete (adrel, &tup->t_ctid); - - heap_endscan (adscan); - - RelationUnsetLockForWrite (adrel); - heap_close (adrel); + Relation adrel; + HeapScanDesc adscan; + ScanKeyData key; + HeapTuple tup; + + adrel = heap_openr(AttrDefaultRelationName); + + ScanKeyEntryInitialize(&key, 0, Anum_pg_attrdef_adrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + + RelationSetLockForWrite(adrel); + + adscan = heap_beginscan(adrel, 0, NowTimeQual, 1, &key); + + while (tup = heap_getnext(adscan, 0, (Buffer *) NULL), PointerIsValid(tup)) + heap_delete(adrel, &tup->t_ctid); + + heap_endscan(adscan); + + RelationUnsetLockForWrite(adrel); + heap_close(adrel); } -static void -RemoveRelCheck (Relation rel) +static void +RemoveRelCheck(Relation rel) { - Relation rcrel; - HeapScanDesc rcscan; - ScanKeyData key; - HeapTuple tup; - - rcrel = heap_openr (RelCheckRelationName); - - ScanKeyEntryInitialize(&key, 0, Anum_pg_relcheck_rcrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - - RelationSetLockForWrite (rcrel); - - rcscan = heap_beginscan(rcrel, 0, NowTimeQual, 1, &key); - - while (tup = heap_getnext (rcscan, 0, (Buffer *)NULL), PointerIsValid(tup)) - heap_delete (rcrel, &tup->t_ctid); - - heap_endscan (rcscan); - - RelationUnsetLockForWrite (rcrel); - heap_close (rcrel); + Relation rcrel; + HeapScanDesc rcscan; + ScanKeyData key; + HeapTuple tup; + + rcrel = heap_openr(RelCheckRelationName); + + ScanKeyEntryInitialize(&key, 0, Anum_pg_relcheck_rcrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + + RelationSetLockForWrite(rcrel); + + rcscan = heap_beginscan(rcrel, 0, NowTimeQual, 1, &key); + + while (tup = heap_getnext(rcscan, 0, (Buffer *) NULL), PointerIsValid(tup)) + heap_delete(rcrel, &tup->t_ctid); + + heap_endscan(rcscan); + + RelationUnsetLockForWrite(rcrel); + heap_close(rcrel); } -static void -RemoveConstraints (Relation rel) +static void +RemoveConstraints(Relation rel) { - TupleConstr *constr = rel->rd_att->constr; - - if ( !constr ) - return; - - if ( constr->num_defval > 0 ) - RemoveAttrDefault (rel); - - if ( constr->num_check > 0 ) - RemoveRelCheck (rel); - - return; + TupleConstr *constr = rel->rd_att->constr; + + if (!constr) + return; + + if (constr->num_defval > 0) + RemoveAttrDefault(rel); + + if (constr->num_check > 0) + RemoveRelCheck(rel); + + return; } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index b2071f814f2..6dd75742798 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1,25 +1,25 @@ /*------------------------------------------------------------------------- * * index.c-- - * code to create and destroy POSTGRES index relations + * code to create and destroy POSTGRES index relations * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.19 1997/08/22 14:10:26 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.20 1997/09/07 04:40:19 momjian Exp $ * * * INTERFACE ROUTINES - * index_create() - Create a cataloged index relation - * index_destroy() - Removes index relation from catalogs + * index_create() - Create a cataloged index relation + * index_destroy() - Removes index relation from catalogs * * NOTES - * Much of this code uses hardcoded sequential heap relation scans - * to fetch information from the catalogs. These should all be - * rewritten to use the system caches lookup routines like - * SearchSysCacheTuple, which can do efficient lookup and - * caching. + * Much of this code uses hardcoded sequential heap relation scans + * to fetch information from the catalogs. These should all be + * rewritten to use the system caches lookup routines like + * SearchSysCacheTuple, which can do efficient lookup and + * caching. * *------------------------------------------------------------------------- */ @@ -51,9 +51,9 @@ #include <access/istrat.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* @@ -63,70 +63,75 @@ #define NTUPLES_PER_PAGE(natts) (BLCKSZ/((natts)*AVG_TUPLE_SIZE)) /* non-export function prototypes */ -static Oid RelationNameGetObjectId(char *relationName, Relation pg_class, - bool setHasIndexAttribute); -static Oid GetHeapRelationOid(char *heapRelationName, char *indexRelationName); -static TupleDesc BuildFuncTupleDesc(FuncIndexInfo *funcInfo); -static TupleDesc ConstructTupleDescriptor(Oid heapoid, Relation heapRelation, - List *attributeList, - int numatts, AttrNumber attNums[]); - -static void ConstructIndexReldesc(Relation indexRelation, Oid amoid); -static Oid UpdateRelationRelation(Relation indexRelation); -static void InitializeAttributeOids(Relation indexRelation, - int numatts, - Oid indexoid); +static Oid +RelationNameGetObjectId(char *relationName, Relation pg_class, + bool setHasIndexAttribute); +static Oid GetHeapRelationOid(char *heapRelationName, char *indexRelationName); +static TupleDesc BuildFuncTupleDesc(FuncIndexInfo * funcInfo); +static TupleDesc +ConstructTupleDescriptor(Oid heapoid, Relation heapRelation, + List * attributeList, + int numatts, AttrNumber attNums[]); + +static void ConstructIndexReldesc(Relation indexRelation, Oid amoid); +static Oid UpdateRelationRelation(Relation indexRelation); +static void +InitializeAttributeOids(Relation indexRelation, + int numatts, + Oid indexoid); static void -AppendAttributeTuples(Relation indexRelation, int numatts); -static void UpdateIndexRelation(Oid indexoid, Oid heapoid, - FuncIndexInfo *funcInfo, int natts, - AttrNumber attNums[], Oid classOids[], Node *predicate, - List *attributeList, bool islossy, bool unique); -static void DefaultBuild(Relation heapRelation, Relation indexRelation, - int numberOfAttributes, AttrNumber attributeNumber[], - IndexStrategy indexStrategy, uint16 parameterCount, - Datum parameter[], FuncIndexInfoPtr funcInfo, PredInfo *predInfo); + AppendAttributeTuples(Relation indexRelation, int numatts); +static void +UpdateIndexRelation(Oid indexoid, Oid heapoid, + FuncIndexInfo * funcInfo, int natts, + AttrNumber attNums[], Oid classOids[], Node * predicate, + List * attributeList, bool islossy, bool unique); +static void +DefaultBuild(Relation heapRelation, Relation indexRelation, + int numberOfAttributes, AttrNumber attributeNumber[], + IndexStrategy indexStrategy, uint16 parameterCount, + Datum parameter[], FuncIndexInfoPtr funcInfo, PredInfo * predInfo); /* ---------------------------------------------------------------- - * sysatts is a structure containing attribute tuple forms - * for system attributes (numbered -1, -2, ...). This really - * should be generated or eliminated or moved elsewhere. -cim 1/19/91 + * sysatts is a structure containing attribute tuple forms + * for system attributes (numbered -1, -2, ...). This really + * should be generated or eliminated or moved elsewhere. -cim 1/19/91 * * typedef struct FormData_pg_attribute { - * Oid attrelid; - * NameData attname; - * Oid atttypid; - * uint32 attnvals; - * int16 attlen; - * AttrNumber attnum; - * uint32 attnelems; - * int32 attcacheoff; - * bool attbyval; - * bool attisset; - * char attalign; - * bool attnotnull; - * bool atthasdef; + * Oid attrelid; + * NameData attname; + * Oid atttypid; + * uint32 attnvals; + * int16 attlen; + * AttrNumber attnum; + * uint32 attnelems; + * int32 attcacheoff; + * bool attbyval; + * bool attisset; + * char attalign; + * bool attnotnull; + * bool atthasdef; * } FormData_pg_attribute; * * ---------------------------------------------------------------- */ -static FormData_pg_attribute sysatts[] = { - { 0l, {"ctid"}, 27l, 0l, 6, -1, 0, -1, '\0', '\0', 'i', '\0', '\0' }, - { 0l, {"oid"}, 26l, 0l, 4, -2, 0, -1, '\001', '\0', 'i', '\0', '\0' }, - { 0l, {"xmin"}, 28l, 0l, 4, -3, 0, -1, '\0', '\0', 'i', '\0', '\0' }, - { 0l, {"cmin"}, 29l, 0l, 2, -4, 0, -1, '\001', '\0', 's', '\0', '\0' }, - { 0l, {"xmax"}, 28l, 0l, 4, -5, 0, -1, '\0', '\0', 'i', '\0', '\0' }, - { 0l, {"cmax"}, 29l, 0l, 2, -6, 0, -1, '\001', '\0', 's', '\0', '\0' }, - { 0l, {"chain"}, 27l, 0l, 6, -7, 0, -1, '\0', '\0', 'i', '\0', '\0' }, - { 0l, {"anchor"}, 27l, 0l, 6, -8, 0, -1, '\0', '\0', 'i', '\0', '\0' }, - { 0l, {"tmin"}, 702l, 0l, 4, -9, 0, -1, '\001', '\0', 'i', '\0', '\0' }, - { 0l, {"tmax"}, 702l, 0l, 4, -10, 0, -1, '\001', '\0', 'i', '\0', '\0' }, - { 0l, {"vtype"}, 18l, 0l, 1, -11, 0, -1, '\001', '\0', 'c', '\0', '\0' }, +static FormData_pg_attribute sysatts[] = { + {0l, {"ctid"}, 27l, 0l, 6, -1, 0, -1, '\0', '\0', 'i', '\0', '\0'}, + {0l, {"oid"}, 26l, 0l, 4, -2, 0, -1, '\001', '\0', 'i', '\0', '\0'}, + {0l, {"xmin"}, 28l, 0l, 4, -3, 0, -1, '\0', '\0', 'i', '\0', '\0'}, + {0l, {"cmin"}, 29l, 0l, 2, -4, 0, -1, '\001', '\0', 's', '\0', '\0'}, + {0l, {"xmax"}, 28l, 0l, 4, -5, 0, -1, '\0', '\0', 'i', '\0', '\0'}, + {0l, {"cmax"}, 29l, 0l, 2, -6, 0, -1, '\001', '\0', 's', '\0', '\0'}, + {0l, {"chain"}, 27l, 0l, 6, -7, 0, -1, '\0', '\0', 'i', '\0', '\0'}, + {0l, {"anchor"}, 27l, 0l, 6, -8, 0, -1, '\0', '\0', 'i', '\0', '\0'}, + {0l, {"tmin"}, 702l, 0l, 4, -9, 0, -1, '\001', '\0', 'i', '\0', '\0'}, + {0l, {"tmax"}, 702l, 0l, 4, -10, 0, -1, '\001', '\0', 'i', '\0', '\0'}, + {0l, {"vtype"}, 18l, 0l, 1, -11, 0, -1, '\001', '\0', 'c', '\0', '\0'}, }; /* ---------------------------------------------------------------- * RelationNameGetObjectId -- - * Returns the object identifier for a relation given its name. + * Returns the object identifier for a relation given its name. * * > The HASINDEX attribute for the relation with this name will * > be set if it exists and if it is indicated by the call argument. @@ -135,1574 +140,1638 @@ static FormData_pg_attribute sysatts[] = { * probably be replaced by SearchSysCacheTuple. -cim 1/19/91 * * Note: - * Assumes relation name is valid. - * Assumes relation descriptor is valid. + * Assumes relation name is valid. + * Assumes relation descriptor is valid. * ---------------------------------------------------------------- */ -static Oid +static Oid RelationNameGetObjectId(char *relationName, - Relation pg_class, - bool setHasIndexAttribute) -{ - HeapScanDesc pg_class_scan; - HeapTuple pg_class_tuple; - Oid relationObjectId; - Buffer buffer; - ScanKeyData key; - - /* - * If this isn't bootstrap time, we can use the system catalogs to - * speed this up. - */ - - if (!IsBootstrapProcessingMode()) { - pg_class_tuple = ClassNameIndexScan(pg_class, relationName); - if (HeapTupleIsValid(pg_class_tuple)) { - relationObjectId = pg_class_tuple->t_oid; - pfree(pg_class_tuple); - } else - relationObjectId = InvalidOid; - - return (relationObjectId); - } - - /* ---------------- - * Bootstrap time, do this the hard way. - * begin a scan of pg_class for the named relation - * ---------------- - */ - ScanKeyEntryInitialize(&key, 0, Anum_pg_class_relname, - NameEqualRegProcedure, - PointerGetDatum(relationName)); - - pg_class_scan = heap_beginscan(pg_class, 0, NowTimeQual, 1, &key); - - /* ---------------- - * if we find the named relation, fetch its relation id - * (the oid of the tuple we found). - * ---------------- - */ - pg_class_tuple = heap_getnext(pg_class_scan, 0, &buffer); - - if (! HeapTupleIsValid(pg_class_tuple)) { - relationObjectId = InvalidOid; - } else { - relationObjectId = pg_class_tuple->t_oid; - ReleaseBuffer(buffer); - } - - /* ---------------- - * cleanup and return results - * ---------------- - */ - heap_endscan(pg_class_scan); - - return - relationObjectId; + Relation pg_class, + bool setHasIndexAttribute) +{ + HeapScanDesc pg_class_scan; + HeapTuple pg_class_tuple; + Oid relationObjectId; + Buffer buffer; + ScanKeyData key; + + /* + * If this isn't bootstrap time, we can use the system catalogs to + * speed this up. + */ + + if (!IsBootstrapProcessingMode()) + { + pg_class_tuple = ClassNameIndexScan(pg_class, relationName); + if (HeapTupleIsValid(pg_class_tuple)) + { + relationObjectId = pg_class_tuple->t_oid; + pfree(pg_class_tuple); + } + else + relationObjectId = InvalidOid; + + return (relationObjectId); + } + + /* ---------------- + * Bootstrap time, do this the hard way. + * begin a scan of pg_class for the named relation + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, Anum_pg_class_relname, + NameEqualRegProcedure, + PointerGetDatum(relationName)); + + pg_class_scan = heap_beginscan(pg_class, 0, NowTimeQual, 1, &key); + + /* ---------------- + * if we find the named relation, fetch its relation id + * (the oid of the tuple we found). + * ---------------- + */ + pg_class_tuple = heap_getnext(pg_class_scan, 0, &buffer); + + if (!HeapTupleIsValid(pg_class_tuple)) + { + relationObjectId = InvalidOid; + } + else + { + relationObjectId = pg_class_tuple->t_oid; + ReleaseBuffer(buffer); + } + + /* ---------------- + * cleanup and return results + * ---------------- + */ + heap_endscan(pg_class_scan); + + return + relationObjectId; } /* ---------------------------------------------------------------- - * GetHeapRelationOid + * GetHeapRelationOid * ---------------------------------------------------------------- */ -static Oid +static Oid GetHeapRelationOid(char *heapRelationName, char *indexRelationName) { - Relation pg_class; - Oid indoid; - Oid heapoid; - - /* ---------------- - * XXX ADD INDEXING HERE - * ---------------- - */ - /* ---------------- - * open pg_class and get the oid of the relation - * corresponding to the name of the index relation. - * ---------------- - */ - pg_class = heap_openr(RelationRelationName); - - indoid = RelationNameGetObjectId(indexRelationName, - pg_class, - false); - - if (OidIsValid(indoid)) - elog(WARN, "Cannot create index: '%s' already exists", - indexRelationName); - - /* ---------------- - * get the object id of the heap relation - * ---------------- - */ - heapoid = RelationNameGetObjectId(heapRelationName, - pg_class, - true); - - /* ---------------- - * check that the heap relation exists.. - * ---------------- - */ - if (! OidIsValid(heapoid)) - elog(WARN, "Cannot create index on '%s': relation does not exist", - heapRelationName); - - /* ---------------- - * close pg_class and return the heap relation oid - * ---------------- - */ - heap_close(pg_class); - - return heapoid; + Relation pg_class; + Oid indoid; + Oid heapoid; + + /* ---------------- + * XXX ADD INDEXING HERE + * ---------------- + */ + /* ---------------- + * open pg_class and get the oid of the relation + * corresponding to the name of the index relation. + * ---------------- + */ + pg_class = heap_openr(RelationRelationName); + + indoid = RelationNameGetObjectId(indexRelationName, + pg_class, + false); + + if (OidIsValid(indoid)) + elog(WARN, "Cannot create index: '%s' already exists", + indexRelationName); + + /* ---------------- + * get the object id of the heap relation + * ---------------- + */ + heapoid = RelationNameGetObjectId(heapRelationName, + pg_class, + true); + + /* ---------------- + * check that the heap relation exists.. + * ---------------- + */ + if (!OidIsValid(heapoid)) + elog(WARN, "Cannot create index on '%s': relation does not exist", + heapRelationName); + + /* ---------------- + * close pg_class and return the heap relation oid + * ---------------- + */ + heap_close(pg_class); + + return heapoid; } -static TupleDesc -BuildFuncTupleDesc(FuncIndexInfo *funcInfo) +static TupleDesc +BuildFuncTupleDesc(FuncIndexInfo * funcInfo) { - HeapTuple tuple; - TupleDesc funcTupDesc; - Oid retType; - char *funcname; - int4 nargs; - Oid *argtypes; - - /* - * Allocate and zero a tuple descriptor. - */ - funcTupDesc = CreateTemplateTupleDesc(1); - funcTupDesc->attrs[0] = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); - memset(funcTupDesc->attrs[0], 0, ATTRIBUTE_TUPLE_SIZE); - - /* - * Lookup the function for the return type. - */ - funcname = FIgetname(funcInfo); - nargs = FIgetnArgs(funcInfo); - argtypes = FIgetArglist(funcInfo); - tuple = SearchSysCacheTuple(PRONAME, - PointerGetDatum(funcname), - Int32GetDatum(nargs), - PointerGetDatum(argtypes), - 0); - - if (!HeapTupleIsValid(tuple)) - func_error("BuildFuncTupleDesc", funcname, nargs, argtypes); - - retType = ((Form_pg_proc)GETSTRUCT(tuple))->prorettype; - - /* - * Look up the return type in pg_type for the type length. - */ - tuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(retType), - 0,0,0); - if (!HeapTupleIsValid(tuple)) - elog(WARN,"Function %s return type does not exist",FIgetname(funcInfo)); - - /* - * Assign some of the attributes values. Leave the rest as 0. - */ - funcTupDesc->attrs[0]->attlen = ((TypeTupleForm)GETSTRUCT(tuple))->typlen; - funcTupDesc->attrs[0]->atttypid = retType; - funcTupDesc->attrs[0]->attnum = 1; - funcTupDesc->attrs[0]->attbyval = ((TypeTupleForm)GETSTRUCT(tuple))->typbyval; - - /* - * make the attributes name the same as the functions - */ - namestrcpy(&funcTupDesc->attrs[0]->attname, funcname); - - return (funcTupDesc); + HeapTuple tuple; + TupleDesc funcTupDesc; + Oid retType; + char *funcname; + int4 nargs; + Oid *argtypes; + + /* + * Allocate and zero a tuple descriptor. + */ + funcTupDesc = CreateTemplateTupleDesc(1); + funcTupDesc->attrs[0] = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + memset(funcTupDesc->attrs[0], 0, ATTRIBUTE_TUPLE_SIZE); + + /* + * Lookup the function for the return type. + */ + funcname = FIgetname(funcInfo); + nargs = FIgetnArgs(funcInfo); + argtypes = FIgetArglist(funcInfo); + tuple = SearchSysCacheTuple(PRONAME, + PointerGetDatum(funcname), + Int32GetDatum(nargs), + PointerGetDatum(argtypes), + 0); + + if (!HeapTupleIsValid(tuple)) + func_error("BuildFuncTupleDesc", funcname, nargs, argtypes); + + retType = ((Form_pg_proc) GETSTRUCT(tuple))->prorettype; + + /* + * Look up the return type in pg_type for the type length. + */ + tuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(retType), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(WARN, "Function %s return type does not exist", FIgetname(funcInfo)); + + /* + * Assign some of the attributes values. Leave the rest as 0. + */ + funcTupDesc->attrs[0]->attlen = ((TypeTupleForm) GETSTRUCT(tuple))->typlen; + funcTupDesc->attrs[0]->atttypid = retType; + funcTupDesc->attrs[0]->attnum = 1; + funcTupDesc->attrs[0]->attbyval = ((TypeTupleForm) GETSTRUCT(tuple))->typbyval; + + /* + * make the attributes name the same as the functions + */ + namestrcpy(&funcTupDesc->attrs[0]->attname, funcname); + + return (funcTupDesc); } /* ---------------------------------------------------------------- - * ConstructTupleDescriptor + * ConstructTupleDescriptor * ---------------------------------------------------------------- */ -static TupleDesc +static TupleDesc ConstructTupleDescriptor(Oid heapoid, - Relation heapRelation, - List *attributeList, - int numatts, - AttrNumber attNums[]) + Relation heapRelation, + List * attributeList, + int numatts, + AttrNumber attNums[]) { - TupleDesc heapTupDesc; - TupleDesc indexTupDesc; - IndexElem *IndexKey; - TypeName *IndexKeyType; - AttrNumber atnum; /* attributeNumber[attributeOffset] */ - AttrNumber atind; - int natts; /* RelationTupleForm->relnatts */ - char *from; /* used to simplify memcpy below */ - char *to; /* used to simplify memcpy below */ - int i; - - /* ---------------- - * allocate the new tuple descriptor - * ---------------- - */ - natts = RelationGetRelationTupleForm(heapRelation)->relnatts; - - indexTupDesc = CreateTemplateTupleDesc(numatts); - - /* ---------------- - * - * ---------------- - */ - - /* ---------------- - * for each attribute we are indexing, obtain its attribute - * tuple form from either the static table of system attribute - * tuple forms or the relation tuple descriptor - * ---------------- - */ - for (i = 0; i < numatts; i += 1) { - + TupleDesc heapTupDesc; + TupleDesc indexTupDesc; + IndexElem *IndexKey; + TypeName *IndexKeyType; + AttrNumber atnum; /* attributeNumber[attributeOffset] */ + AttrNumber atind; + int natts; /* RelationTupleForm->relnatts */ + char *from; /* used to simplify memcpy below */ + char *to; /* used to simplify memcpy below */ + int i; + /* ---------------- - * get the attribute number and make sure it's valid + * allocate the new tuple descriptor * ---------------- */ - atnum = attNums[i]; - if (atnum > natts) - elog(WARN, "Cannot create index: attribute %d does not exist", - atnum); - if (attributeList) { - IndexKey = (IndexElem*) lfirst(attributeList); - attributeList = lnext(attributeList); - IndexKeyType = IndexKey->tname; - } else { - IndexKeyType = NULL; - } - - indexTupDesc->attrs[i] = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); - + natts = RelationGetRelationTupleForm(heapRelation)->relnatts; + + indexTupDesc = CreateTemplateTupleDesc(numatts); + /* ---------------- - * determine which tuple descriptor to copy + * * ---------------- */ - if (!AttrNumberIsForUserDefinedAttr(atnum)) { - - /* ---------------- - * here we are indexing on a system attribute (-1...-12) - * so we convert atnum into a usable index 0...11 so we can - * use it to dereference the array sysatts[] which stores - * tuple descriptor information for system attributes. - * ---------------- - */ - if (atnum <= FirstLowInvalidHeapAttributeNumber || atnum >= 0 ) - elog(WARN, "Cannot create index on system attribute: attribute number out of range (%d)", atnum); - atind = (-atnum) - 1; - - from = (char *) (& sysatts[atind]); - - } else { - /* ---------------- - * here we are indexing on a normal attribute (1...n) - * ---------------- - */ - - heapTupDesc = RelationGetTupleDescriptor(heapRelation); - atind = AttrNumberGetAttrOffset(atnum); - - from = (char *) (heapTupDesc->attrs[ atind ]); - } - + /* ---------------- - * now that we've determined the "from", let's copy - * the tuple desc data... + * for each attribute we are indexing, obtain its attribute + * tuple form from either the static table of system attribute + * tuple forms or the relation tuple descriptor * ---------------- */ - - to = (char *) (indexTupDesc->attrs[ i ]); - memcpy(to, from, ATTRIBUTE_TUPLE_SIZE); - - ((AttributeTupleForm) to)->attnum = i+1; - ((AttributeTupleForm) to)->attcacheoff = -1; - - ((AttributeTupleForm) to)->attnotnull = false; - ((AttributeTupleForm) to)->atthasdef = false; - - /* if the keytype is defined, we need to change the tuple form's - atttypid & attlen field to match that of the key's type */ - if (IndexKeyType != NULL) { - HeapTuple tup; - - tup = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(IndexKeyType->name), - 0,0,0); - if(!HeapTupleIsValid(tup)) - elog(WARN, "create index: type '%s' undefined", - IndexKeyType->name); - ((AttributeTupleForm) to)->atttypid = tup->t_oid; - ((AttributeTupleForm) to)->attbyval = - ((TypeTupleForm) ((char *)tup + tup->t_hoff))->typbyval; - if (IndexKeyType->typlen > 0) - ((AttributeTupleForm) to)->attlen = IndexKeyType->typlen; - else ((AttributeTupleForm) to)->attlen = - ((TypeTupleForm) ((char *)tup + tup->t_hoff))->typlen; + for (i = 0; i < numatts; i += 1) + { + + /* ---------------- + * get the attribute number and make sure it's valid + * ---------------- + */ + atnum = attNums[i]; + if (atnum > natts) + elog(WARN, "Cannot create index: attribute %d does not exist", + atnum); + if (attributeList) + { + IndexKey = (IndexElem *) lfirst(attributeList); + attributeList = lnext(attributeList); + IndexKeyType = IndexKey->tname; + } + else + { + IndexKeyType = NULL; + } + + indexTupDesc->attrs[i] = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + + /* ---------------- + * determine which tuple descriptor to copy + * ---------------- + */ + if (!AttrNumberIsForUserDefinedAttr(atnum)) + { + + /* ---------------- + * here we are indexing on a system attribute (-1...-12) + * so we convert atnum into a usable index 0...11 so we can + * use it to dereference the array sysatts[] which stores + * tuple descriptor information for system attributes. + * ---------------- + */ + if (atnum <= FirstLowInvalidHeapAttributeNumber || atnum >= 0) + elog(WARN, "Cannot create index on system attribute: attribute number out of range (%d)", atnum); + atind = (-atnum) - 1; + + from = (char *) (&sysatts[atind]); + + } + else + { + /* ---------------- + * here we are indexing on a normal attribute (1...n) + * ---------------- + */ + + heapTupDesc = RelationGetTupleDescriptor(heapRelation); + atind = AttrNumberGetAttrOffset(atnum); + + from = (char *) (heapTupDesc->attrs[atind]); + } + + /* ---------------- + * now that we've determined the "from", let's copy + * the tuple desc data... + * ---------------- + */ + + to = (char *) (indexTupDesc->attrs[i]); + memcpy(to, from, ATTRIBUTE_TUPLE_SIZE); + + ((AttributeTupleForm) to)->attnum = i + 1; + ((AttributeTupleForm) to)->attcacheoff = -1; + + ((AttributeTupleForm) to)->attnotnull = false; + ((AttributeTupleForm) to)->atthasdef = false; + + /* + * if the keytype is defined, we need to change the tuple form's + * atttypid & attlen field to match that of the key's type + */ + if (IndexKeyType != NULL) + { + HeapTuple tup; + + tup = SearchSysCacheTuple(TYPNAME, + PointerGetDatum(IndexKeyType->name), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "create index: type '%s' undefined", + IndexKeyType->name); + ((AttributeTupleForm) to)->atttypid = tup->t_oid; + ((AttributeTupleForm) to)->attbyval = + ((TypeTupleForm) ((char *) tup + tup->t_hoff))->typbyval; + if (IndexKeyType->typlen > 0) + ((AttributeTupleForm) to)->attlen = IndexKeyType->typlen; + else + ((AttributeTupleForm) to)->attlen = + ((TypeTupleForm) ((char *) tup + tup->t_hoff))->typlen; + } + + + /* ---------------- + * now we have to drop in the proper relation descriptor + * into the copied tuple form's attrelid and we should be + * all set. + * ---------------- + */ + ((AttributeTupleForm) to)->attrelid = heapoid; } - - /* ---------------- - * now we have to drop in the proper relation descriptor - * into the copied tuple form's attrelid and we should be - * all set. - * ---------------- - */ - ((AttributeTupleForm) to)->attrelid = heapoid; - } - - return indexTupDesc; + return indexTupDesc; } /* ---------------------------------------------------------------- * AccessMethodObjectIdGetAccessMethodTupleForm -- - * Returns the formated access method tuple given its object identifier. + * Returns the formated access method tuple given its object identifier. * * XXX ADD INDEXING * * Note: - * Assumes object identifier is valid. + * Assumes object identifier is valid. * ---------------------------------------------------------------- */ Form_pg_am AccessMethodObjectIdGetAccessMethodTupleForm(Oid accessMethodObjectId) { - Relation pg_am_desc; - HeapScanDesc pg_am_scan; - HeapTuple pg_am_tuple; - ScanKeyData key; - Form_pg_am form; - - /* ---------------- - * form a scan key for the pg_am relation - * ---------------- - */ - ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(accessMethodObjectId)); - - /* ---------------- - * fetch the desired access method tuple - * ---------------- - */ - pg_am_desc = heap_openr(AccessMethodRelationName); - pg_am_scan = heap_beginscan(pg_am_desc, 0, NowTimeQual, 1, &key); - - pg_am_tuple = heap_getnext(pg_am_scan, 0, (Buffer *)NULL); - - /* ---------------- - * return NULL if not found - * ---------------- - */ - if (! HeapTupleIsValid(pg_am_tuple)) { + Relation pg_am_desc; + HeapScanDesc pg_am_scan; + HeapTuple pg_am_tuple; + ScanKeyData key; + Form_pg_am form; + + /* ---------------- + * form a scan key for the pg_am relation + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(accessMethodObjectId)); + + /* ---------------- + * fetch the desired access method tuple + * ---------------- + */ + pg_am_desc = heap_openr(AccessMethodRelationName); + pg_am_scan = heap_beginscan(pg_am_desc, 0, NowTimeQual, 1, &key); + + pg_am_tuple = heap_getnext(pg_am_scan, 0, (Buffer *) NULL); + + /* ---------------- + * return NULL if not found + * ---------------- + */ + if (!HeapTupleIsValid(pg_am_tuple)) + { + heap_endscan(pg_am_scan); + heap_close(pg_am_desc); + return (NULL); + } + + /* ---------------- + * if found am tuple, then copy the form and return the copy + * ---------------- + */ + form = (Form_pg_am) palloc(sizeof *form); + memcpy(form, GETSTRUCT(pg_am_tuple), sizeof *form); + heap_endscan(pg_am_scan); heap_close(pg_am_desc); - return (NULL); - } - - /* ---------------- - * if found am tuple, then copy the form and return the copy - * ---------------- - */ - form = (Form_pg_am)palloc(sizeof *form); - memcpy(form, GETSTRUCT(pg_am_tuple), sizeof *form); - - heap_endscan(pg_am_scan); - heap_close(pg_am_desc); - - return (form); + + return (form); } /* ---------------------------------------------------------------- - * ConstructIndexReldesc + * ConstructIndexReldesc * ---------------------------------------------------------------- */ static void ConstructIndexReldesc(Relation indexRelation, Oid amoid) { - extern GlobalMemory CacheCxt; - MemoryContext oldcxt; - - /* ---------------- - * here we make certain to allocate the access method - * tuple within the cache context lest it vanish when the - * context changes - * ---------------- - */ - if (!CacheCxt) - CacheCxt = CreateGlobalMemory("Cache"); - - oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); - - indexRelation->rd_am = - AccessMethodObjectIdGetAccessMethodTupleForm(amoid); - - MemoryContextSwitchTo(oldcxt); - - /* ---------------- - * XXX missing the initialization of some other fields - * ---------------- - */ - - indexRelation->rd_rel->relowner = GetUserId(); - - indexRelation->rd_rel->relam = amoid; - indexRelation->rd_rel->reltuples = 1; /* XXX */ - indexRelation->rd_rel->relexpires = 0; /* XXX */ - indexRelation->rd_rel->relpreserved = 0; /* XXX */ - indexRelation->rd_rel->relkind = RELKIND_INDEX; - indexRelation->rd_rel->relarch = 'n'; /* XXX */ + extern GlobalMemory CacheCxt; + MemoryContext oldcxt; + + /* ---------------- + * here we make certain to allocate the access method + * tuple within the cache context lest it vanish when the + * context changes + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + + indexRelation->rd_am = + AccessMethodObjectIdGetAccessMethodTupleForm(amoid); + + MemoryContextSwitchTo(oldcxt); + + /* ---------------- + * XXX missing the initialization of some other fields + * ---------------- + */ + + indexRelation->rd_rel->relowner = GetUserId(); + + indexRelation->rd_rel->relam = amoid; + indexRelation->rd_rel->reltuples = 1; /* XXX */ + indexRelation->rd_rel->relexpires = 0; /* XXX */ + indexRelation->rd_rel->relpreserved = 0; /* XXX */ + indexRelation->rd_rel->relkind = RELKIND_INDEX; + indexRelation->rd_rel->relarch = 'n'; /* XXX */ } /* ---------------------------------------------------------------- - * UpdateRelationRelation + * UpdateRelationRelation * ---------------------------------------------------------------- */ -static Oid +static Oid UpdateRelationRelation(Relation indexRelation) { - Relation pg_class; - HeapTuple tuple; - Oid tupleOid; - Relation idescs[Num_pg_class_indices]; - - pg_class = heap_openr(RelationRelationName); - - /* XXX Natts_pg_class_fixed is a hack - see pg_class.h */ - tuple = heap_addheader(Natts_pg_class_fixed, - sizeof(*indexRelation->rd_rel), - (char *) indexRelation->rd_rel); - - /* ---------------- - * the new tuple must have the same oid as the relcache entry for the - * index. sure would be embarassing to do this sort of thing in polite - * company. - * ---------------- - */ - tuple->t_oid = indexRelation->rd_id; - heap_insert(pg_class, tuple); - - /* - * During normal processing, we need to make sure that the system - * catalog indices are correct. Bootstrap (initdb) time doesn't - * require this, because we make sure that the indices are correct - * just before exiting. - */ - - if (!IsBootstrapProcessingMode()) { - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, tuple); - CatalogCloseIndices(Num_pg_class_indices, idescs); - } - - tupleOid = tuple->t_oid; - pfree(tuple); - heap_close(pg_class); - - return(tupleOid); + Relation pg_class; + HeapTuple tuple; + Oid tupleOid; + Relation idescs[Num_pg_class_indices]; + + pg_class = heap_openr(RelationRelationName); + + /* XXX Natts_pg_class_fixed is a hack - see pg_class.h */ + tuple = heap_addheader(Natts_pg_class_fixed, + sizeof(*indexRelation->rd_rel), + (char *) indexRelation->rd_rel); + + /* ---------------- + * the new tuple must have the same oid as the relcache entry for the + * index. sure would be embarassing to do this sort of thing in polite + * company. + * ---------------- + */ + tuple->t_oid = indexRelation->rd_id; + heap_insert(pg_class, tuple); + + /* + * During normal processing, we need to make sure that the system + * catalog indices are correct. Bootstrap (initdb) time doesn't + * require this, because we make sure that the indices are correct + * just before exiting. + */ + + if (!IsBootstrapProcessingMode()) + { + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, tuple); + CatalogCloseIndices(Num_pg_class_indices, idescs); + } + + tupleOid = tuple->t_oid; + pfree(tuple); + heap_close(pg_class); + + return (tupleOid); } /* ---------------------------------------------------------------- - * InitializeAttributeOids + * InitializeAttributeOids * ---------------------------------------------------------------- */ static void InitializeAttributeOids(Relation indexRelation, - int numatts, - Oid indexoid) + int numatts, + Oid indexoid) { - TupleDesc tupleDescriptor; - int i; - - tupleDescriptor = RelationGetTupleDescriptor(indexRelation); - - for (i = 0; i < numatts; i += 1) - tupleDescriptor->attrs[i]->attrelid = indexoid; + TupleDesc tupleDescriptor; + int i; + + tupleDescriptor = RelationGetTupleDescriptor(indexRelation); + + for (i = 0; i < numatts; i += 1) + tupleDescriptor->attrs[i]->attrelid = indexoid; } /* ---------------------------------------------------------------- - * AppendAttributeTuples + * AppendAttributeTuples * - * XXX For now, only change the ATTNUM attribute value + * XXX For now, only change the ATTNUM attribute value * ---------------------------------------------------------------- */ static void AppendAttributeTuples(Relation indexRelation, int numatts) { - Relation pg_attribute; - HeapTuple tuple; - HeapTuple newtuple; - bool hasind; - Relation idescs[Num_pg_attr_indices]; - - Datum value[ Natts_pg_attribute ]; - char nullv[ Natts_pg_attribute ]; - char replace[ Natts_pg_attribute ]; - - TupleDesc indexTupDesc; - int i; - - /* ---------------- - * open the attribute relation - * XXX ADD INDEXING - * ---------------- - */ - pg_attribute = heap_openr(AttributeRelationName); - - /* ---------------- - * initialize null[], replace[] and value[] - * ---------------- - */ - memset(nullv, ' ', Natts_pg_attribute); - memset(replace, ' ', Natts_pg_attribute); - - /* ---------------- - * create the first attribute tuple. - * XXX For now, only change the ATTNUM attribute value - * ---------------- - */ - replace[ Anum_pg_attribute_attnum - 1 ] = 'r'; - replace[ Anum_pg_attribute_attcacheoff - 1 ] = 'r'; - - value[ Anum_pg_attribute_attnum - 1 ] = Int16GetDatum(1); - value[ Anum_pg_attribute_attcacheoff - 1 ] = Int32GetDatum(-1); - - tuple = heap_addheader(Natts_pg_attribute, - sizeof *(indexRelation->rd_att->attrs[0]), - (char *)(indexRelation->rd_att->attrs[0])); - - hasind = false; - if (!IsBootstrapProcessingMode() && pg_attribute->rd_rel->relhasindex) { - hasind = true; - CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); - } - - /* ---------------- - * insert the first attribute tuple. - * ---------------- - */ - tuple = heap_modifytuple(tuple, - InvalidBuffer, - pg_attribute, - value, - nullv, - replace); - - heap_insert(pg_attribute, tuple); - if (hasind) - CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, tuple); - - /* ---------------- - * now we use the information in the index tuple - * descriptor to form the remaining attribute tuples. - * ---------------- - */ - indexTupDesc = RelationGetTupleDescriptor(indexRelation); - - for (i = 1; i < numatts; i += 1) { + Relation pg_attribute; + HeapTuple tuple; + HeapTuple newtuple; + bool hasind; + Relation idescs[Num_pg_attr_indices]; + + Datum value[Natts_pg_attribute]; + char nullv[Natts_pg_attribute]; + char replace[Natts_pg_attribute]; + + TupleDesc indexTupDesc; + int i; + /* ---------------- - * process the remaining attributes... + * open the attribute relation + * XXX ADD INDEXING * ---------------- */ - memmove(GETSTRUCT(tuple), - (char *)indexTupDesc->attrs[i], - sizeof (FormData_pg_attribute)); - - value[ Anum_pg_attribute_attnum - 1 ] = Int16GetDatum(i + 1); - - newtuple = heap_modifytuple(tuple, - InvalidBuffer, - pg_attribute, - value, - nullv, - replace); - - heap_insert(pg_attribute, newtuple); + pg_attribute = heap_openr(AttributeRelationName); + + /* ---------------- + * initialize null[], replace[] and value[] + * ---------------- + */ + memset(nullv, ' ', Natts_pg_attribute); + memset(replace, ' ', Natts_pg_attribute); + + /* ---------------- + * create the first attribute tuple. + * XXX For now, only change the ATTNUM attribute value + * ---------------- + */ + replace[Anum_pg_attribute_attnum - 1] = 'r'; + replace[Anum_pg_attribute_attcacheoff - 1] = 'r'; + + value[Anum_pg_attribute_attnum - 1] = Int16GetDatum(1); + value[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(-1); + + tuple = heap_addheader(Natts_pg_attribute, + sizeof *(indexRelation->rd_att->attrs[0]), + (char *) (indexRelation->rd_att->attrs[0])); + + hasind = false; + if (!IsBootstrapProcessingMode() && pg_attribute->rd_rel->relhasindex) + { + hasind = true; + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); + } + + /* ---------------- + * insert the first attribute tuple. + * ---------------- + */ + tuple = heap_modifytuple(tuple, + InvalidBuffer, + pg_attribute, + value, + nullv, + replace); + + heap_insert(pg_attribute, tuple); if (hasind) - CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, newtuple); - + CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, tuple); + + /* ---------------- + * now we use the information in the index tuple + * descriptor to form the remaining attribute tuples. + * ---------------- + */ + indexTupDesc = RelationGetTupleDescriptor(indexRelation); + + for (i = 1; i < numatts; i += 1) + { + /* ---------------- + * process the remaining attributes... + * ---------------- + */ + memmove(GETSTRUCT(tuple), + (char *) indexTupDesc->attrs[i], + sizeof(FormData_pg_attribute)); + + value[Anum_pg_attribute_attnum - 1] = Int16GetDatum(i + 1); + + newtuple = heap_modifytuple(tuple, + InvalidBuffer, + pg_attribute, + value, + nullv, + replace); + + heap_insert(pg_attribute, newtuple); + if (hasind) + CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, newtuple); + + /* ---------------- + * ModifyHeapTuple returns a new copy of a tuple + * so we free the original and use the copy.. + * ---------------- + */ + pfree(tuple); + tuple = newtuple; + } + /* ---------------- - * ModifyHeapTuple returns a new copy of a tuple - * so we free the original and use the copy.. + * close the attribute relation and free the tuple * ---------------- */ + heap_close(pg_attribute); + + if (hasind) + CatalogCloseIndices(Num_pg_attr_indices, idescs); + pfree(tuple); - tuple = newtuple; - } - - /* ---------------- - * close the attribute relation and free the tuple - * ---------------- - */ - heap_close(pg_attribute); - - if (hasind) - CatalogCloseIndices(Num_pg_attr_indices, idescs); - - pfree(tuple); } /* ---------------------------------------------------------------- - * UpdateIndexRelation + * UpdateIndexRelation * ---------------------------------------------------------------- */ static void UpdateIndexRelation(Oid indexoid, - Oid heapoid, - FuncIndexInfo *funcInfo, - int natts, - AttrNumber attNums[], - Oid classOids[], - Node *predicate, - List *attributeList, - bool islossy, - bool unique) + Oid heapoid, + FuncIndexInfo * funcInfo, + int natts, + AttrNumber attNums[], + Oid classOids[], + Node * predicate, + List * attributeList, + bool islossy, + bool unique) { - IndexTupleForm indexForm; - IndexElem *IndexKey; - char *predString; - text *predText; - int predLen, itupLen; - Relation pg_index; - HeapTuple tuple; - int i; - - /* ---------------- - * allocate an IndexTupleForm big enough to hold the - * index-predicate (if any) in string form - * ---------------- - */ - if (predicate != NULL) { - predString = nodeToString(predicate); - predText = (text *)fmgr(F_TEXTIN, predString); - pfree(predString); - } else { - predText = (text *)fmgr(F_TEXTIN, ""); - } - predLen = VARSIZE(predText); - itupLen = predLen + sizeof(FormData_pg_index); - indexForm = (IndexTupleForm) palloc(itupLen); - - memmove((char *)& indexForm->indpred, (char *)predText, predLen); - - /* ---------------- - * store the oid information into the index tuple form - * ---------------- - */ - indexForm->indrelid = heapoid; - indexForm->indexrelid = indexoid; - indexForm->indproc = (PointerIsValid(funcInfo)) ? - FIgetProcOid(funcInfo) : InvalidOid; - indexForm->indislossy = islossy; - indexForm->indisunique = unique; - - indexForm->indhaskeytype = 0; - while (attributeList != NIL ) - { - IndexKey = (IndexElem*) lfirst(attributeList); - if ( IndexKey->tname != NULL ) + IndexTupleForm indexForm; + IndexElem *IndexKey; + char *predString; + text *predText; + int predLen, + itupLen; + Relation pg_index; + HeapTuple tuple; + int i; + + /* ---------------- + * allocate an IndexTupleForm big enough to hold the + * index-predicate (if any) in string form + * ---------------- + */ + if (predicate != NULL) { - indexForm->indhaskeytype = 1; - break; - } - attributeList = lnext(attributeList); - } - - memset((char *)& indexForm->indkey[0], 0, sizeof indexForm->indkey); - memset((char *)& indexForm->indclass[0], 0, sizeof indexForm->indclass); - - /* ---------------- - * copy index key and op class information - * ---------------- - */ - for (i = 0; i < natts; i += 1) { - indexForm->indkey[i] = attNums[i]; - indexForm->indclass[i] = classOids[i]; - } - /* - * If we have a functional index, add all attribute arguments - */ - if (PointerIsValid(funcInfo)) + predString = nodeToString(predicate); + predText = (text *) fmgr(F_TEXTIN, predString); + pfree(predString); + } + else { - for (i=1; i < FIgetnArgs(funcInfo); i++) - indexForm->indkey[i] = attNums[i]; + predText = (text *) fmgr(F_TEXTIN, ""); } - - indexForm->indisclustered = '\0'; /* XXX constant */ - indexForm->indisarchived = '\0'; /* XXX constant */ - - /* ---------------- - * open the system catalog index relation - * ---------------- - */ - pg_index = heap_openr(IndexRelationName); - - /* ---------------- - * form a tuple to insert into pg_index - * ---------------- - */ - tuple = heap_addheader(Natts_pg_index, - itupLen, - (char *)indexForm); - - /* ---------------- - * insert the tuple into the pg_index - * XXX ADD INDEX TUPLES TOO - * ---------------- - */ - heap_insert(pg_index, tuple); - - /* ---------------- - * close the relation and free the tuple - * ---------------- - */ - heap_close(pg_index); - pfree(predText); - pfree(indexForm); - pfree(tuple); + predLen = VARSIZE(predText); + itupLen = predLen + sizeof(FormData_pg_index); + indexForm = (IndexTupleForm) palloc(itupLen); + + memmove((char *) &indexForm->indpred, (char *) predText, predLen); + + /* ---------------- + * store the oid information into the index tuple form + * ---------------- + */ + indexForm->indrelid = heapoid; + indexForm->indexrelid = indexoid; + indexForm->indproc = (PointerIsValid(funcInfo)) ? + FIgetProcOid(funcInfo) : InvalidOid; + indexForm->indislossy = islossy; + indexForm->indisunique = unique; + + indexForm->indhaskeytype = 0; + while (attributeList != NIL) + { + IndexKey = (IndexElem *) lfirst(attributeList); + if (IndexKey->tname != NULL) + { + indexForm->indhaskeytype = 1; + break; + } + attributeList = lnext(attributeList); + } + + memset((char *) &indexForm->indkey[0], 0, sizeof indexForm->indkey); + memset((char *) &indexForm->indclass[0], 0, sizeof indexForm->indclass); + + /* ---------------- + * copy index key and op class information + * ---------------- + */ + for (i = 0; i < natts; i += 1) + { + indexForm->indkey[i] = attNums[i]; + indexForm->indclass[i] = classOids[i]; + } + + /* + * If we have a functional index, add all attribute arguments + */ + if (PointerIsValid(funcInfo)) + { + for (i = 1; i < FIgetnArgs(funcInfo); i++) + indexForm->indkey[i] = attNums[i]; + } + + indexForm->indisclustered = '\0'; /* XXX constant */ + indexForm->indisarchived = '\0'; /* XXX constant */ + + /* ---------------- + * open the system catalog index relation + * ---------------- + */ + pg_index = heap_openr(IndexRelationName); + + /* ---------------- + * form a tuple to insert into pg_index + * ---------------- + */ + tuple = heap_addheader(Natts_pg_index, + itupLen, + (char *) indexForm); + + /* ---------------- + * insert the tuple into the pg_index + * XXX ADD INDEX TUPLES TOO + * ---------------- + */ + heap_insert(pg_index, tuple); + + /* ---------------- + * close the relation and free the tuple + * ---------------- + */ + heap_close(pg_index); + pfree(predText); + pfree(indexForm); + pfree(tuple); } /* ---------------------------------------------------------------- - * UpdateIndexPredicate + * UpdateIndexPredicate * ---------------------------------------------------------------- */ void -UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate) +UpdateIndexPredicate(Oid indexoid, Node * oldPred, Node * predicate) { - Node *newPred; - char *predString; - text *predText; - Relation pg_index; - HeapTuple tuple; - HeapTuple newtup; - ScanKeyData entry; - HeapScanDesc scan; - Buffer buffer; - int i; - Datum values[Natts_pg_index]; - char nulls[Natts_pg_index]; - char replace[Natts_pg_index]; - - /* - * Construct newPred as a CNF expression equivalent to the OR of the - * original partial-index predicate ("oldPred") and the extension - * predicate ("predicate"). - * - * This should really try to process the result to change things like - * "a>2 OR a>1" to simply "a>1", but for now all it does is make sure - * that if the extension predicate is NULL (i.e., it is being extended - * to be a complete index), then newPred will be NULL - in effect, - * changing "a>2 OR TRUE" to "TRUE". --Nels, Jan '93 - */ - newPred = NULL; - if (predicate != NULL) { - newPred = - (Node*)make_orclause(lcons(make_andclause((List*)predicate), - lcons(make_andclause((List*)oldPred), - NIL))); - newPred = (Node*)cnfify((Expr*)newPred, true); - } - - /* translate the index-predicate to string form */ - if (newPred != NULL) { - predString = nodeToString(newPred); - predText = (text *)fmgr(F_TEXTIN, predString); - pfree(predString); - } else { - predText = (text *)fmgr(F_TEXTIN, ""); - } - - /* open the index system catalog relation */ - pg_index = heap_openr(IndexRelationName); - - ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indexrelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(indexoid)); - - scan = heap_beginscan(pg_index, 0, NowTimeQual, 1, &entry); - tuple = heap_getnext(scan, 0, &buffer); - heap_endscan(scan); - - for (i = 0; i < Natts_pg_index; i++) { - nulls[i] = heap_attisnull(tuple, i+1) ? 'n' : ' '; - replace[i] = ' '; - values[i] = (Datum) NULL; - } - - replace[Anum_pg_index_indpred - 1] = 'r'; - values[Anum_pg_index_indpred - 1] = (Datum) predText; - - newtup = heap_modifytuple(tuple, buffer, pg_index, values, nulls, replace); - - heap_replace(pg_index, &(newtup->t_ctid), newtup); - - heap_close(pg_index); - pfree(predText); + Node *newPred; + char *predString; + text *predText; + Relation pg_index; + HeapTuple tuple; + HeapTuple newtup; + ScanKeyData entry; + HeapScanDesc scan; + Buffer buffer; + int i; + Datum values[Natts_pg_index]; + char nulls[Natts_pg_index]; + char replace[Natts_pg_index]; + + /* + * Construct newPred as a CNF expression equivalent to the OR of the + * original partial-index predicate ("oldPred") and the extension + * predicate ("predicate"). + * + * This should really try to process the result to change things like + * "a>2 OR a>1" to simply "a>1", but for now all it does is make sure + * that if the extension predicate is NULL (i.e., it is being extended + * to be a complete index), then newPred will be NULL - in effect, + * changing "a>2 OR TRUE" to "TRUE". --Nels, Jan '93 + */ + newPred = NULL; + if (predicate != NULL) + { + newPred = + (Node *) make_orclause(lcons(make_andclause((List *) predicate), + lcons(make_andclause((List *) oldPred), + NIL))); + newPred = (Node *) cnfify((Expr *) newPred, true); + } + + /* translate the index-predicate to string form */ + if (newPred != NULL) + { + predString = nodeToString(newPred); + predText = (text *) fmgr(F_TEXTIN, predString); + pfree(predString); + } + else + { + predText = (text *) fmgr(F_TEXTIN, ""); + } + + /* open the index system catalog relation */ + pg_index = heap_openr(IndexRelationName); + + ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indexrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(indexoid)); + + scan = heap_beginscan(pg_index, 0, NowTimeQual, 1, &entry); + tuple = heap_getnext(scan, 0, &buffer); + heap_endscan(scan); + + for (i = 0; i < Natts_pg_index; i++) + { + nulls[i] = heap_attisnull(tuple, i + 1) ? 'n' : ' '; + replace[i] = ' '; + values[i] = (Datum) NULL; + } + + replace[Anum_pg_index_indpred - 1] = 'r'; + values[Anum_pg_index_indpred - 1] = (Datum) predText; + + newtup = heap_modifytuple(tuple, buffer, pg_index, values, nulls, replace); + + heap_replace(pg_index, &(newtup->t_ctid), newtup); + + heap_close(pg_index); + pfree(predText); } /* ---------------------------------------------------------------- - * InitIndexStrategy + * InitIndexStrategy * ---------------------------------------------------------------- */ void InitIndexStrategy(int numatts, - Relation indexRelation, - Oid accessMethodObjectId) + Relation indexRelation, + Oid accessMethodObjectId) { - IndexStrategy strategy; - RegProcedure *support; - uint16 amstrategies; - uint16 amsupport; - Oid attrelid; - Size strsize; - extern GlobalMemory CacheCxt; - - /* ---------------- - * get information from the index relation descriptor - * ---------------- - */ - attrelid = indexRelation->rd_att->attrs[0]->attrelid; - amstrategies = indexRelation->rd_am->amstrategies; - amsupport = indexRelation->rd_am->amsupport; - - /* ---------------- - * get the size of the strategy - * ---------------- - */ - strsize = AttributeNumberGetIndexStrategySize(numatts, amstrategies); - - /* ---------------- - * allocate the new index strategy structure - * - * the index strategy has to be allocated in the same - * context as the relation descriptor cache or else - * it will be lost at the end of the transaction. - * ---------------- - */ - if (!CacheCxt) - CacheCxt = CreateGlobalMemory("Cache"); - - strategy = (IndexStrategy) - MemoryContextAlloc((MemoryContext)CacheCxt, strsize); - - if (amsupport > 0) { - strsize = numatts * (amsupport * sizeof(RegProcedure)); - support = (RegProcedure *) MemoryContextAlloc((MemoryContext)CacheCxt, - strsize); - } else { - support = (RegProcedure *) NULL; - } - - /* ---------------- - * fill in the index strategy structure with information - * from the catalogs. Note: we use heap override mode - * in order to be allowed to see the correct information in the - * catalogs, even though our transaction has not yet committed. - * ---------------- - */ - setheapoverride(1); - - IndexSupportInitialize(strategy, support, - attrelid, accessMethodObjectId, - amstrategies, amsupport, numatts); - - setheapoverride(0); - - /* ---------------- - * store the strategy information in the index reldesc - * ---------------- - */ - RelationSetIndexSupport(indexRelation, strategy, support); + IndexStrategy strategy; + RegProcedure *support; + uint16 amstrategies; + uint16 amsupport; + Oid attrelid; + Size strsize; + extern GlobalMemory CacheCxt; + + /* ---------------- + * get information from the index relation descriptor + * ---------------- + */ + attrelid = indexRelation->rd_att->attrs[0]->attrelid; + amstrategies = indexRelation->rd_am->amstrategies; + amsupport = indexRelation->rd_am->amsupport; + + /* ---------------- + * get the size of the strategy + * ---------------- + */ + strsize = AttributeNumberGetIndexStrategySize(numatts, amstrategies); + + /* ---------------- + * allocate the new index strategy structure + * + * the index strategy has to be allocated in the same + * context as the relation descriptor cache or else + * it will be lost at the end of the transaction. + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + strategy = (IndexStrategy) + MemoryContextAlloc((MemoryContext) CacheCxt, strsize); + + if (amsupport > 0) + { + strsize = numatts * (amsupport * sizeof(RegProcedure)); + support = (RegProcedure *) MemoryContextAlloc((MemoryContext) CacheCxt, + strsize); + } + else + { + support = (RegProcedure *) NULL; + } + + /* ---------------- + * fill in the index strategy structure with information + * from the catalogs. Note: we use heap override mode + * in order to be allowed to see the correct information in the + * catalogs, even though our transaction has not yet committed. + * ---------------- + */ + setheapoverride(1); + + IndexSupportInitialize(strategy, support, + attrelid, accessMethodObjectId, + amstrategies, amsupport, numatts); + + setheapoverride(0); + + /* ---------------- + * store the strategy information in the index reldesc + * ---------------- + */ + RelationSetIndexSupport(indexRelation, strategy, support); } /* ---------------------------------------------------------------- - * index_create + * index_create * ---------------------------------------------------------------- */ void index_create(char *heapRelationName, - char *indexRelationName, - FuncIndexInfo *funcInfo, - List *attributeList, - Oid accessMethodObjectId, - int numatts, - AttrNumber attNums[], - Oid classObjectId[], - uint16 parameterCount, - Datum *parameter, - Node *predicate, - bool islossy, - bool unique) + char *indexRelationName, + FuncIndexInfo * funcInfo, + List * attributeList, + Oid accessMethodObjectId, + int numatts, + AttrNumber attNums[], + Oid classObjectId[], + uint16 parameterCount, + Datum * parameter, + Node * predicate, + bool islossy, + bool unique) { - Relation heapRelation; - Relation indexRelation; - TupleDesc indexTupDesc; - Oid heapoid; - Oid indexoid; - PredInfo *predInfo; - - /* ---------------- - * check parameters - * ---------------- - */ - if (numatts < 1) - elog(WARN, "must index at least one attribute"); - - /* ---------------- - * get heap relation oid and open the heap relation - * XXX ADD INDEXING - * ---------------- - */ - heapoid = GetHeapRelationOid(heapRelationName, indexRelationName); - - heapRelation = heap_open(heapoid); - - /* ---------------- - * write lock heap to guarantee exclusive access - * ---------------- - */ - - RelationSetLockForWrite(heapRelation); - - /* ---------------- - * construct new tuple descriptor - * ---------------- - */ - if (PointerIsValid(funcInfo)) - indexTupDesc = BuildFuncTupleDesc(funcInfo); - else - indexTupDesc = ConstructTupleDescriptor(heapoid, - heapRelation, - attributeList, - numatts, - attNums); - - /* ---------------- - * create the index relation - * ---------------- - */ - indexRelation = heap_creatr(indexRelationName, - DEFAULT_SMGR, - indexTupDesc); - - /* ---------------- - * construct the index relation descriptor - * - * XXX should have a proper way to create cataloged relations - * ---------------- - */ - ConstructIndexReldesc(indexRelation, accessMethodObjectId); - - /* ---------------- - * add index to catalogs - * (append RELATION tuple) - * ---------------- - */ - indexoid = UpdateRelationRelation(indexRelation); - - /* ---------------- - * Now get the index procedure (only relevant for functional indices). - * ---------------- - */ - - if (PointerIsValid(funcInfo)) + Relation heapRelation; + Relation indexRelation; + TupleDesc indexTupDesc; + Oid heapoid; + Oid indexoid; + PredInfo *predInfo; + + /* ---------------- + * check parameters + * ---------------- + */ + if (numatts < 1) + elog(WARN, "must index at least one attribute"); + + /* ---------------- + * get heap relation oid and open the heap relation + * XXX ADD INDEXING + * ---------------- + */ + heapoid = GetHeapRelationOid(heapRelationName, indexRelationName); + + heapRelation = heap_open(heapoid); + + /* ---------------- + * write lock heap to guarantee exclusive access + * ---------------- + */ + + RelationSetLockForWrite(heapRelation); + + /* ---------------- + * construct new tuple descriptor + * ---------------- + */ + if (PointerIsValid(funcInfo)) + indexTupDesc = BuildFuncTupleDesc(funcInfo); + else + indexTupDesc = ConstructTupleDescriptor(heapoid, + heapRelation, + attributeList, + numatts, + attNums); + + /* ---------------- + * create the index relation + * ---------------- + */ + indexRelation = heap_creatr(indexRelationName, + DEFAULT_SMGR, + indexTupDesc); + + /* ---------------- + * construct the index relation descriptor + * + * XXX should have a proper way to create cataloged relations + * ---------------- + */ + ConstructIndexReldesc(indexRelation, accessMethodObjectId); + + /* ---------------- + * add index to catalogs + * (append RELATION tuple) + * ---------------- + */ + indexoid = UpdateRelationRelation(indexRelation); + + /* ---------------- + * Now get the index procedure (only relevant for functional indices). + * ---------------- + */ + + if (PointerIsValid(funcInfo)) { - HeapTuple proc_tup; - - proc_tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(FIgetname(funcInfo)), - Int32GetDatum(FIgetnArgs(funcInfo)), - PointerGetDatum(FIgetArglist(funcInfo)), - 0); - - if (!HeapTupleIsValid(proc_tup)) { - func_error("index_create", FIgetname(funcInfo), - FIgetnArgs(funcInfo), - FIgetArglist(funcInfo)); - } - FIgetProcOid(funcInfo) = proc_tup->t_oid; + HeapTuple proc_tup; + + proc_tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(FIgetname(funcInfo)), + Int32GetDatum(FIgetnArgs(funcInfo)), + PointerGetDatum(FIgetArglist(funcInfo)), + 0); + + if (!HeapTupleIsValid(proc_tup)) + { + func_error("index_create", FIgetname(funcInfo), + FIgetnArgs(funcInfo), + FIgetArglist(funcInfo)); + } + FIgetProcOid(funcInfo) = proc_tup->t_oid; + } + + /* ---------------- + * now update the object id's of all the attribute + * tuple forms in the index relation's tuple descriptor + * ---------------- + */ + InitializeAttributeOids(indexRelation, numatts, indexoid); + + /* ---------------- + * append ATTRIBUTE tuples + * ---------------- + */ + AppendAttributeTuples(indexRelation, numatts); + + /* ---------------- + * update pg_index + * (append INDEX tuple) + * + * Note that this stows away a representation of "predicate". + * (Or, could define a rule to maintain the predicate) --Nels, Feb '92 + * ---------------- + */ + UpdateIndexRelation(indexoid, heapoid, funcInfo, + numatts, attNums, classObjectId, predicate, + attributeList, islossy, unique); + + predInfo = (PredInfo *) palloc(sizeof(PredInfo)); + predInfo->pred = predicate; + predInfo->oldPred = NULL; + + /* ---------------- + * initialize the index strategy + * ---------------- + */ + InitIndexStrategy(numatts, indexRelation, accessMethodObjectId); + + /* + * If this is bootstrap (initdb) time, then we don't actually fill in + * the index yet. We'll be creating more indices and classes later, + * so we delay filling them in until just before we're done with + * bootstrapping. Otherwise, we call the routine that constructs the + * index. The heap and index relations are closed by index_build(). + */ + if (IsBootstrapProcessingMode()) + { + index_register(heapRelationName, indexRelationName, numatts, attNums, + parameterCount, parameter, funcInfo, predInfo); + } + else + { + heapRelation = heap_openr(heapRelationName); + index_build(heapRelation, indexRelation, numatts, attNums, + parameterCount, parameter, funcInfo, predInfo); } - - /* ---------------- - * now update the object id's of all the attribute - * tuple forms in the index relation's tuple descriptor - * ---------------- - */ - InitializeAttributeOids(indexRelation, numatts, indexoid); - - /* ---------------- - * append ATTRIBUTE tuples - * ---------------- - */ - AppendAttributeTuples(indexRelation, numatts); - - /* ---------------- - * update pg_index - * (append INDEX tuple) - * - * Note that this stows away a representation of "predicate". - * (Or, could define a rule to maintain the predicate) --Nels, Feb '92 - * ---------------- - */ - UpdateIndexRelation(indexoid, heapoid, funcInfo, - numatts, attNums, classObjectId, predicate, - attributeList, islossy, unique); - - predInfo = (PredInfo*)palloc(sizeof(PredInfo)); - predInfo->pred = predicate; - predInfo->oldPred = NULL; - - /* ---------------- - * initialize the index strategy - * ---------------- - */ - InitIndexStrategy(numatts, indexRelation, accessMethodObjectId); - - /* - * If this is bootstrap (initdb) time, then we don't actually - * fill in the index yet. We'll be creating more indices and classes - * later, so we delay filling them in until just before we're done - * with bootstrapping. Otherwise, we call the routine that constructs - * the index. The heap and index relations are closed by index_build(). - */ - if (IsBootstrapProcessingMode()) { - index_register(heapRelationName, indexRelationName, numatts, attNums, - parameterCount, parameter, funcInfo, predInfo); - } else { - heapRelation = heap_openr(heapRelationName); - index_build(heapRelation, indexRelation, numatts, attNums, - parameterCount, parameter, funcInfo, predInfo); - } } /* ---------------------------------------------------------------- - * index_destroy + * index_destroy * - * XXX break into modules like index_create + * XXX break into modules like index_create * ---------------------------------------------------------------- */ void index_destroy(Oid indexId) { - Relation indexRelation; - Relation catalogRelation; - HeapTuple tuple; - HeapScanDesc scan; - ScanKeyData entry; - - Assert(OidIsValid(indexId)); - - indexRelation = index_open(indexId); - - /* ---------------- - * fix RELATION relation - * ---------------- - */ - catalogRelation = heap_openr(RelationRelationName); - - ScanKeyEntryInitialize(&entry, 0x0, ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(indexId));; - - scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry); - tuple = heap_getnext(scan, 0, (Buffer *)NULL); - - AssertState(HeapTupleIsValid(tuple)); - - heap_delete(catalogRelation, &tuple->t_ctid); - heap_endscan(scan); - heap_close(catalogRelation); - - /* ---------------- - * fix ATTRIBUTE relation - * ---------------- - */ - catalogRelation = heap_openr(AttributeRelationName); - - entry.sk_attno = Anum_pg_attribute_attrelid; - - scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry); - - while (tuple = heap_getnext(scan, 0, (Buffer *)NULL), - HeapTupleIsValid(tuple)) { - + Relation indexRelation; + Relation catalogRelation; + HeapTuple tuple; + HeapScanDesc scan; + ScanKeyData entry; + + Assert(OidIsValid(indexId)); + + indexRelation = index_open(indexId); + + /* ---------------- + * fix RELATION relation + * ---------------- + */ + catalogRelation = heap_openr(RelationRelationName); + + ScanKeyEntryInitialize(&entry, 0x0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(indexId));; + + scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry); + tuple = heap_getnext(scan, 0, (Buffer *) NULL); + + AssertState(HeapTupleIsValid(tuple)); + + heap_delete(catalogRelation, &tuple->t_ctid); + heap_endscan(scan); + heap_close(catalogRelation); + + /* ---------------- + * fix ATTRIBUTE relation + * ---------------- + */ + catalogRelation = heap_openr(AttributeRelationName); + + entry.sk_attno = Anum_pg_attribute_attrelid; + + scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry); + + while (tuple = heap_getnext(scan, 0, (Buffer *) NULL), + HeapTupleIsValid(tuple)) + { + + heap_delete(catalogRelation, &tuple->t_ctid); + } + heap_endscan(scan); + heap_close(catalogRelation); + + /* ---------------- + * fix INDEX relation + * ---------------- + */ + catalogRelation = heap_openr(IndexRelationName); + + entry.sk_attno = Anum_pg_index_indexrelid; + + scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry); + tuple = heap_getnext(scan, 0, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(NOTICE, "IndexRelationDestroy: %s's INDEX tuple missing", + RelationGetRelationName(indexRelation)); + } heap_delete(catalogRelation, &tuple->t_ctid); - } - heap_endscan(scan); - heap_close(catalogRelation); - - /* ---------------- - * fix INDEX relation - * ---------------- - */ - catalogRelation = heap_openr(IndexRelationName); - - entry.sk_attno = Anum_pg_index_indexrelid; - - scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry); - tuple = heap_getnext(scan, 0, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(NOTICE, "IndexRelationDestroy: %s's INDEX tuple missing", - RelationGetRelationName(indexRelation)); - } - heap_delete(catalogRelation, &tuple->t_ctid); - heap_endscan(scan); - heap_close(catalogRelation); - - /* - * physically remove the file - */ - if (FileNameUnlink(relpath(indexRelation->rd_rel->relname.data)) < 0) - elog(WARN, "amdestroyr: unlink: %m"); - - index_close(indexRelation); + heap_endscan(scan); + heap_close(catalogRelation); + + /* + * physically remove the file + */ + if (FileNameUnlink(relpath(indexRelation->rd_rel->relname.data)) < 0) + elog(WARN, "amdestroyr: unlink: %m"); + + index_close(indexRelation); } /* ---------------------------------------------------------------- - * index_build support + * index_build support * ---------------------------------------------------------------- */ /* ---------------- - * FormIndexDatum + * FormIndexDatum * ---------------- */ void FormIndexDatum(int numberOfAttributes, - AttrNumber attributeNumber[], - HeapTuple heapTuple, - TupleDesc heapDescriptor, - Buffer buffer, - Datum *datum, - char *nullv, - FuncIndexInfoPtr fInfo) + AttrNumber attributeNumber[], + HeapTuple heapTuple, + TupleDesc heapDescriptor, + Buffer buffer, + Datum * datum, + char *nullv, + FuncIndexInfoPtr fInfo) { - AttrNumber i; - int offset; - bool isNull; - - /* ---------------- - * for each attribute we need from the heap tuple, - * get the attribute and stick it into the datum and - * null arrays. - * ---------------- - */ - - for (i = 1; i <= numberOfAttributes; i += 1) { - offset = AttrNumberGetAttrOffset(i); - - datum[ offset ] = - PointerGetDatum( GetIndexValue(heapTuple, - heapDescriptor, - offset, - attributeNumber, - fInfo, - &isNull, - buffer) ); - - nullv[ offset ] = (isNull) ? 'n' : ' '; - } + AttrNumber i; + int offset; + bool isNull; + + /* ---------------- + * for each attribute we need from the heap tuple, + * get the attribute and stick it into the datum and + * null arrays. + * ---------------- + */ + + for (i = 1; i <= numberOfAttributes; i += 1) + { + offset = AttrNumberGetAttrOffset(i); + + datum[offset] = + PointerGetDatum(GetIndexValue(heapTuple, + heapDescriptor, + offset, + attributeNumber, + fInfo, + &isNull, + buffer)); + + nullv[offset] = (isNull) ? 'n' : ' '; + } } /* ---------------- - * UpdateStats + * UpdateStats * ---------------- */ void UpdateStats(Oid relid, long reltuples, bool hasindex) { - Relation whichRel; - Relation pg_class; - HeapScanDesc pg_class_scan; - HeapTuple htup; - HeapTuple newtup; - long relpages; - Buffer buffer; - int i; - Form_pg_class rd_rel; - Relation idescs[Num_pg_class_indices]; - - static ScanKeyData key[1] = { - { 0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure } - }; - Datum values[Natts_pg_class]; - char nulls[Natts_pg_class]; - char replace[Natts_pg_class]; - - fmgr_info(ObjectIdEqualRegProcedure, (func_ptr *) &key[0].sk_func, - &key[0].sk_nargs); - - /* ---------------- - * This routine handles updates for both the heap and index relation - * statistics. In order to guarantee that we're able to *see* the index - * relation tuple, we bump the command counter id here. The index - * relation tuple was created in the current transaction. - * ---------------- - */ - CommandCounterIncrement(); - - /* ---------------- - * CommandCounterIncrement() flushes invalid cache entries, including - * those for the heap and index relations for which we're updating - * statistics. Now that the cache is flushed, it's safe to open the - * relation again. We need the relation open in order to figure out - * how many blocks it contains. - * ---------------- - */ - - whichRel = RelationIdGetRelation(relid); - - if (!RelationIsValid(whichRel)) - elog(WARN, "UpdateStats: cannot open relation id %d", relid); - - /* ---------------- - * Find the RELATION relation tuple for the given relation. - * ---------------- - */ - pg_class = heap_openr(RelationRelationName); - if (! RelationIsValid(pg_class)) { - elog(WARN, "UpdateStats: could not open RELATION relation"); - } - key[0].sk_argument = ObjectIdGetDatum(relid); - - pg_class_scan = - heap_beginscan(pg_class, 0, NowTimeQual, 1, key); - - if (! HeapScanIsValid(pg_class_scan)) { - heap_close(pg_class); - elog(WARN, "UpdateStats: cannot scan RELATION relation"); - } - - /* if the heap_open above succeeded, then so will this heap_getnext() */ - htup = heap_getnext(pg_class_scan, 0, &buffer); - heap_endscan(pg_class_scan); - - /* ---------------- - * update statistics - * ---------------- - */ - relpages = RelationGetNumberOfBlocks(whichRel); - - /* - * We shouldn't have to do this, but we do... Modify the reldesc - * in place with the new values so that the cache contains the - * latest copy. - */ - - whichRel->rd_rel->relhasindex = hasindex; - whichRel->rd_rel->relpages = relpages; - whichRel->rd_rel->reltuples = reltuples; - - for (i = 0; i < Natts_pg_class; i++) { - nulls[i] = heap_attisnull(htup, i+1) ? 'n' : ' '; - replace[i] = ' '; - values[i] = (Datum) NULL; - } - - /* - * If reltuples wasn't supplied take an educated guess. - */ - if (reltuples == 0) - reltuples = relpages*NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts); - - if (IsBootstrapProcessingMode()) { - + Relation whichRel; + Relation pg_class; + HeapScanDesc pg_class_scan; + HeapTuple htup; + HeapTuple newtup; + long relpages; + Buffer buffer; + int i; + Form_pg_class rd_rel; + Relation idescs[Num_pg_class_indices]; + + static ScanKeyData key[1] = { + {0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure} + }; + Datum values[Natts_pg_class]; + char nulls[Natts_pg_class]; + char replace[Natts_pg_class]; + + fmgr_info(ObjectIdEqualRegProcedure, (func_ptr *) & key[0].sk_func, + &key[0].sk_nargs); + + /* ---------------- + * This routine handles updates for both the heap and index relation + * statistics. In order to guarantee that we're able to *see* the index + * relation tuple, we bump the command counter id here. The index + * relation tuple was created in the current transaction. + * ---------------- + */ + CommandCounterIncrement(); + + /* ---------------- + * CommandCounterIncrement() flushes invalid cache entries, including + * those for the heap and index relations for which we're updating + * statistics. Now that the cache is flushed, it's safe to open the + * relation again. We need the relation open in order to figure out + * how many blocks it contains. + * ---------------- + */ + + whichRel = RelationIdGetRelation(relid); + + if (!RelationIsValid(whichRel)) + elog(WARN, "UpdateStats: cannot open relation id %d", relid); + + /* ---------------- + * Find the RELATION relation tuple for the given relation. + * ---------------- + */ + pg_class = heap_openr(RelationRelationName); + if (!RelationIsValid(pg_class)) + { + elog(WARN, "UpdateStats: could not open RELATION relation"); + } + key[0].sk_argument = ObjectIdGetDatum(relid); + + pg_class_scan = + heap_beginscan(pg_class, 0, NowTimeQual, 1, key); + + if (!HeapScanIsValid(pg_class_scan)) + { + heap_close(pg_class); + elog(WARN, "UpdateStats: cannot scan RELATION relation"); + } + + /* if the heap_open above succeeded, then so will this heap_getnext() */ + htup = heap_getnext(pg_class_scan, 0, &buffer); + heap_endscan(pg_class_scan); + + /* ---------------- + * update statistics + * ---------------- + */ + relpages = RelationGetNumberOfBlocks(whichRel); + + /* + * We shouldn't have to do this, but we do... Modify the reldesc in + * place with the new values so that the cache contains the latest + * copy. + */ + + whichRel->rd_rel->relhasindex = hasindex; + whichRel->rd_rel->relpages = relpages; + whichRel->rd_rel->reltuples = reltuples; + + for (i = 0; i < Natts_pg_class; i++) + { + nulls[i] = heap_attisnull(htup, i + 1) ? 'n' : ' '; + replace[i] = ' '; + values[i] = (Datum) NULL; + } + /* - * At bootstrap time, we don't need to worry about concurrency - * or visibility of changes, so we cheat. - */ - - rd_rel = (Form_pg_class) GETSTRUCT(htup); - rd_rel->relpages = relpages; - rd_rel->reltuples = reltuples; - rd_rel->relhasindex = hasindex; - } else { - /* during normal processing, work harder */ - replace[Anum_pg_class_relpages - 1] = 'r'; - values[Anum_pg_class_relpages - 1] = (Datum)relpages; - replace[Anum_pg_class_reltuples - 1] = 'r'; - values[Anum_pg_class_reltuples - 1] = (Datum)reltuples; - replace[Anum_pg_class_relhasindex - 1] = 'r'; - values[Anum_pg_class_relhasindex - 1] = CharGetDatum(hasindex); - - newtup = heap_modifytuple(htup, buffer, pg_class, values, - nulls, replace); - heap_replace(pg_class, &(newtup->t_ctid), newtup); - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, newtup); - CatalogCloseIndices(Num_pg_class_indices, idescs); - } - - heap_close(pg_class); - heap_close(whichRel); + * If reltuples wasn't supplied take an educated guess. + */ + if (reltuples == 0) + reltuples = relpages * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts); + + if (IsBootstrapProcessingMode()) + { + + /* + * At bootstrap time, we don't need to worry about concurrency or + * visibility of changes, so we cheat. + */ + + rd_rel = (Form_pg_class) GETSTRUCT(htup); + rd_rel->relpages = relpages; + rd_rel->reltuples = reltuples; + rd_rel->relhasindex = hasindex; + } + else + { + /* during normal processing, work harder */ + replace[Anum_pg_class_relpages - 1] = 'r'; + values[Anum_pg_class_relpages - 1] = (Datum) relpages; + replace[Anum_pg_class_reltuples - 1] = 'r'; + values[Anum_pg_class_reltuples - 1] = (Datum) reltuples; + replace[Anum_pg_class_relhasindex - 1] = 'r'; + values[Anum_pg_class_relhasindex - 1] = CharGetDatum(hasindex); + + newtup = heap_modifytuple(htup, buffer, pg_class, values, + nulls, replace); + heap_replace(pg_class, &(newtup->t_ctid), newtup); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, newtup); + CatalogCloseIndices(Num_pg_class_indices, idescs); + } + + heap_close(pg_class); + heap_close(whichRel); } /* ------------------------- - * FillDummyExprContext - * Sets up dummy ExprContext and TupleTableSlot objects for use - * with ExecQual. + * FillDummyExprContext + * Sets up dummy ExprContext and TupleTableSlot objects for use + * with ExecQual. * ------------------------- */ void -FillDummyExprContext(ExprContext *econtext, - TupleTableSlot *slot, - TupleDesc tupdesc, - Buffer buffer) +FillDummyExprContext(ExprContext * econtext, + TupleTableSlot * slot, + TupleDesc tupdesc, + Buffer buffer) { - econtext->ecxt_scantuple = slot; - econtext->ecxt_innertuple = NULL; - econtext->ecxt_outertuple = NULL; - econtext->ecxt_param_list_info = NULL; - econtext->ecxt_range_table = NULL; - - slot->ttc_tupleDescriptor = tupdesc; - slot->ttc_buffer = buffer; - slot->ttc_shouldFree = false; + econtext->ecxt_scantuple = slot; + econtext->ecxt_innertuple = NULL; + econtext->ecxt_outertuple = NULL; + econtext->ecxt_param_list_info = NULL; + econtext->ecxt_range_table = NULL; + + slot->ttc_tupleDescriptor = tupdesc; + slot->ttc_buffer = buffer; + slot->ttc_shouldFree = false; } /* ---------------- - * DefaultBuild + * DefaultBuild * ---------------- */ static void DefaultBuild(Relation heapRelation, - Relation indexRelation, - int numberOfAttributes, - AttrNumber attributeNumber[], - IndexStrategy indexStrategy, /* not used */ - uint16 parameterCount, /* not used */ - Datum parameter[], /* not used */ - FuncIndexInfoPtr funcInfo, - PredInfo *predInfo) + Relation indexRelation, + int numberOfAttributes, + AttrNumber attributeNumber[], + IndexStrategy indexStrategy, /* not used */ + uint16 parameterCount, /* not used */ + Datum parameter[], /* not used */ + FuncIndexInfoPtr funcInfo, + PredInfo * predInfo) { - HeapScanDesc scan; - HeapTuple heapTuple; - Buffer buffer; - - IndexTuple indexTuple; - TupleDesc heapDescriptor; - TupleDesc indexDescriptor; - Datum *datum; - char *nullv; - long reltuples, indtuples; + HeapScanDesc scan; + HeapTuple heapTuple; + Buffer buffer; + + IndexTuple indexTuple; + TupleDesc heapDescriptor; + TupleDesc indexDescriptor; + Datum *datum; + char *nullv; + long reltuples, + indtuples; + #ifndef OMIT_PARTIAL_INDEX - ExprContext *econtext; - TupleTable tupleTable; - TupleTableSlot *slot; + ExprContext *econtext; + TupleTable tupleTable; + TupleTableSlot *slot; + #endif - Node *predicate; - Node *oldPred; - - InsertIndexResult insertResult; - - /* ---------------- - * more & better checking is needed - * ---------------- - */ - Assert(OidIsValid(indexRelation->rd_rel->relam)); /* XXX */ - - /* ---------------- - * get the tuple descriptors from the relations so we know - * how to form the index tuples.. - * ---------------- - */ - heapDescriptor = RelationGetTupleDescriptor(heapRelation); - indexDescriptor = RelationGetTupleDescriptor(indexRelation); - - /* ---------------- - * datum and null are arrays in which we collect the index attributes - * when forming a new index tuple. - * ---------------- - */ - datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); - nullv = (char *) palloc(numberOfAttributes * sizeof *nullv); - - /* - * 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 - */ - - predicate = predInfo->pred; - oldPred = predInfo->oldPred; + Node *predicate; + Node *oldPred; + + InsertIndexResult insertResult; + + /* ---------------- + * more & better checking is needed + * ---------------- + */ + Assert(OidIsValid(indexRelation->rd_rel->relam)); /* XXX */ + + /* ---------------- + * get the tuple descriptors from the relations so we know + * how to form the index tuples.. + * ---------------- + */ + heapDescriptor = RelationGetTupleDescriptor(heapRelation); + indexDescriptor = RelationGetTupleDescriptor(indexRelation); + + /* ---------------- + * datum and null are arrays in which we collect the index attributes + * when forming a new index tuple. + * ---------------- + */ + datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); + nullv = (char *) palloc(numberOfAttributes * sizeof *nullv); + + /* + * 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 + */ + + predicate = predInfo->pred; + oldPred = predInfo->oldPred; #ifndef OMIT_PARTIAL_INDEX - if (predicate != NULL || oldPred != NULL) { - tupleTable = ExecCreateTupleTable(1); - slot = ExecAllocTableSlot(tupleTable); - econtext = makeNode(ExprContext); - FillDummyExprContext(econtext, slot, heapDescriptor, buffer); - } + if (predicate != NULL || oldPred != NULL) + { + tupleTable = ExecCreateTupleTable(1); + slot = ExecAllocTableSlot(tupleTable); + econtext = makeNode(ExprContext); + FillDummyExprContext(econtext, slot, heapDescriptor, buffer); + } else { econtext = NULL; tupleTable = 0; slot = NULL; } -#endif /* OMIT_PARTIAL_INDEX */ - - /* ---------------- - * Ok, begin our scan of the base relation. - * ---------------- - */ - scan = heap_beginscan(heapRelation, /* relation */ - 0, /* start at end */ - NowTimeQual, /* time range */ - 0, /* number of keys */ - (ScanKey) NULL); /* scan key */ - - reltuples = indtuples = 0; - - /* ---------------- - * for each tuple in the base relation, we create an index - * tuple and add it to the index relation. We keep a running - * count of the number of tuples so that we can update pg_class - * with correct statistics when we're done building the index. - * ---------------- - */ - while (heapTuple = heap_getnext(scan, 0, &buffer), - HeapTupleIsValid(heapTuple)) { - - reltuples++; - - /* - * If oldPred != NULL, this is an EXTEND INDEX command, so skip - * this tuple if it was already in the existing partial index +#endif /* OMIT_PARTIAL_INDEX */ + + /* ---------------- + * Ok, begin our scan of the base relation. + * ---------------- */ - if (oldPred != NULL) { + scan = heap_beginscan(heapRelation, /* relation */ + 0, /* start at end */ + NowTimeQual, /* time range */ + 0, /* number of keys */ + (ScanKey) NULL); /* scan key */ + + reltuples = indtuples = 0; + + /* ---------------- + * for each tuple in the base relation, we create an index + * tuple and add it to the index relation. We keep a running + * count of the number of tuples so that we can update pg_class + * with correct statistics when we're done building the index. + * ---------------- + */ + while (heapTuple = heap_getnext(scan, 0, &buffer), + HeapTupleIsValid(heapTuple)) + { + + reltuples++; + + /* + * 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, heapTuple); */ - slot->val = heapTuple; - if (ExecQual((List*)oldPred, econtext) == true) { + /* SetSlotContents(slot, heapTuple); */ + slot->val = heapTuple; + if (ExecQual((List *) oldPred, econtext) == true) + { + indtuples++; + continue; + } +#endif /* OMIT_PARTIAL_INDEX */ + } + + /* + * Skip this tuple if it doesn't satisfy the partial-index + * predicate + */ + if (predicate != NULL) + { +#ifndef OMIT_PARTIAL_INDEX + /* SetSlotContents(slot, heapTuple); */ + slot->val = heapTuple; + if (ExecQual((List *) predicate, econtext) == false) + continue; +#endif /* OMIT_PARTIAL_INDEX */ + } + indtuples++; - continue; - } -#endif /* OMIT_PARTIAL_INDEX */ + + /* ---------------- + * FormIndexDatum fills in its datum and null parameters + * with attribute information taken from the given heap tuple. + * ---------------- + */ + FormIndexDatum(numberOfAttributes, /* num attributes */ + attributeNumber, /* array of att nums to extract */ + heapTuple, /* tuple from base relation */ + heapDescriptor, /* heap tuple's descriptor */ + buffer, /* buffer used in the scan */ + datum, /* return: array of attributes */ + nullv, /* return: array of char's */ + funcInfo); + + indexTuple = index_formtuple(indexDescriptor, + datum, + nullv); + + indexTuple->t_tid = heapTuple->t_ctid; + + insertResult = index_insert(indexRelation, datum, nullv, + &(heapTuple->t_ctid), heapRelation); + + if (insertResult) + pfree(insertResult); + pfree(indexTuple); } - - /* Skip this tuple if it doesn't satisfy the partial-index predicate */ - if (predicate != NULL) { + + heap_endscan(scan); + + if (predicate != NULL || oldPred != NULL) + { #ifndef OMIT_PARTIAL_INDEX - /*SetSlotContents(slot, heapTuple); */ - slot->val = heapTuple; - if (ExecQual((List*)predicate, econtext) == false) - continue; -#endif /* OMIT_PARTIAL_INDEX */ + ExecDestroyTupleTable(tupleTable, false); +#endif /* OMIT_PARTIAL_INDEX */ } - - indtuples++; - - /* ---------------- - * FormIndexDatum fills in its datum and null parameters - * with attribute information taken from the given heap tuple. - * ---------------- + + pfree(nullv); + pfree(datum); + + /* + * Okay, now update the reltuples and relpages statistics for both the + * heap relation and the index. These statistics are used by the + * planner to choose a scan type. They are maintained generally by + * the vacuum daemon, but we update them here to make the index useful + * as soon as possible. */ - FormIndexDatum(numberOfAttributes, /* num attributes */ - attributeNumber, /* array of att nums to extract */ - heapTuple, /* tuple from base relation */ - heapDescriptor, /* heap tuple's descriptor */ - buffer, /* buffer used in the scan */ - datum, /* return: array of attributes */ - nullv, /* return: array of char's */ - funcInfo); - - indexTuple = index_formtuple(indexDescriptor, - datum, - nullv); - - indexTuple->t_tid = heapTuple->t_ctid; - - insertResult = index_insert(indexRelation, datum, nullv, - &(heapTuple->t_ctid), heapRelation); - - if (insertResult) pfree(insertResult); - pfree(indexTuple); - } - - heap_endscan(scan); - - if (predicate != NULL || oldPred != NULL) { -#ifndef OMIT_PARTIAL_INDEX - ExecDestroyTupleTable(tupleTable, false); -#endif /* OMIT_PARTIAL_INDEX */ - } - - pfree(nullv); - pfree(datum); - - /* - * Okay, now update the reltuples and relpages statistics for both - * the heap relation and the index. These statistics are used by - * the planner to choose a scan type. They are maintained generally - * by the vacuum daemon, but we update them here to make the index - * useful as soon as possible. - */ - UpdateStats(heapRelation->rd_id, reltuples, true); - UpdateStats(indexRelation->rd_id, indtuples, false); - if (oldPred != NULL) { - if (indtuples == reltuples) predicate = NULL; - UpdateIndexPredicate(indexRelation->rd_id, oldPred, predicate); - } + UpdateStats(heapRelation->rd_id, reltuples, true); + UpdateStats(indexRelation->rd_id, indtuples, false); + if (oldPred != NULL) + { + if (indtuples == reltuples) + predicate = NULL; + UpdateIndexPredicate(indexRelation->rd_id, oldPred, predicate); + } } /* ---------------- - * index_build + * index_build * ---------------- */ void index_build(Relation heapRelation, - Relation indexRelation, - int numberOfAttributes, - AttrNumber attributeNumber[], - uint16 parameterCount, - Datum *parameter, - FuncIndexInfo *funcInfo, - PredInfo *predInfo) + Relation indexRelation, + int numberOfAttributes, + AttrNumber attributeNumber[], + uint16 parameterCount, + Datum * parameter, + FuncIndexInfo * funcInfo, + PredInfo * predInfo) { - RegProcedure procedure; - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(RelationIsValid(indexRelation)); - Assert(PointerIsValid(indexRelation->rd_am)); - - procedure = indexRelation->rd_am->ambuild; - - /* ---------------- - * use the access method build procedure if supplied.. - * ---------------- - */ - if (RegProcedureIsValid(procedure)) - fmgr(procedure, - heapRelation, - indexRelation, - numberOfAttributes, - attributeNumber, - RelationGetIndexStrategy(indexRelation), - parameterCount, - parameter, - funcInfo, - predInfo); - else - DefaultBuild(heapRelation, - indexRelation, - numberOfAttributes, - attributeNumber, - RelationGetIndexStrategy(indexRelation), - parameterCount, - parameter, - funcInfo, - predInfo); + RegProcedure procedure; + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(RelationIsValid(indexRelation)); + Assert(PointerIsValid(indexRelation->rd_am)); + + procedure = indexRelation->rd_am->ambuild; + + /* ---------------- + * use the access method build procedure if supplied.. + * ---------------- + */ + if (RegProcedureIsValid(procedure)) + fmgr(procedure, + heapRelation, + indexRelation, + numberOfAttributes, + attributeNumber, + RelationGetIndexStrategy(indexRelation), + parameterCount, + parameter, + funcInfo, + predInfo); + else + DefaultBuild(heapRelation, + indexRelation, + numberOfAttributes, + attributeNumber, + RelationGetIndexStrategy(indexRelation), + parameterCount, + parameter, + funcInfo, + predInfo); } /* @@ -1712,20 +1781,21 @@ index_build(Relation heapRelation, bool IndexIsUnique(Oid indexId) { - HeapTuple tuple; - IndexTupleForm index; - - tuple = SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(indexId), - 0,0,0); - if(!HeapTupleIsValid(tuple)) { - elog(WARN, "IndexIsUnique: can't find index id %d", - indexId); - } - index = (IndexTupleForm)GETSTRUCT(tuple); - Assert(index->indexrelid == indexId); - - return index->indisunique; + HeapTuple tuple; + IndexTupleForm index; + + tuple = SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(indexId), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "IndexIsUnique: can't find index id %d", + indexId); + } + index = (IndexTupleForm) GETSTRUCT(tuple); + Assert(index->indexrelid == indexId); + + return index->indisunique; } /* @@ -1743,32 +1813,33 @@ IndexIsUnique(Oid indexId) bool IndexIsUniqueNoCache(Oid indexId) { - Relation pg_index; - ScanKeyData skey[1]; - HeapScanDesc scandesc; - HeapTuple tuple; - IndexTupleForm index; - bool isunique; - - pg_index = heap_openr(IndexRelationName); - - ScanKeyEntryInitialize(&skey[0], (bits16)0x0, - Anum_pg_index_indexrelid, - (RegProcedure)ObjectIdEqualRegProcedure, - ObjectIdGetDatum(indexId)); - - scandesc = heap_beginscan(pg_index, 0, SelfTimeQual, 1, skey); - - tuple = heap_getnext(scandesc, 0, NULL); - if(!HeapTupleIsValid(tuple)) { - elog(WARN, "IndexIsUniqueNoCache: can't find index id %d", - indexId); - } - index = (IndexTupleForm)GETSTRUCT(tuple); - Assert(index->indexrelid == indexId); - isunique = index->indisunique; - - heap_endscan (scandesc); - heap_close (pg_index); - return isunique; + Relation pg_index; + ScanKeyData skey[1]; + HeapScanDesc scandesc; + HeapTuple tuple; + IndexTupleForm index; + bool isunique; + + pg_index = heap_openr(IndexRelationName); + + ScanKeyEntryInitialize(&skey[0], (bits16) 0x0, + Anum_pg_index_indexrelid, + (RegProcedure) ObjectIdEqualRegProcedure, + ObjectIdGetDatum(indexId)); + + scandesc = heap_beginscan(pg_index, 0, SelfTimeQual, 1, skey); + + tuple = heap_getnext(scandesc, 0, NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "IndexIsUniqueNoCache: can't find index id %d", + indexId); + } + index = (IndexTupleForm) GETSTRUCT(tuple); + Assert(index->indexrelid == indexId); + isunique = index->indisunique; + + heap_endscan(scandesc); + heap_close(pg_index); + return isunique; } diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 2d769d58615..6a89258d972 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * indexing.c-- - * This file contains routines to support indices defined on system - * catalogs. + * This file contains routines to support indices defined on system + * catalogs. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.11 1997/08/31 09:56:18 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.12 1997/09/07 04:40:21 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,36 +34,37 @@ /* * Names of indices on the following system catalogs: * - * pg_attribute - * pg_proc - * pg_type - * pg_naming - * pg_class - * pg_attrdef - * pg_relcheck - * pg_trigger + * pg_attribute + * pg_proc + * pg_type + * pg_naming + * pg_class + * pg_attrdef + * pg_relcheck + * pg_trigger */ -char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndex, - AttributeNumIndex, - AttributeRelidIndex}; -char *Name_pg_proc_indices[Num_pg_proc_indices] = { ProcedureNameIndex, - ProcedureOidIndex, - ProcedureSrcIndex}; -char *Name_pg_type_indices[Num_pg_type_indices] = { TypeNameIndex, - TypeOidIndex}; -char *Name_pg_class_indices[Num_pg_class_indices]= { ClassNameIndex, - ClassOidIndex}; -char *Name_pg_attrdef_indices[Num_pg_attrdef_indices]= { AttrDefaultIndex }; +char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndex, + AttributeNumIndex, +AttributeRelidIndex}; +char *Name_pg_proc_indices[Num_pg_proc_indices] = {ProcedureNameIndex, + ProcedureOidIndex, +ProcedureSrcIndex}; +char *Name_pg_type_indices[Num_pg_type_indices] = {TypeNameIndex, +TypeOidIndex}; +char *Name_pg_class_indices[Num_pg_class_indices] = {ClassNameIndex, +ClassOidIndex}; +char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] = {AttrDefaultIndex}; -char *Name_pg_relcheck_indices[Num_pg_relcheck_indices]= { RelCheckIndex }; +char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] = {RelCheckIndex}; -char *Name_pg_trigger_indices[Num_pg_trigger_indices]= { TriggerRelidIndex }; +char *Name_pg_trigger_indices[Num_pg_trigger_indices] = {TriggerRelidIndex}; -static HeapTuple CatalogIndexFetchTuple(Relation heapRelation, - Relation idesc, - ScanKey skey); +static HeapTuple +CatalogIndexFetchTuple(Relation heapRelation, + Relation idesc, + ScanKey skey); /* @@ -75,11 +76,11 @@ static HeapTuple CatalogIndexFetchTuple(Relation heapRelation, void CatalogOpenIndices(int nIndices, char *names[], Relation idescs[]) { - int i; - - for (i=0; i<nIndices; i++) + int i; + + for (i = 0; i < nIndices; i++) { - idescs[i] = index_openr(names[i]); + idescs[i] = index_openr(names[i]); } } @@ -87,83 +88,84 @@ CatalogOpenIndices(int nIndices, char *names[], Relation idescs[]) * This is the inverse routine to CatalogOpenIndices() */ void -CatalogCloseIndices(int nIndices, Relation *idescs) +CatalogCloseIndices(int nIndices, Relation * idescs) { - int i; - - for (i=0; i<nIndices; i++) - index_close(idescs[i]); + int i; + + for (i = 0; i < nIndices; i++) + index_close(idescs[i]); } /* * For the same reasons outlined above CatalogOpenIndices() we need a routine - * that takes a new catalog tuple and inserts an associated index tuple into + * that takes a new catalog tuple and inserts an associated index tuple into * each catalog index. */ void -CatalogIndexInsert(Relation *idescs, - int nIndices, - Relation heapRelation, - HeapTuple heapTuple) +CatalogIndexInsert(Relation * idescs, + int nIndices, + Relation heapRelation, + HeapTuple heapTuple) { - HeapTuple pgIndexTup; - TupleDesc heapDescriptor; - IndexTupleForm pgIndexP; - Datum datum; - int natts; - AttrNumber *attnumP; - FuncIndexInfo finfo, *finfoP; - char nulls[INDEX_MAX_KEYS]; - int i; - - heapDescriptor = RelationGetTupleDescriptor(heapRelation); - - for (i=0; i<nIndices; i++) + HeapTuple pgIndexTup; + TupleDesc heapDescriptor; + IndexTupleForm pgIndexP; + Datum datum; + int natts; + AttrNumber *attnumP; + FuncIndexInfo finfo, + *finfoP; + char nulls[INDEX_MAX_KEYS]; + int i; + + heapDescriptor = RelationGetTupleDescriptor(heapRelation); + + for (i = 0; i < nIndices; i++) { - TupleDesc indexDescriptor; - InsertIndexResult indexRes; - - indexDescriptor = RelationGetTupleDescriptor(idescs[i]); - pgIndexTup = SearchSysCacheTuple(INDEXRELID, - Int32GetDatum(idescs[i]->rd_id), - 0,0,0); - Assert(pgIndexTup); - pgIndexP = (IndexTupleForm)GETSTRUCT(pgIndexTup); - - /* - * Compute the number of attributes we are indexing upon. - * very important - can't assume one if this is a functional - * index. - */ - for (attnumP=(&pgIndexP->indkey[0]), natts=0; - *attnumP != InvalidAttrNumber; - attnumP++, natts++) - ; - - if (pgIndexP->indproc != InvalidOid) + TupleDesc indexDescriptor; + InsertIndexResult indexRes; + + indexDescriptor = RelationGetTupleDescriptor(idescs[i]); + pgIndexTup = SearchSysCacheTuple(INDEXRELID, + Int32GetDatum(idescs[i]->rd_id), + 0, 0, 0); + Assert(pgIndexTup); + pgIndexP = (IndexTupleForm) GETSTRUCT(pgIndexTup); + + /* + * Compute the number of attributes we are indexing upon. very + * important - can't assume one if this is a functional index. + */ + for (attnumP = (&pgIndexP->indkey[0]), natts = 0; + *attnumP != InvalidAttrNumber; + attnumP++, natts++) + ; + + if (pgIndexP->indproc != InvalidOid) { - FIgetnArgs(&finfo) = natts; - natts = 1; - FIgetProcOid(&finfo) = pgIndexP->indproc; - *(FIgetname(&finfo)) = '\0'; - finfoP = &finfo; + FIgetnArgs(&finfo) = natts; + natts = 1; + FIgetProcOid(&finfo) = pgIndexP->indproc; + *(FIgetname(&finfo)) = '\0'; + finfoP = &finfo; } - else - finfoP = (FuncIndexInfo *)NULL; - - FormIndexDatum(natts, - (AttrNumber *)&pgIndexP->indkey[0], - heapTuple, - heapDescriptor, - InvalidBuffer, - &datum, - nulls, - finfoP); - - indexRes = index_insert(idescs[i], &datum, nulls, - &(heapTuple->t_ctid), heapRelation); - if (indexRes) pfree(indexRes); + else + finfoP = (FuncIndexInfo *) NULL; + + FormIndexDatum(natts, + (AttrNumber *) & pgIndexP->indkey[0], + heapTuple, + heapDescriptor, + InvalidBuffer, + &datum, + nulls, + finfoP); + + indexRes = index_insert(idescs[i], &datum, nulls, + &(heapTuple->t_ctid), heapRelation); + if (indexRes) + pfree(indexRes); } } @@ -174,81 +176,88 @@ CatalogIndexInsert(Relation *idescs, bool CatalogHasIndex(char *catName, Oid catId) { - Relation pg_class; - HeapTuple htup; - Form_pg_class pgRelP; - int i; - - Assert(IsSystemRelationName(catName)); - - /* - * If we're bootstraping we don't have pg_class (or any indices). - */ - if (IsBootstrapProcessingMode()) - return false; - - if (IsInitProcessingMode()) { - for (i = 0; IndexedCatalogNames[i] != NULL; i++) { - if ( strcmp(IndexedCatalogNames[i], catName) == 0) - return (true); + Relation pg_class; + HeapTuple htup; + Form_pg_class pgRelP; + int i; + + Assert(IsSystemRelationName(catName)); + + /* + * If we're bootstraping we don't have pg_class (or any indices). + */ + if (IsBootstrapProcessingMode()) + return false; + + if (IsInitProcessingMode()) + { + for (i = 0; IndexedCatalogNames[i] != NULL; i++) + { + if (strcmp(IndexedCatalogNames[i], catName) == 0) + return (true); + } + return (false); } - return (false); - } - - pg_class = heap_openr(RelationRelationName); - htup = ClassOidIndexScan(pg_class, catId); - heap_close(pg_class); - - if (! HeapTupleIsValid(htup)) { - elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId); - return false; - } - - pgRelP = (Form_pg_class)GETSTRUCT(htup); - return (pgRelP->relhasindex); + + pg_class = heap_openr(RelationRelationName); + htup = ClassOidIndexScan(pg_class, catId); + heap_close(pg_class); + + if (!HeapTupleIsValid(htup)) + { + elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId); + return false; + } + + pgRelP = (Form_pg_class) GETSTRUCT(htup); + return (pgRelP->relhasindex); } /* - * CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key - * from a catalog relation. + * CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key + * from a catalog relation. * - * Since the index may contain pointers to dead tuples, we need to - * iterate until we find a tuple that's valid and satisfies the scan - * key. + * Since the index may contain pointers to dead tuples, we need to + * iterate until we find a tuple that's valid and satisfies the scan + * key. */ -static HeapTuple +static HeapTuple CatalogIndexFetchTuple(Relation heapRelation, - Relation idesc, - ScanKey skey) + Relation idesc, + ScanKey skey) { - IndexScanDesc sd; - RetrieveIndexResult indexRes; - HeapTuple tuple; - Buffer buffer; - - sd = index_beginscan(idesc, false, 1, skey); - tuple = (HeapTuple)NULL; - - do { - indexRes = index_getnext(sd, ForwardScanDirection); - if (indexRes) { - ItemPointer iptr; - - iptr = &indexRes->heap_iptr; - tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); - pfree(indexRes); - } else - break; - } while (!HeapTupleIsValid(tuple)); - - if (HeapTupleIsValid(tuple)) { - tuple = heap_copytuple(tuple); - ReleaseBuffer(buffer); - } - - index_endscan(sd); - pfree(sd); - return (tuple); + IndexScanDesc sd; + RetrieveIndexResult indexRes; + HeapTuple tuple; + Buffer buffer; + + sd = index_beginscan(idesc, false, 1, skey); + tuple = (HeapTuple) NULL; + + do + { + indexRes = index_getnext(sd, ForwardScanDirection); + if (indexRes) + { + ItemPointer iptr; + + iptr = &indexRes->heap_iptr; + tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); + pfree(indexRes); + } + else + break; + } while (!HeapTupleIsValid(tuple)); + + if (HeapTupleIsValid(tuple)) + { + tuple = heap_copytuple(tuple); + ReleaseBuffer(buffer); + } + + index_endscan(sd); + pfree(sd); + return (tuple); } /* @@ -259,276 +268,295 @@ CatalogIndexFetchTuple(Relation heapRelation, */ HeapTuple AttributeNameIndexScan(Relation heapRelation, - Oid relid, - char *attname) + Oid relid, + char *attname) { - Relation idesc; - ScanKeyData skey; - OidName keyarg; - HeapTuple tuple; - - keyarg = mkoidname(relid, attname); - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)OidNameEqRegProcedure, - (Datum)keyarg); - - idesc = index_openr(AttributeNameIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); - - index_close(idesc); - pfree(keyarg); - - return tuple; + Relation idesc; + ScanKeyData skey; + OidName keyarg; + HeapTuple tuple; + + keyarg = mkoidname(relid, attname); + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) OidNameEqRegProcedure, + (Datum) keyarg); + + idesc = index_openr(AttributeNameIndex); + tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); + + index_close(idesc); + pfree(keyarg); + + return tuple; } HeapTuple AttributeNumIndexScan(Relation heapRelation, - Oid relid, - AttrNumber attnum) + Oid relid, + AttrNumber attnum) { - Relation idesc; - ScanKeyData skey; - OidInt2 keyarg; - HeapTuple tuple; - - keyarg = mkoidint2(relid, (uint16)attnum); - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)OidInt2EqRegProcedure, - (Datum)keyarg); - - idesc = index_openr(AttributeNumIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); - - index_close(idesc); - pfree(keyarg); - - return tuple; + Relation idesc; + ScanKeyData skey; + OidInt2 keyarg; + HeapTuple tuple; + + keyarg = mkoidint2(relid, (uint16) attnum); + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) OidInt2EqRegProcedure, + (Datum) keyarg); + + idesc = index_openr(AttributeNumIndex); + tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); + + index_close(idesc); + pfree(keyarg); + + return tuple; } HeapTuple ProcedureOidIndexScan(Relation heapRelation, Oid procId) { - Relation idesc; - ScanKeyData skey; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)ObjectIdEqualRegProcedure, - (Datum)procId); - - idesc = index_openr(ProcedureOidIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); - - index_close(idesc); - - return tuple; + Relation idesc; + ScanKeyData skey; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) ObjectIdEqualRegProcedure, + (Datum) procId); + + idesc = index_openr(ProcedureOidIndex); + tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); + + index_close(idesc); + + return tuple; } HeapTuple ProcedureNameIndexScan(Relation heapRelation, - char *procName, - int nargs, - Oid *argTypes) + char *procName, + int nargs, + Oid * argTypes) { - Relation idesc; - ScanKeyData skey; - HeapTuple tuple; /* tuple being tested */ - HeapTuple return_tuple; /* The tuple pointer we eventually return */ - IndexScanDesc sd; - RetrieveIndexResult indexRes; - Buffer buffer; - Form_pg_proc pgProcP; - bool ScanComplete; - /* The index scan is complete, i.e. we've scanned everything there - is to scan. - */ - bool FoundMatch; - /* In scanning pg_proc, we have found a row that meets our search - criteria. - */ - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)NameEqualRegProcedure, - (Datum)procName); - - idesc = index_openr(ProcedureNameIndex); - - sd = index_beginscan(idesc, false, 1, &skey); - - /* - * for now, we do the work usually done by CatalogIndexFetchTuple - * by hand, so that we can check that the other keys match. when - * multi-key indices are added, they will be used here. - */ - tuple = (HeapTuple) NULL; /* initial value */ - ScanComplete = false; /* Scan hasn't begun yet */ - FoundMatch = false; /* No match yet; haven't even looked. */ - while (!FoundMatch && !ScanComplete) { - indexRes = index_getnext(sd, ForwardScanDirection); - if (indexRes) { - ItemPointer iptr; - - iptr = &indexRes->heap_iptr; - tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); - pfree(indexRes); - if (HeapTupleIsValid(tuple)) { - /* Here's a row for a procedure that has the sought procedure - name. To be a match, though, we need it to have the - right number and type of arguments too, so we check that - now. - */ - pgProcP = (Form_pg_proc)GETSTRUCT(tuple); - if (pgProcP->pronargs == nargs && - oid8eq(&(pgProcP->proargtypes[0]), argTypes)) - FoundMatch = true; - else ReleaseBuffer(buffer); - } - } else ScanComplete = true; - } - - if (FoundMatch) { - Assert(HeapTupleIsValid(tuple)); - return_tuple = heap_copytuple(tuple); - ReleaseBuffer(buffer); - } else return_tuple = (HeapTuple)NULL; - - index_endscan(sd); - index_close(idesc); - - return return_tuple; + Relation idesc; + ScanKeyData skey; + HeapTuple tuple; /* tuple being tested */ + HeapTuple return_tuple; /* The tuple pointer we eventually + * return */ + IndexScanDesc sd; + RetrieveIndexResult indexRes; + Buffer buffer; + Form_pg_proc pgProcP; + bool ScanComplete; + + /* + * The index scan is complete, i.e. we've scanned everything there is + * to scan. + */ + bool FoundMatch; + + /* + * In scanning pg_proc, we have found a row that meets our search + * criteria. + */ + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) NameEqualRegProcedure, + (Datum) procName); + + idesc = index_openr(ProcedureNameIndex); + + sd = index_beginscan(idesc, false, 1, &skey); + + /* + * for now, we do the work usually done by CatalogIndexFetchTuple by + * hand, so that we can check that the other keys match. when + * multi-key indices are added, they will be used here. + */ + tuple = (HeapTuple) NULL; /* initial value */ + ScanComplete = false; /* Scan hasn't begun yet */ + FoundMatch = false; /* No match yet; haven't even looked. */ + while (!FoundMatch && !ScanComplete) + { + indexRes = index_getnext(sd, ForwardScanDirection); + if (indexRes) + { + ItemPointer iptr; + + iptr = &indexRes->heap_iptr; + tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); + pfree(indexRes); + if (HeapTupleIsValid(tuple)) + { + + /* + * Here's a row for a procedure that has the sought + * procedure name. To be a match, though, we need it to + * have the right number and type of arguments too, so we + * check that now. + */ + pgProcP = (Form_pg_proc) GETSTRUCT(tuple); + if (pgProcP->pronargs == nargs && + oid8eq(&(pgProcP->proargtypes[0]), argTypes)) + FoundMatch = true; + else + ReleaseBuffer(buffer); + } + } + else + ScanComplete = true; + } + + if (FoundMatch) + { + Assert(HeapTupleIsValid(tuple)); + return_tuple = heap_copytuple(tuple); + ReleaseBuffer(buffer); + } + else + return_tuple = (HeapTuple) NULL; + + index_endscan(sd); + index_close(idesc); + + return return_tuple; } HeapTuple -ProcedureSrcIndexScan(Relation heapRelation, text *procSrc) +ProcedureSrcIndexScan(Relation heapRelation, text * procSrc) { - Relation idesc; - IndexScanDesc sd; - ScanKeyData skey; - RetrieveIndexResult indexRes; - HeapTuple tuple; - Buffer buffer; - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)Anum_pg_proc_prosrc, - (RegProcedure)TextEqualRegProcedure, - (Datum)procSrc); - - idesc = index_openr(ProcedureSrcIndex); - sd = index_beginscan(idesc, false, 1, &skey); - - indexRes = index_getnext(sd, ForwardScanDirection); - if (indexRes) { - ItemPointer iptr; - - iptr = &indexRes->heap_iptr; - tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); - pfree(indexRes); - } else - tuple = (HeapTuple)NULL; - - if (HeapTupleIsValid(tuple)) { - tuple = heap_copytuple(tuple); - ReleaseBuffer(buffer); - } - - index_endscan(sd); - - return tuple; + Relation idesc; + IndexScanDesc sd; + ScanKeyData skey; + RetrieveIndexResult indexRes; + HeapTuple tuple; + Buffer buffer; + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) Anum_pg_proc_prosrc, + (RegProcedure) TextEqualRegProcedure, + (Datum) procSrc); + + idesc = index_openr(ProcedureSrcIndex); + sd = index_beginscan(idesc, false, 1, &skey); + + indexRes = index_getnext(sd, ForwardScanDirection); + if (indexRes) + { + ItemPointer iptr; + + iptr = &indexRes->heap_iptr; + tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); + pfree(indexRes); + } + else + tuple = (HeapTuple) NULL; + + if (HeapTupleIsValid(tuple)) + { + tuple = heap_copytuple(tuple); + ReleaseBuffer(buffer); + } + + index_endscan(sd); + + return tuple; } HeapTuple TypeOidIndexScan(Relation heapRelation, Oid typeId) { - Relation idesc; - ScanKeyData skey; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)ObjectIdEqualRegProcedure, - (Datum)typeId); - - idesc = index_openr(TypeOidIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); - - index_close(idesc); - - return tuple; + Relation idesc; + ScanKeyData skey; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) ObjectIdEqualRegProcedure, + (Datum) typeId); + + idesc = index_openr(TypeOidIndex); + tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); + + index_close(idesc); + + return tuple; } HeapTuple TypeNameIndexScan(Relation heapRelation, char *typeName) { - Relation idesc; - ScanKeyData skey; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)NameEqualRegProcedure, - (Datum)typeName); - - idesc = index_openr(TypeNameIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); - - index_close(idesc); - - return tuple; + Relation idesc; + ScanKeyData skey; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) NameEqualRegProcedure, + (Datum) typeName); + + idesc = index_openr(TypeNameIndex); + tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); + + index_close(idesc); + + return tuple; } HeapTuple ClassNameIndexScan(Relation heapRelation, char *relName) { - Relation idesc; - ScanKeyData skey; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)NameEqualRegProcedure, - (Datum)relName); - - idesc = index_openr(ClassNameIndex); - - tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); - - index_close(idesc); - return tuple; + Relation idesc; + ScanKeyData skey; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) NameEqualRegProcedure, + (Datum) relName); + + idesc = index_openr(ClassNameIndex); + + tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); + + index_close(idesc); + return tuple; } HeapTuple ClassOidIndexScan(Relation heapRelation, Oid relId) { - Relation idesc; - ScanKeyData skey; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)ObjectIdEqualRegProcedure, - (Datum)relId); - - idesc = index_openr(ClassOidIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); - - index_close(idesc); - - return tuple; + Relation idesc; + ScanKeyData skey; + HeapTuple tuple; + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) ObjectIdEqualRegProcedure, + (Datum) relId); + + idesc = index_openr(ClassOidIndex); + tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey); + + index_close(idesc); + + return tuple; } diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 13a3c48e9f4..540350bac9b 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * pg_aggregate.c-- - * routines to support manipulation of the pg_aggregate relation + * routines to support manipulation of the pg_aggregate relation * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.5 1997/07/24 20:11:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.6 1997/09/07 04:40:24 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,9 +24,9 @@ #include <catalog/pg_aggregate.h> #include <miscadmin.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* ---------------- @@ -34,288 +34,303 @@ * * aggregates overloading has been added. Instead of the full * overload support we have for functions, aggregate overloading only - * applies to exact basetype matches. That is, we don't check the + * applies to exact basetype matches. That is, we don't check the * the inheritance hierarchy * * OLD COMMENTS: - * Currently, redefining aggregates using the same name is not - * supported. In such a case, a warning is printed that the - * aggregate already exists. If such is not the case, a new tuple - * is created and inserted in the aggregate relation. The fields - * of this tuple are aggregate name, owner id, 2 transition functions - * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn), - * type of data on which aggtransfn1 operates (aggbasetype), return - * types of the two transition functions (aggtranstype1 and - * aggtranstype2), final return type (aggfinaltype), and initial values - * for the two state transition functions (agginitval1 and agginitval2). - * All types and functions must have been defined - * prior to defining the aggregate. - * + * Currently, redefining aggregates using the same name is not + * supported. In such a case, a warning is printed that the + * aggregate already exists. If such is not the case, a new tuple + * is created and inserted in the aggregate relation. The fields + * of this tuple are aggregate name, owner id, 2 transition functions + * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn), + * type of data on which aggtransfn1 operates (aggbasetype), return + * types of the two transition functions (aggtranstype1 and + * aggtranstype2), final return type (aggfinaltype), and initial values + * for the two state transition functions (agginitval1 and agginitval2). + * All types and functions must have been defined + * prior to defining the aggregate. + * * --------------- */ void AggregateCreate(char *aggName, - char *aggtransfn1Name, - char *aggtransfn2Name, - char *aggfinalfnName, - char *aggbasetypeName, - char *aggtransfn1typeName, - char *aggtransfn2typeName, - char *agginitval1, - char *agginitval2) + char *aggtransfn1Name, + char *aggtransfn2Name, + char *aggfinalfnName, + char *aggbasetypeName, + char *aggtransfn1typeName, + char *aggtransfn2typeName, + char *agginitval1, + char *agginitval2) { - register i; - Relation aggdesc; - HeapTuple tup; - char nulls[Natts_pg_aggregate]; - Datum values[Natts_pg_aggregate]; - Form_pg_proc proc; - Oid xfn1 = InvalidOid; - Oid xfn2 = InvalidOid; - Oid ffn = InvalidOid; - Oid xbase = InvalidOid; - Oid xret1 = InvalidOid; - Oid xret2 = InvalidOid; - Oid fret = InvalidOid; - Oid fnArgs[8]; - TupleDesc tupDesc; - - memset(fnArgs, 0, 8 * sizeof(Oid)); - - /* sanity checks */ - if (!aggName) - elog(WARN, "AggregateCreate: no aggregate name supplied"); - - if (!aggtransfn1Name && !aggtransfn2Name) - elog(WARN, "AggregateCreate: aggregate must have at least one transition function"); - - tup = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(aggbasetypeName), - 0,0,0); - if(!HeapTupleIsValid(tup)) - elog(WARN, "AggregateCreate: Type '%s' undefined",aggbasetypeName); - xbase = tup->t_oid; + register i; + Relation aggdesc; + HeapTuple tup; + char nulls[Natts_pg_aggregate]; + Datum values[Natts_pg_aggregate]; + Form_pg_proc proc; + Oid xfn1 = InvalidOid; + Oid xfn2 = InvalidOid; + Oid ffn = InvalidOid; + Oid xbase = InvalidOid; + Oid xret1 = InvalidOid; + Oid xret2 = InvalidOid; + Oid fret = InvalidOid; + Oid fnArgs[8]; + TupleDesc tupDesc; + + memset(fnArgs, 0, 8 * sizeof(Oid)); + + /* sanity checks */ + if (!aggName) + elog(WARN, "AggregateCreate: no aggregate name supplied"); + + if (!aggtransfn1Name && !aggtransfn2Name) + elog(WARN, "AggregateCreate: aggregate must have at least one transition function"); + + tup = SearchSysCacheTuple(TYPNAME, + PointerGetDatum(aggbasetypeName), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "AggregateCreate: Type '%s' undefined", aggbasetypeName); + xbase = tup->t_oid; + + if (aggtransfn1Name) + { + tup = SearchSysCacheTuple(TYPNAME, + PointerGetDatum(aggtransfn1typeName), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "AggregateCreate: Type '%s' undefined", + aggtransfn1typeName); + xret1 = tup->t_oid; + + fnArgs[0] = xret1; + fnArgs[1] = xbase; + tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(aggtransfn1Name), + Int32GetDatum(2), + PointerGetDatum(fnArgs), + 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist", + aggtransfn1Name, aggtransfn1typeName, aggbasetypeName); + if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1) + elog(WARN, "AggregateCreate: return type of '%s' is not '%s'", + aggtransfn1Name, + aggtransfn1typeName); + xfn1 = tup->t_oid; + if (!OidIsValid(xfn1) || !OidIsValid(xret1) || + !OidIsValid(xbase)) + elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName); + } + + if (aggtransfn2Name) + { + tup = SearchSysCacheTuple(TYPNAME, + PointerGetDatum(aggtransfn2typeName), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "AggregateCreate: Type '%s' undefined", + aggtransfn2typeName); + xret2 = tup->t_oid; + + fnArgs[0] = xret2; + fnArgs[1] = 0; + tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(aggtransfn2Name), + Int32GetDatum(1), + PointerGetDatum(fnArgs), + 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "AggregateCreate: '%s'('%s') does not exist", + aggtransfn2Name, aggtransfn2typeName); + if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2) + elog(WARN, "AggregateCreate: return type of '%s' is not '%s'", + aggtransfn2Name, aggtransfn2typeName); + xfn2 = tup->t_oid; + if (!OidIsValid(xfn2) || !OidIsValid(xret2)) + elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName); + } + + tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName), + ObjectIdGetDatum(xbase), + 0, 0); + if (HeapTupleIsValid(tup)) + elog(WARN, + "AggregateCreate: aggregate '%s' with base type '%s' already exists", + aggName, aggbasetypeName); + + /* more sanity checks */ + if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName) + elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions"); - if (aggtransfn1Name) { - tup = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(aggtransfn1typeName), - 0,0,0); - if(!HeapTupleIsValid(tup)) - elog(WARN, "AggregateCreate: Type '%s' undefined", - aggtransfn1typeName); - xret1 = tup->t_oid; - - fnArgs[0] = xret1; - fnArgs[1] = xbase; - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(aggtransfn1Name), - Int32GetDatum(2), - PointerGetDatum(fnArgs), - 0); - if(!HeapTupleIsValid(tup)) - elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist", - aggtransfn1Name, aggtransfn1typeName, aggbasetypeName); - if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1) - elog(WARN, "AggregateCreate: return type of '%s' is not '%s'", - aggtransfn1Name, - aggtransfn1typeName); - xfn1 = tup->t_oid; - if (!OidIsValid(xfn1) || !OidIsValid(xret1) || - !OidIsValid(xbase)) - elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName); - } - - if (aggtransfn2Name) { - tup = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(aggtransfn2typeName), - 0,0,0); - if(!HeapTupleIsValid(tup)) - elog(WARN, "AggregateCreate: Type '%s' undefined", - aggtransfn2typeName); - xret2 = tup->t_oid; - - fnArgs[0] = xret2; - fnArgs[1] = 0; - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(aggtransfn2Name), - Int32GetDatum(1), - PointerGetDatum(fnArgs), - 0); - if(!HeapTupleIsValid(tup)) - elog(WARN, "AggregateCreate: '%s'('%s') does not exist", - aggtransfn2Name, aggtransfn2typeName); - if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2) - elog(WARN, "AggregateCreate: return type of '%s' is not '%s'", - aggtransfn2Name, aggtransfn2typeName); - xfn2 = tup->t_oid; - if (!OidIsValid(xfn2) || !OidIsValid(xret2)) - elog(WARN, "AggregateCreate: bogus function '%s'",aggfinalfnName); - } - - tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName), - ObjectIdGetDatum(xbase), - 0,0); - if (HeapTupleIsValid(tup)) - elog(WARN, - "AggregateCreate: aggregate '%s' with base type '%s' already exists", - aggName, aggbasetypeName); + if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName) + elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions"); - /* more sanity checks */ - if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName) - elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions"); - - if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName) - elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions"); - - if (aggfinalfnName) { - fnArgs[0] = xret1; - fnArgs[1] = xret2; - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(aggfinalfnName), - Int32GetDatum(2), - PointerGetDatum(fnArgs), - 0); - if(!HeapTupleIsValid(tup)) - elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist", - aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName); - ffn = tup->t_oid; - proc = (Form_pg_proc) GETSTRUCT(tup); - fret = proc->prorettype; - if (!OidIsValid(ffn) || !OidIsValid(fret)) - elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName); - } - - /* - * If transition function 2 is defined, it must have an initial value, - * whereas transition function 1 does not, which allows man and min - * aggregates to return NULL if they are evaluated on empty sets. - */ - if (OidIsValid(xfn2) && !agginitval2) - elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value"); - - /* initialize nulls and values */ - for(i=0; i < Natts_pg_aggregate; i++) { - nulls[i] = ' '; - values[i] = (Datum)NULL; - } - values[Anum_pg_aggregate_aggname-1] = PointerGetDatum(aggName); - values[Anum_pg_aggregate_aggowner-1] = - Int32GetDatum(GetUserId()); - values[Anum_pg_aggregate_aggtransfn1-1] = - ObjectIdGetDatum(xfn1); - values[Anum_pg_aggregate_aggtransfn2-1] = - ObjectIdGetDatum(xfn2); - values[Anum_pg_aggregate_aggfinalfn-1] = - ObjectIdGetDatum(ffn); - - values[Anum_pg_aggregate_aggbasetype-1] = - ObjectIdGetDatum(xbase); - if (!OidIsValid(xfn1)) { - values[Anum_pg_aggregate_aggtranstype1-1] = - ObjectIdGetDatum(InvalidOid); - values[Anum_pg_aggregate_aggtranstype2-1] = - ObjectIdGetDatum(xret2); - values[Anum_pg_aggregate_aggfinaltype-1] = - ObjectIdGetDatum(xret2); - } - else if (!OidIsValid(xfn2)) { - values[Anum_pg_aggregate_aggtranstype1-1] = - ObjectIdGetDatum(xret1); - values[Anum_pg_aggregate_aggtranstype2-1] = - ObjectIdGetDatum(InvalidOid); - values[Anum_pg_aggregate_aggfinaltype-1] = - ObjectIdGetDatum(xret1); - } - else { - values[Anum_pg_aggregate_aggtranstype1-1] = - ObjectIdGetDatum(xret1); - values[Anum_pg_aggregate_aggtranstype2-1] = - ObjectIdGetDatum(xret2); - values[Anum_pg_aggregate_aggfinaltype-1] = - ObjectIdGetDatum(fret); - } - - if (agginitval1) - values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1)); - else - nulls[Anum_pg_aggregate_agginitval1-1] = 'n'; - - if (agginitval2) - values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2)); - else - nulls[Anum_pg_aggregate_agginitval2-1] = 'n'; - - if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName))) - elog(WARN, "AggregateCreate: could not open '%s'", - AggregateRelationName); + if (aggfinalfnName) + { + fnArgs[0] = xret1; + fnArgs[1] = xret2; + tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(aggfinalfnName), + Int32GetDatum(2), + PointerGetDatum(fnArgs), + 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist", + aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName); + ffn = tup->t_oid; + proc = (Form_pg_proc) GETSTRUCT(tup); + fret = proc->prorettype; + if (!OidIsValid(ffn) || !OidIsValid(fret)) + elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName); + } - tupDesc = aggdesc->rd_att; - if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc, - values, - nulls))) - elog(WARN, "AggregateCreate: heap_formtuple failed"); - if (!OidIsValid(heap_insert(aggdesc, tup))) - elog(WARN, "AggregateCreate: heap_insert failed"); - heap_close(aggdesc); + /* + * If transition function 2 is defined, it must have an initial value, + * whereas transition function 1 does not, which allows man and min + * aggregates to return NULL if they are evaluated on empty sets. + */ + if (OidIsValid(xfn2) && !agginitval2) + elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value"); + + /* initialize nulls and values */ + for (i = 0; i < Natts_pg_aggregate; i++) + { + nulls[i] = ' '; + values[i] = (Datum) NULL; + } + values[Anum_pg_aggregate_aggname - 1] = PointerGetDatum(aggName); + values[Anum_pg_aggregate_aggowner - 1] = + Int32GetDatum(GetUserId()); + values[Anum_pg_aggregate_aggtransfn1 - 1] = + ObjectIdGetDatum(xfn1); + values[Anum_pg_aggregate_aggtransfn2 - 1] = + ObjectIdGetDatum(xfn2); + values[Anum_pg_aggregate_aggfinalfn - 1] = + ObjectIdGetDatum(ffn); + + values[Anum_pg_aggregate_aggbasetype - 1] = + ObjectIdGetDatum(xbase); + if (!OidIsValid(xfn1)) + { + values[Anum_pg_aggregate_aggtranstype1 - 1] = + ObjectIdGetDatum(InvalidOid); + values[Anum_pg_aggregate_aggtranstype2 - 1] = + ObjectIdGetDatum(xret2); + values[Anum_pg_aggregate_aggfinaltype - 1] = + ObjectIdGetDatum(xret2); + } + else if (!OidIsValid(xfn2)) + { + values[Anum_pg_aggregate_aggtranstype1 - 1] = + ObjectIdGetDatum(xret1); + values[Anum_pg_aggregate_aggtranstype2 - 1] = + ObjectIdGetDatum(InvalidOid); + values[Anum_pg_aggregate_aggfinaltype - 1] = + ObjectIdGetDatum(xret1); + } + else + { + values[Anum_pg_aggregate_aggtranstype1 - 1] = + ObjectIdGetDatum(xret1); + values[Anum_pg_aggregate_aggtranstype2 - 1] = + ObjectIdGetDatum(xret2); + values[Anum_pg_aggregate_aggfinaltype - 1] = + ObjectIdGetDatum(fret); + } + + if (agginitval1) + values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1)); + else + nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n'; + + if (agginitval2) + values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2)); + else + nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n'; + + if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName))) + elog(WARN, "AggregateCreate: could not open '%s'", + AggregateRelationName); + + tupDesc = aggdesc->rd_att; + if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc, + values, + nulls))) + elog(WARN, "AggregateCreate: heap_formtuple failed"); + if (!OidIsValid(heap_insert(aggdesc, tup))) + elog(WARN, "AggregateCreate: heap_insert failed"); + heap_close(aggdesc); } -char * -AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull) +char * +AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool * isNull) { - HeapTuple tup; - Relation aggRel; - int initValAttno; - Oid transtype; - text *textInitVal; - char *strInitVal, *initVal; - - Assert(PointerIsValid(aggName)); - Assert(PointerIsValid(isNull)); - Assert(xfuncno == 1 || xfuncno == 2); + HeapTuple tup; + Relation aggRel; + int initValAttno; + Oid transtype; + text *textInitVal; + char *strInitVal, + *initVal; + + Assert(PointerIsValid(aggName)); + Assert(PointerIsValid(isNull)); + Assert(xfuncno == 1 || xfuncno == 2); + + tup = SearchSysCacheTuple(AGGNAME, + PointerGetDatum(aggName), + PointerGetDatum(basetype), + 0, 0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'", + aggName); + if (xfuncno == 1) + { + transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1; + initValAttno = Anum_pg_aggregate_agginitval1; + } + else + /* can only be 1 or 2 */ + { + transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2; + initValAttno = Anum_pg_aggregate_agginitval2; + } - tup = SearchSysCacheTuple(AGGNAME, - PointerGetDatum(aggName), - PointerGetDatum(basetype), - 0,0); - if (!HeapTupleIsValid(tup)) - elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'", - aggName); - if (xfuncno == 1) { - transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1; - initValAttno = Anum_pg_aggregate_agginitval1; - } - else /* can only be 1 or 2 */ { - transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2; - initValAttno = Anum_pg_aggregate_agginitval2; - } - - aggRel = heap_openr(AggregateRelationName); - if (!RelationIsValid(aggRel)) - elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"", - AggregateRelationName); - /* - * must use fastgetattr in case one or other of the init values is NULL - */ - textInitVal = (text *) fastgetattr(tup, initValAttno, - RelationGetTupleDescriptor(aggRel), - isNull); - if (!PointerIsValid(textInitVal)) - *isNull = true; - if (*isNull) { + aggRel = heap_openr(AggregateRelationName); + if (!RelationIsValid(aggRel)) + elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"", + AggregateRelationName); + + /* + * must use fastgetattr in case one or other of the init values is + * NULL + */ + textInitVal = (text *) fastgetattr(tup, initValAttno, + RelationGetTupleDescriptor(aggRel), + isNull); + if (!PointerIsValid(textInitVal)) + *isNull = true; + if (*isNull) + { + heap_close(aggRel); + return ((char *) NULL); + } + strInitVal = textout(textInitVal); heap_close(aggRel); - return((char *) NULL); - } - strInitVal = textout(textInitVal); - heap_close(aggRel); - - tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype), - 0,0,0); - if (!HeapTupleIsValid(tup)) { + + tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + { + pfree(strInitVal); + elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type"); + } + initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1); pfree(strInitVal); - elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type"); - } - initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1); - pfree(strInitVal); - return(initVal); + return (initVal); } diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 1151f9f2e69..67a3a2f1495 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * pg_operator.c-- - * routines to support manipulation of the pg_operator relation + * routines to support manipulation of the pg_operator relation * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.11 1997/08/18 20:52:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.12 1997/09/07 04:40:27 momjian Exp $ * * NOTES - * these routines moved here from commands/define.c and somewhat cleaned up. - * + * these routines moved here from commands/define.c and somewhat cleaned up. + * *------------------------------------------------------------------------- */ #include <postgres.h> @@ -26,28 +26,33 @@ #include <fmgr.h> #include <miscadmin.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static Oid OperatorGetWithOpenRelation(Relation pg_operator_desc, - const char *operatorName, - Oid leftObjectId, - Oid rightObjectId ); -static Oid OperatorGet(char *operatorName, - char *leftTypeName, - char *rightTypeName ); - -static Oid OperatorShellMakeWithOpenRelation(Relation pg_operator_desc, - char *operatorName, - Oid leftObjectId, - Oid rightObjectId ); -static Oid OperatorShellMake(char *operatorName, - char *leftTypeName, - char *rightTypeName ); - -static void OperatorDef(char *operatorName, +static Oid +OperatorGetWithOpenRelation(Relation pg_operator_desc, + const char *operatorName, + Oid leftObjectId, + Oid rightObjectId); +static Oid +OperatorGet(char *operatorName, + char *leftTypeName, + char *rightTypeName); + +static Oid +OperatorShellMakeWithOpenRelation(Relation pg_operator_desc, + char *operatorName, + Oid leftObjectId, + Oid rightObjectId); +static Oid +OperatorShellMake(char *operatorName, + char *leftTypeName, + char *rightTypeName); + +static void +OperatorDef(char *operatorName, int definedOK, char *leftTypeName, char *rightTypeName, @@ -60,289 +65,292 @@ static void OperatorDef(char *operatorName, char *oinName, bool canHash, char *leftSortName, - char *rightSortName ); -static void OperatorUpd(Oid baseId , Oid commId , Oid negId ); - + char *rightSortName); +static void OperatorUpd(Oid baseId, Oid commId, Oid negId); + /* ---------------------------------------------------------------- - * OperatorGetWithOpenRelation + * OperatorGetWithOpenRelation * - * preforms a scan on pg_operator for an operator tuple - * with given name and left/right type oids. + * preforms a scan on pg_operator for an operator tuple + * with given name and left/right type oids. * ---------------------------------------------------------------- - * pg_operator_desc -- reldesc for pg_operator - * operatorName -- name of operator to fetch - * leftObjectId -- left oid of operator to fetch - * rightObjectId -- right oid of operator to fetch + * pg_operator_desc -- reldesc for pg_operator + * operatorName -- name of operator to fetch + * leftObjectId -- left oid of operator to fetch + * rightObjectId -- right oid of operator to fetch */ -static Oid +static Oid OperatorGetWithOpenRelation(Relation pg_operator_desc, - const char *operatorName, - Oid leftObjectId, - Oid rightObjectId) + const char *operatorName, + Oid leftObjectId, + Oid rightObjectId) { - HeapScanDesc pg_operator_scan; - Oid operatorObjectId; - HeapTuple tup; - - static ScanKeyData opKey[3] = { - { 0, Anum_pg_operator_oprname, NameEqualRegProcedure }, - { 0, Anum_pg_operator_oprleft, ObjectIdEqualRegProcedure }, - { 0, Anum_pg_operator_oprright, ObjectIdEqualRegProcedure }, - }; - - fmgr_info(NameEqualRegProcedure, - &opKey[0].sk_func, &opKey[0].sk_nargs); - fmgr_info(ObjectIdEqualRegProcedure, - &opKey[1].sk_func, &opKey[1].sk_nargs); - fmgr_info(ObjectIdEqualRegProcedure, - &opKey[2].sk_func, &opKey[2].sk_nargs); - - /* ---------------- - * form scan key - * ---------------- - */ - opKey[0].sk_argument = PointerGetDatum(operatorName); - opKey[1].sk_argument = ObjectIdGetDatum(leftObjectId); - opKey[2].sk_argument = ObjectIdGetDatum(rightObjectId); - - /* ---------------- - * begin the scan - * ---------------- - */ - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SelfTimeQual, - 3, - opKey); - - /* ---------------- - * fetch the operator tuple, if it exists, and determine - * the proper return oid value. - * ---------------- - */ - tup = heap_getnext(pg_operator_scan, 0, (Buffer *) 0); - operatorObjectId = HeapTupleIsValid(tup) ? tup->t_oid : InvalidOid; - - /* ---------------- - * close the scan and return the oid. - * ---------------- - */ - heap_endscan(pg_operator_scan); - - return - operatorObjectId; + HeapScanDesc pg_operator_scan; + Oid operatorObjectId; + HeapTuple tup; + + static ScanKeyData opKey[3] = { + {0, Anum_pg_operator_oprname, NameEqualRegProcedure}, + {0, Anum_pg_operator_oprleft, ObjectIdEqualRegProcedure}, + {0, Anum_pg_operator_oprright, ObjectIdEqualRegProcedure}, + }; + + fmgr_info(NameEqualRegProcedure, + &opKey[0].sk_func, &opKey[0].sk_nargs); + fmgr_info(ObjectIdEqualRegProcedure, + &opKey[1].sk_func, &opKey[1].sk_nargs); + fmgr_info(ObjectIdEqualRegProcedure, + &opKey[2].sk_func, &opKey[2].sk_nargs); + + /* ---------------- + * form scan key + * ---------------- + */ + opKey[0].sk_argument = PointerGetDatum(operatorName); + opKey[1].sk_argument = ObjectIdGetDatum(leftObjectId); + opKey[2].sk_argument = ObjectIdGetDatum(rightObjectId); + + /* ---------------- + * begin the scan + * ---------------- + */ + pg_operator_scan = heap_beginscan(pg_operator_desc, + 0, + SelfTimeQual, + 3, + opKey); + + /* ---------------- + * fetch the operator tuple, if it exists, and determine + * the proper return oid value. + * ---------------- + */ + tup = heap_getnext(pg_operator_scan, 0, (Buffer *) 0); + operatorObjectId = HeapTupleIsValid(tup) ? tup->t_oid : InvalidOid; + + /* ---------------- + * close the scan and return the oid. + * ---------------- + */ + heap_endscan(pg_operator_scan); + + return + operatorObjectId; } /* ---------------------------------------------------------------- - * OperatorGet + * OperatorGet * - * finds the operator associated with the specified name - * and left and right type names. + * finds the operator associated with the specified name + * and left and right type names. * ---------------------------------------------------------------- */ -static Oid +static Oid OperatorGet(char *operatorName, - char *leftTypeName, - char *rightTypeName) + char *leftTypeName, + char *rightTypeName) { - Relation pg_operator_desc; - - Oid operatorObjectId; - Oid leftObjectId = InvalidOid; - Oid rightObjectId = InvalidOid; - bool leftDefined = false; - bool rightDefined = false; - - /* ---------------- - * look up the operator types. - * - * Note: types must be defined before operators - * ---------------- - */ - if (leftTypeName) { - leftObjectId = TypeGet(leftTypeName, &leftDefined); - - if (!OidIsValid(leftObjectId) || !leftDefined) - elog(WARN, "OperatorGet: left type '%s' nonexistent",leftTypeName); - } - - if (rightTypeName) { - rightObjectId = TypeGet(rightTypeName, &rightDefined); - - if (!OidIsValid(rightObjectId) || !rightDefined) - elog(WARN, "OperatorGet: right type '%s' nonexistent", - rightTypeName); - } - - if (!((OidIsValid(leftObjectId) && leftDefined) || - (OidIsValid(rightObjectId) && rightDefined))) - elog(WARN, "OperatorGet: no argument types??"); - - /* ---------------- - * open the pg_operator relation - * ---------------- - */ - pg_operator_desc = heap_openr(OperatorRelationName); - - /* ---------------- - * get the oid for the operator with the appropriate name - * and left/right types. - * ---------------- - */ - operatorObjectId = OperatorGetWithOpenRelation(pg_operator_desc, - operatorName, - leftObjectId, - rightObjectId); - - /* ---------------- - * close the relation and return the operator oid. - * ---------------- - */ - heap_close(pg_operator_desc); - - return - operatorObjectId; + Relation pg_operator_desc; + + Oid operatorObjectId; + Oid leftObjectId = InvalidOid; + Oid rightObjectId = InvalidOid; + bool leftDefined = false; + bool rightDefined = false; + + /* ---------------- + * look up the operator types. + * + * Note: types must be defined before operators + * ---------------- + */ + if (leftTypeName) + { + leftObjectId = TypeGet(leftTypeName, &leftDefined); + + if (!OidIsValid(leftObjectId) || !leftDefined) + elog(WARN, "OperatorGet: left type '%s' nonexistent", leftTypeName); + } + + if (rightTypeName) + { + rightObjectId = TypeGet(rightTypeName, &rightDefined); + + if (!OidIsValid(rightObjectId) || !rightDefined) + elog(WARN, "OperatorGet: right type '%s' nonexistent", + rightTypeName); + } + + if (!((OidIsValid(leftObjectId) && leftDefined) || + (OidIsValid(rightObjectId) && rightDefined))) + elog(WARN, "OperatorGet: no argument types??"); + + /* ---------------- + * open the pg_operator relation + * ---------------- + */ + pg_operator_desc = heap_openr(OperatorRelationName); + + /* ---------------- + * get the oid for the operator with the appropriate name + * and left/right types. + * ---------------- + */ + operatorObjectId = OperatorGetWithOpenRelation(pg_operator_desc, + operatorName, + leftObjectId, + rightObjectId); + + /* ---------------- + * close the relation and return the operator oid. + * ---------------- + */ + heap_close(pg_operator_desc); + + return + operatorObjectId; } /* ---------------------------------------------------------------- - * OperatorShellMakeWithOpenRelation + * OperatorShellMakeWithOpenRelation * * ---------------------------------------------------------------- */ -static Oid +static Oid OperatorShellMakeWithOpenRelation(Relation pg_operator_desc, - char *operatorName, - Oid leftObjectId, - Oid rightObjectId) + char *operatorName, + Oid leftObjectId, + Oid rightObjectId) { - register int i; - HeapTuple tup; - Datum values[ Natts_pg_operator ]; - char nulls[ Natts_pg_operator ]; - Oid operatorObjectId; - TupleDesc tupDesc; - - /* ---------------- - * initialize our nulls[] and values[] arrays - * ---------------- - */ - for (i = 0; i < Natts_pg_operator; ++i) { - nulls[i] = ' '; - values[i] = (Datum)NULL; /* redundant, but safe */ - } - - /* ---------------- - * initialize values[] with the type name and - * ---------------- - */ - i = 0; - values[i++] = PointerGetDatum(operatorName); - values[i++] = Int32GetDatum(GetUserId()); - values[i++] = (Datum) (uint16) 0; - - values[i++] = (Datum)'b'; /* fill oprkind with a bogus value */ - - values[i++] = (Datum) (bool) 0; - values[i++] = (Datum) (bool) 0; - values[i++] = ObjectIdGetDatum(leftObjectId); /* <-- left oid */ - values[i++] = ObjectIdGetDatum(rightObjectId); /* <-- right oid */ - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - - /* ---------------- - * create a new operator tuple - * ---------------- - */ - tupDesc = pg_operator_desc->rd_att; - - tup = heap_formtuple(tupDesc, - values, - nulls); - - /* ---------------- - * insert our "shell" operator tuple and - * close the relation - * ---------------- - */ - heap_insert(pg_operator_desc, tup); - operatorObjectId = tup->t_oid; - - /* ---------------- - * free the tuple and return the operator oid - * ---------------- - */ - pfree(tup); - - return - operatorObjectId; + register int i; + HeapTuple tup; + Datum values[Natts_pg_operator]; + char nulls[Natts_pg_operator]; + Oid operatorObjectId; + TupleDesc tupDesc; + + /* ---------------- + * initialize our nulls[] and values[] arrays + * ---------------- + */ + for (i = 0; i < Natts_pg_operator; ++i) + { + nulls[i] = ' '; + values[i] = (Datum) NULL; /* redundant, but safe */ + } + + /* ---------------- + * initialize values[] with the type name and + * ---------------- + */ + i = 0; + values[i++] = PointerGetDatum(operatorName); + values[i++] = Int32GetDatum(GetUserId()); + values[i++] = (Datum) (uint16) 0; + + values[i++] = (Datum) 'b'; /* fill oprkind with a bogus value */ + + values[i++] = (Datum) (bool) 0; + values[i++] = (Datum) (bool) 0; + values[i++] = ObjectIdGetDatum(leftObjectId); /* <-- left oid */ + values[i++] = ObjectIdGetDatum(rightObjectId); /* <-- right oid */ + values[i++] = ObjectIdGetDatum(InvalidOid); + values[i++] = ObjectIdGetDatum(InvalidOid); + values[i++] = ObjectIdGetDatum(InvalidOid); + values[i++] = ObjectIdGetDatum(InvalidOid); + values[i++] = ObjectIdGetDatum(InvalidOid); + values[i++] = ObjectIdGetDatum(InvalidOid); + values[i++] = ObjectIdGetDatum(InvalidOid); + values[i++] = ObjectIdGetDatum(InvalidOid); + + /* ---------------- + * create a new operator tuple + * ---------------- + */ + tupDesc = pg_operator_desc->rd_att; + + tup = heap_formtuple(tupDesc, + values, + nulls); + + /* ---------------- + * insert our "shell" operator tuple and + * close the relation + * ---------------- + */ + heap_insert(pg_operator_desc, tup); + operatorObjectId = tup->t_oid; + + /* ---------------- + * free the tuple and return the operator oid + * ---------------- + */ + pfree(tup); + + return + operatorObjectId; } /* ---------------------------------------------------------------- - * OperatorShellMake + * OperatorShellMake * - * Specify operator name and left and right type names, - * fill an operator struct with this info and NULL's, - * call heap_insert and return the Oid - * to the caller. + * Specify operator name and left and right type names, + * fill an operator struct with this info and NULL's, + * call heap_insert and return the Oid + * to the caller. * ---------------------------------------------------------------- */ -static Oid +static Oid OperatorShellMake(char *operatorName, - char *leftTypeName, - char *rightTypeName) -{ - Relation pg_operator_desc; - Oid operatorObjectId; - - Oid leftObjectId = InvalidOid; - Oid rightObjectId = InvalidOid; - bool leftDefined = false; - bool rightDefined = false; - - /* ---------------- - * get the left and right type oid's for this operator - * ---------------- - */ - if (leftTypeName) - leftObjectId = TypeGet(leftTypeName, &leftDefined); - - if (rightTypeName) - rightObjectId = TypeGet(rightTypeName, &rightDefined); - - if (!((OidIsValid(leftObjectId) && leftDefined) || - (OidIsValid(rightObjectId) && rightDefined))) - elog(WARN, "OperatorShellMake: no valid argument types??"); - - /* ---------------- - * open pg_operator - * ---------------- - */ - pg_operator_desc = heap_openr(OperatorRelationName); - - /* ---------------- - * add a "shell" operator tuple to the operator relation - * and recover the shell tuple's oid. - * ---------------- - */ - operatorObjectId = - OperatorShellMakeWithOpenRelation(pg_operator_desc, - operatorName, - leftObjectId, - rightObjectId); - /* ---------------- - * close the operator relation and return the oid. - * ---------------- - */ - heap_close(pg_operator_desc); - - return - operatorObjectId; + char *leftTypeName, + char *rightTypeName) +{ + Relation pg_operator_desc; + Oid operatorObjectId; + + Oid leftObjectId = InvalidOid; + Oid rightObjectId = InvalidOid; + bool leftDefined = false; + bool rightDefined = false; + + /* ---------------- + * get the left and right type oid's for this operator + * ---------------- + */ + if (leftTypeName) + leftObjectId = TypeGet(leftTypeName, &leftDefined); + + if (rightTypeName) + rightObjectId = TypeGet(rightTypeName, &rightDefined); + + if (!((OidIsValid(leftObjectId) && leftDefined) || + (OidIsValid(rightObjectId) && rightDefined))) + elog(WARN, "OperatorShellMake: no valid argument types??"); + + /* ---------------- + * open pg_operator + * ---------------- + */ + pg_operator_desc = heap_openr(OperatorRelationName); + + /* ---------------- + * add a "shell" operator tuple to the operator relation + * and recover the shell tuple's oid. + * ---------------- + */ + operatorObjectId = + OperatorShellMakeWithOpenRelation(pg_operator_desc, + operatorName, + leftObjectId, + rightObjectId); + /* ---------------- + * close the operator relation and return the oid. + * ---------------- + */ + heap_close(pg_operator_desc); + + return + operatorObjectId; } /* -------------------------------- @@ -352,7 +360,7 @@ OperatorShellMake(char *operatorName, * specify operators that do not exist. For example, if operator * "op" is being defined, the negator operator "negop" and the * commutator "commop" can also be defined without specifying - * any information other than their names. Since in order to + * any information other than their names. Since in order to * add "op" to the PG_OPERATOR catalog, all the Oid's for these * operators must be placed in the fields of "op", a forward * declaration is done on the commutator and negator operators. @@ -363,518 +371,552 @@ OperatorShellMake(char *operatorName, * not available to the user as it is for type definition. * * Algorithm: - * - * check if operator already defined - * if so issue error if not definedOk, this is a duplicate - * but if definedOk, save the Oid -- filling in a shell + * + * check if operator already defined + * if so issue error if not definedOk, this is a duplicate + * but if definedOk, save the Oid -- filling in a shell * get the attribute types from relation descriptor for pg_operator * assign values to the fields of the operator: - * operatorName - * owner id (simply the user id of the caller) - * precedence - * operator "kind" either "b" for binary or "l" for left unary - * isLeftAssociative boolean - * canHash boolean - * leftTypeObjectId -- type must already be defined - * rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified - * resultType -- defer this, since it must be determined from - * the pg_procedure catalog - * commutatorObjectId -- if this is NULL, enter ObjectId=0 - * else if this already exists, enter it's ObjectId - * else if this does not yet exist, and is not - * the same as the main operatorName, then create - * a shell and enter the new ObjectId - * else if this does not exist but IS the same - * name as the main operator, set the ObjectId=0. - * Later OperatorCreate will make another call - * to OperatorDef which will cause this field - * to be filled in (because even though the names - * will be switched, they are the same name and - * at this point this ObjectId will then be defined) - * negatorObjectId -- same as for commutatorObjectId - * leftSortObjectId -- same as for commutatorObjectId - * rightSortObjectId -- same as for commutatorObjectId - * operatorProcedure -- must access the pg_procedure catalog to get the - * ObjectId of the procedure that actually does the operator - * actions this is required. Do an amgetattr to find out the - * return type of the procedure - * restrictionProcedure -- must access the pg_procedure catalog to get - * the ObjectId but this is optional - * joinProcedure -- same as restrictionProcedure + * operatorName + * owner id (simply the user id of the caller) + * precedence + * operator "kind" either "b" for binary or "l" for left unary + * isLeftAssociative boolean + * canHash boolean + * leftTypeObjectId -- type must already be defined + * rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified + * resultType -- defer this, since it must be determined from + * the pg_procedure catalog + * commutatorObjectId -- if this is NULL, enter ObjectId=0 + * else if this already exists, enter it's ObjectId + * else if this does not yet exist, and is not + * the same as the main operatorName, then create + * a shell and enter the new ObjectId + * else if this does not exist but IS the same + * name as the main operator, set the ObjectId=0. + * Later OperatorCreate will make another call + * to OperatorDef which will cause this field + * to be filled in (because even though the names + * will be switched, they are the same name and + * at this point this ObjectId will then be defined) + * negatorObjectId -- same as for commutatorObjectId + * leftSortObjectId -- same as for commutatorObjectId + * rightSortObjectId -- same as for commutatorObjectId + * operatorProcedure -- must access the pg_procedure catalog to get the + * ObjectId of the procedure that actually does the operator + * actions this is required. Do an amgetattr to find out the + * return type of the procedure + * restrictionProcedure -- must access the pg_procedure catalog to get + * the ObjectId but this is optional + * joinProcedure -- same as restrictionProcedure * now either insert or replace the operator into the pg_operator catalog * if the operator shell is being filled in - * access the catalog in order to get a valid buffer - * create a tuple using ModifyHeapTuple - * get the t_ctid from the modified tuple and call RelationReplaceHeapTuple + * access the catalog in order to get a valid buffer + * create a tuple using ModifyHeapTuple + * get the t_ctid from the modified tuple and call RelationReplaceHeapTuple * else if a new operator is being created - * create a tuple using heap_formtuple - * call heap_insert + * create a tuple using heap_formtuple + * call heap_insert * -------------------------------- - * "X" indicates an optional argument (i.e. one that can be NULL) - * operatorName; -- operator name - * definedOK; -- operator can already have an oid? - * leftTypeName; -- X left type name - * rightTypeName; -- X right type name - * procedureName; -- procedure oid for operator code - * precedence; -- operator precedence - * isLeftAssociative; -- operator is left associative? - * commutatorName; -- X commutator operator name - * negatorName; -- X negator operator name - * restrictionName; -- X restriction sel. procedure name - * joinName; -- X join sel. procedure name - * canHash; -- possible hash operator? - * leftSortName; -- X left sort operator - * rightSortName; -- X right sort operator + * "X" indicates an optional argument (i.e. one that can be NULL) + * operatorName; -- operator name + * definedOK; -- operator can already have an oid? + * leftTypeName; -- X left type name + * rightTypeName; -- X right type name + * procedureName; -- procedure oid for operator code + * precedence; -- operator precedence + * isLeftAssociative; -- operator is left associative? + * commutatorName; -- X commutator operator name + * negatorName; -- X negator operator name + * restrictionName; -- X restriction sel. procedure name + * joinName; -- X join sel. procedure name + * canHash; -- possible hash operator? + * leftSortName; -- X left sort operator + * rightSortName; -- X right sort operator */ static void OperatorDef(char *operatorName, - int definedOK, - char *leftTypeName, - char *rightTypeName, - char *procedureName, - uint16 precedence, - bool isLeftAssociative, - char *commutatorName, - char *negatorName, - char *restrictionName, - char *joinName, - bool canHash, - char *leftSortName, - char *rightSortName) + int definedOK, + char *leftTypeName, + char *rightTypeName, + char *procedureName, + uint16 precedence, + bool isLeftAssociative, + char *commutatorName, + char *negatorName, + char *restrictionName, + char *joinName, + bool canHash, + char *leftSortName, + char *rightSortName) { - register i, j; - Relation pg_operator_desc; - - HeapScanDesc pg_operator_scan; - HeapTuple tup; - Buffer buffer; - ItemPointerData itemPointerData; - char nulls[ Natts_pg_operator ]; - char replaces[ Natts_pg_operator ]; - Datum values[ Natts_pg_operator ]; - Oid other_oid = 0; - Oid operatorObjectId; - Oid leftTypeId = InvalidOid; - Oid rightTypeId = InvalidOid; - Oid commutatorId = InvalidOid; - Oid negatorId = InvalidOid; - bool leftDefined = false; - bool rightDefined = false; - char *name[4]; - Oid typeId[8]; - int nargs; - TupleDesc tupDesc; - - static ScanKeyData opKey[3] = { - { 0, Anum_pg_operator_oprname, NameEqualRegProcedure }, - { 0, Anum_pg_operator_oprleft, ObjectIdEqualRegProcedure }, - { 0, Anum_pg_operator_oprright, ObjectIdEqualRegProcedure }, - }; - - fmgr_info(NameEqualRegProcedure, - &opKey[0].sk_func, &opKey[0].sk_nargs); - fmgr_info(ObjectIdEqualRegProcedure, - &opKey[1].sk_func, &opKey[1].sk_nargs); - fmgr_info(ObjectIdEqualRegProcedure, - &opKey[2].sk_func, &opKey[2].sk_nargs); - - operatorObjectId = OperatorGet(operatorName, - leftTypeName, - rightTypeName); - - if (OidIsValid(operatorObjectId) && !definedOK) - elog(WARN, "OperatorDef: operator \"%s\" already defined", - operatorName); - - if (leftTypeName) - leftTypeId = TypeGet(leftTypeName, &leftDefined); - - if (rightTypeName) - rightTypeId = TypeGet(rightTypeName, &rightDefined); - - if (!((OidIsValid(leftTypeId && leftDefined)) || - (OidIsValid(rightTypeId && rightDefined)))) - elog(WARN, "OperatorGet: no argument types??"); - - for (i = 0; i < Natts_pg_operator; ++i) { - values[i] = (Datum)NULL; - replaces[i] = 'r'; - nulls[i] = ' '; - } - - /* ---------------- - * Look up registered procedures -- find the return type - * of procedureName to place in "result" field. - * Do this before shells are created so we don't - * have to worry about deleting them later. - * ---------------- - */ - memset(typeId, 0, 8 * sizeof(Oid)); - if (!leftTypeName) { - typeId[0] = rightTypeId; - nargs = 1; - } - else if (!rightTypeName) { - typeId[0] = leftTypeId; - nargs = 1; - } - else { - typeId[0] = leftTypeId; - typeId[1] = rightTypeId; - nargs = 2; - } - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(procedureName), - Int32GetDatum(nargs), - PointerGetDatum(typeId), - 0); - - if (!PointerIsValid(tup)) - func_error("OperatorDef", procedureName, nargs, typeId); - - values[ Anum_pg_operator_oprcode-1 ] = ObjectIdGetDatum(tup->t_oid); - values[ Anum_pg_operator_oprresult-1 ] = - ObjectIdGetDatum(((Form_pg_proc) - GETSTRUCT(tup))->prorettype); - - /* ---------------- - * find restriction - * ---------------- - */ - if (restrictionName) { /* optional */ - memset(typeId, 0, 8 * sizeof(Oid)); - typeId[0] = OIDOID; /* operator OID */ - typeId[1] = OIDOID; /* relation OID */ - typeId[2] = INT2OID; /* attribute number */ - typeId[3] = 0; /* value - can be any type */ - typeId[4] = INT4OID; /* flags - left or right selectivity */ - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(restrictionName), - Int32GetDatum(5), - PointerGetDatum(typeId), - 0); - if (!HeapTupleIsValid(tup)) - func_error("OperatorDef", restrictionName, 5, typeId); - - values[ Anum_pg_operator_oprrest-1 ] = ObjectIdGetDatum(tup->t_oid); - } else - values[ Anum_pg_operator_oprrest-1 ] = ObjectIdGetDatum(InvalidOid); - - /* ---------------- - * find join - only valid for binary operators - * ---------------- - */ - if (joinName) { /* optional */ + register i, + j; + Relation pg_operator_desc; + + HeapScanDesc pg_operator_scan; + HeapTuple tup; + Buffer buffer; + ItemPointerData itemPointerData; + char nulls[Natts_pg_operator]; + char replaces[Natts_pg_operator]; + Datum values[Natts_pg_operator]; + Oid other_oid = 0; + Oid operatorObjectId; + Oid leftTypeId = InvalidOid; + Oid rightTypeId = InvalidOid; + Oid commutatorId = InvalidOid; + Oid negatorId = InvalidOid; + bool leftDefined = false; + bool rightDefined = false; + char *name[4]; + Oid typeId[8]; + int nargs; + TupleDesc tupDesc; + + static ScanKeyData opKey[3] = { + {0, Anum_pg_operator_oprname, NameEqualRegProcedure}, + {0, Anum_pg_operator_oprleft, ObjectIdEqualRegProcedure}, + {0, Anum_pg_operator_oprright, ObjectIdEqualRegProcedure}, + }; + + fmgr_info(NameEqualRegProcedure, + &opKey[0].sk_func, &opKey[0].sk_nargs); + fmgr_info(ObjectIdEqualRegProcedure, + &opKey[1].sk_func, &opKey[1].sk_nargs); + fmgr_info(ObjectIdEqualRegProcedure, + &opKey[2].sk_func, &opKey[2].sk_nargs); + + operatorObjectId = OperatorGet(operatorName, + leftTypeName, + rightTypeName); + + if (OidIsValid(operatorObjectId) && !definedOK) + elog(WARN, "OperatorDef: operator \"%s\" already defined", + operatorName); + + if (leftTypeName) + leftTypeId = TypeGet(leftTypeName, &leftDefined); + + if (rightTypeName) + rightTypeId = TypeGet(rightTypeName, &rightDefined); + + if (!((OidIsValid(leftTypeId && leftDefined)) || + (OidIsValid(rightTypeId && rightDefined)))) + elog(WARN, "OperatorGet: no argument types??"); + + for (i = 0; i < Natts_pg_operator; ++i) + { + values[i] = (Datum) NULL; + replaces[i] = 'r'; + nulls[i] = ' '; + } + + /* ---------------- + * Look up registered procedures -- find the return type + * of procedureName to place in "result" field. + * Do this before shells are created so we don't + * have to worry about deleting them later. + * ---------------- + */ memset(typeId, 0, 8 * sizeof(Oid)); - typeId[0] = OIDOID; /* operator OID */ - typeId[1] = OIDOID; /* relation OID 1 */ - typeId[2] = INT2OID; /* attribute number 1 */ - typeId[3] = OIDOID; /* relation OID 2 */ - typeId[4] = INT2OID; /* attribute number 2 */ - + if (!leftTypeName) + { + typeId[0] = rightTypeId; + nargs = 1; + } + else if (!rightTypeName) + { + typeId[0] = leftTypeId; + nargs = 1; + } + else + { + typeId[0] = leftTypeId; + typeId[1] = rightTypeId; + nargs = 2; + } tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(joinName), - Int32GetDatum(5), - PointerGetDatum(typeId), - 0); - if (!HeapTupleIsValid(tup)) - func_error("OperatorDef", joinName, 5, typeId); - - values[Anum_pg_operator_oprjoin-1] = ObjectIdGetDatum(tup->t_oid); - } else - values[Anum_pg_operator_oprjoin-1] = ObjectIdGetDatum(InvalidOid); - - /* ---------------- - * set up values in the operator tuple - * ---------------- - */ - i = 0; - values[i++] = PointerGetDatum(operatorName); - values[i++] = Int32GetDatum(GetUserId()); - values[i++] = UInt16GetDatum(precedence); - values[i++] = leftTypeName ? (rightTypeName ? 'b' : 'r') : 'l'; - values[i++] = Int8GetDatum(isLeftAssociative); - values[i++] = Int8GetDatum(canHash); - values[i++] = ObjectIdGetDatum(leftTypeId); - values[i++] = ObjectIdGetDatum(rightTypeId); - - ++i; /* Skip "prorettype", this was done above */ - - /* - * Set up the other operators. If they do not currently exist, - * set up shells in order to get ObjectId's and call OperatorDef - * again later to fill in the shells. - */ - name[0] = commutatorName; - name[1] = negatorName; - name[2] = leftSortName; - name[3] = rightSortName; - - for (j = 0; j < 4; ++j) { - if (name[j]) { - - /* for the commutator, switch order of arguments */ - if (j == 0) { - other_oid = OperatorGet(name[j], rightTypeName,leftTypeName); - commutatorId = other_oid; - } else { - other_oid = OperatorGet(name[j], leftTypeName,rightTypeName); - if (j == 1) - negatorId = other_oid; - } - - if (OidIsValid(other_oid)) /* already in catalogs */ - values[i++] = ObjectIdGetDatum(other_oid); - else if (strcmp(operatorName, name[j]) != 0) { - /* not in catalogs, different from operator */ - - /* for the commutator, switch order of arguments */ - if (j == 0) { - other_oid = OperatorShellMake(name[j], - rightTypeName, - leftTypeName); - } else { - other_oid = OperatorShellMake(name[j], - leftTypeName, - rightTypeName); + PointerGetDatum(procedureName), + Int32GetDatum(nargs), + PointerGetDatum(typeId), + 0); + + if (!PointerIsValid(tup)) + func_error("OperatorDef", procedureName, nargs, typeId); + + values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(tup->t_oid); + values[Anum_pg_operator_oprresult - 1] = + ObjectIdGetDatum(((Form_pg_proc) + GETSTRUCT(tup))->prorettype); + + /* ---------------- + * find restriction + * ---------------- + */ + if (restrictionName) + { /* optional */ + memset(typeId, 0, 8 * sizeof(Oid)); + typeId[0] = OIDOID; /* operator OID */ + typeId[1] = OIDOID; /* relation OID */ + typeId[2] = INT2OID; /* attribute number */ + typeId[3] = 0; /* value - can be any type */ + typeId[4] = INT4OID; /* flags - left or right selectivity */ + tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(restrictionName), + Int32GetDatum(5), + PointerGetDatum(typeId), + 0); + if (!HeapTupleIsValid(tup)) + func_error("OperatorDef", restrictionName, 5, typeId); + + values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(tup->t_oid); + } + else + values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid); + + /* ---------------- + * find join - only valid for binary operators + * ---------------- + */ + if (joinName) + { /* optional */ + memset(typeId, 0, 8 * sizeof(Oid)); + typeId[0] = OIDOID; /* operator OID */ + typeId[1] = OIDOID; /* relation OID 1 */ + typeId[2] = INT2OID; /* attribute number 1 */ + typeId[3] = OIDOID; /* relation OID 2 */ + typeId[4] = INT2OID; /* attribute number 2 */ + + tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(joinName), + Int32GetDatum(5), + PointerGetDatum(typeId), + 0); + if (!HeapTupleIsValid(tup)) + func_error("OperatorDef", joinName, 5, typeId); + + values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(tup->t_oid); + } + else + values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid); + + /* ---------------- + * set up values in the operator tuple + * ---------------- + */ + i = 0; + values[i++] = PointerGetDatum(operatorName); + values[i++] = Int32GetDatum(GetUserId()); + values[i++] = UInt16GetDatum(precedence); + values[i++] = leftTypeName ? (rightTypeName ? 'b' : 'r') : 'l'; + values[i++] = Int8GetDatum(isLeftAssociative); + values[i++] = Int8GetDatum(canHash); + values[i++] = ObjectIdGetDatum(leftTypeId); + values[i++] = ObjectIdGetDatum(rightTypeId); + + ++i; /* Skip "prorettype", this was done above */ + + /* + * Set up the other operators. If they do not currently exist, set up + * shells in order to get ObjectId's and call OperatorDef again later + * to fill in the shells. + */ + name[0] = commutatorName; + name[1] = negatorName; + name[2] = leftSortName; + name[3] = rightSortName; + + for (j = 0; j < 4; ++j) + { + if (name[j]) + { + + /* for the commutator, switch order of arguments */ + if (j == 0) + { + other_oid = OperatorGet(name[j], rightTypeName, leftTypeName); + commutatorId = other_oid; + } + else + { + other_oid = OperatorGet(name[j], leftTypeName, rightTypeName); + if (j == 1) + negatorId = other_oid; + } + + if (OidIsValid(other_oid)) /* already in catalogs */ + values[i++] = ObjectIdGetDatum(other_oid); + else if (strcmp(operatorName, name[j]) != 0) + { + /* not in catalogs, different from operator */ + + /* for the commutator, switch order of arguments */ + if (j == 0) + { + other_oid = OperatorShellMake(name[j], + rightTypeName, + leftTypeName); + } + else + { + other_oid = OperatorShellMake(name[j], + leftTypeName, + rightTypeName); + } + + if (!OidIsValid(other_oid)) + elog(WARN, + "OperatorDef: can't create operator '%s'", + name[j]); + values[i++] = ObjectIdGetDatum(other_oid); + + } + else +/* not in catalogs, same as operator ??? */ + values[i++] = ObjectIdGetDatum(InvalidOid); + } - - if (!OidIsValid(other_oid)) - elog(WARN, - "OperatorDef: can't create operator '%s'", - name[j]); - values[i++] = ObjectIdGetDatum(other_oid); - - } else /* not in catalogs, same as operator ??? */ - values[i++] = ObjectIdGetDatum(InvalidOid); - - } else /* new operator is optional */ - values[i++] = ObjectIdGetDatum(InvalidOid); - } - - /* last three fields were filled in first */ - - /* - * If we are adding to an operator shell, get its t_ctid and a - * buffer. - */ - pg_operator_desc = heap_openr(OperatorRelationName); - - if (operatorObjectId) { - opKey[0].sk_argument = PointerGetDatum(operatorName); - opKey[1].sk_argument = ObjectIdGetDatum(leftTypeId); - opKey[2].sk_argument = ObjectIdGetDatum(rightTypeId); - - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SelfTimeQual, - 3, - opKey); - - tup = heap_getnext(pg_operator_scan, 0, &buffer); - if (HeapTupleIsValid(tup)) { - tup = heap_modifytuple(tup, - buffer, - pg_operator_desc, - values, - nulls, - replaces); - - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - setheapoverride(true); - heap_replace(pg_operator_desc, &itemPointerData, tup); - setheapoverride(false); - } else - elog(WARN, "OperatorDef: no operator %d", other_oid); - - heap_endscan(pg_operator_scan); - - } else { - tupDesc = pg_operator_desc->rd_att; - tup = heap_formtuple(tupDesc, values, nulls); - - heap_insert(pg_operator_desc, tup); - operatorObjectId = tup->t_oid; - } - - heap_close(pg_operator_desc); - - /* - * It's possible that we're creating a skeleton operator here for - * the commute or negate attributes of a real operator. If we are, - * then we're done. If not, we may need to update the negator and - * commutator for this attribute. The reason for this is that the - * user may want to create two operators (say < and >=). When he - * defines <, if he uses >= as the negator or commutator, he won't - * be able to insert it later, since (for some reason) define operator - * defines it for him. So what he does is to define > without a - * negator or commutator. Then he defines >= with < as the negator - * and commutator. As a side effect, this will update the > tuple - * if it has no commutator or negator defined. - * - * Alstublieft, Tom Vijlbrief. - */ - if (!definedOK) - OperatorUpd(operatorObjectId, commutatorId, negatorId); + else +/* new operator is optional */ + values[i++] = ObjectIdGetDatum(InvalidOid); + } + + /* last three fields were filled in first */ + + /* + * If we are adding to an operator shell, get its t_ctid and a buffer. + */ + pg_operator_desc = heap_openr(OperatorRelationName); + + if (operatorObjectId) + { + opKey[0].sk_argument = PointerGetDatum(operatorName); + opKey[1].sk_argument = ObjectIdGetDatum(leftTypeId); + opKey[2].sk_argument = ObjectIdGetDatum(rightTypeId); + + pg_operator_scan = heap_beginscan(pg_operator_desc, + 0, + SelfTimeQual, + 3, + opKey); + + tup = heap_getnext(pg_operator_scan, 0, &buffer); + if (HeapTupleIsValid(tup)) + { + tup = heap_modifytuple(tup, + buffer, + pg_operator_desc, + values, + nulls, + replaces); + + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + setheapoverride(true); + heap_replace(pg_operator_desc, &itemPointerData, tup); + setheapoverride(false); + } + else + elog(WARN, "OperatorDef: no operator %d", other_oid); + + heap_endscan(pg_operator_scan); + + } + else + { + tupDesc = pg_operator_desc->rd_att; + tup = heap_formtuple(tupDesc, values, nulls); + + heap_insert(pg_operator_desc, tup); + operatorObjectId = tup->t_oid; + } + + heap_close(pg_operator_desc); + + /* + * It's possible that we're creating a skeleton operator here for the + * commute or negate attributes of a real operator. If we are, then + * we're done. If not, we may need to update the negator and + * commutator for this attribute. The reason for this is that the + * user may want to create two operators (say < and >=). When he + * defines <, if he uses >= as the negator or commutator, he won't be + * able to insert it later, since (for some reason) define operator + * defines it for him. So what he does is to define > without a + * negator or commutator. Then he defines >= with < as the negator + * and commutator. As a side effect, this will update the > tuple if + * it has no commutator or negator defined. + * + * Alstublieft, Tom Vijlbrief. + */ + if (!definedOK) + OperatorUpd(operatorObjectId, commutatorId, negatorId); } /* ---------------------------------------------------------------- * OperatorUpd * - * For a given operator, look up its negator and commutator operators. - * If they are defined, but their negator and commutator operators - * (respectively) are not, then use the new operator for neg and comm. - * This solves a problem for users who need to insert two new operators - * which are the negator or commutator of each other. - * ---------------------------------------------------------------- + * For a given operator, look up its negator and commutator operators. + * If they are defined, but their negator and commutator operators + * (respectively) are not, then use the new operator for neg and comm. + * This solves a problem for users who need to insert two new operators + * which are the negator or commutator of each other. + * ---------------------------------------------------------------- */ static void OperatorUpd(Oid baseId, Oid commId, Oid negId) { - register i; - Relation pg_operator_desc; - HeapScanDesc pg_operator_scan; - HeapTuple tup; - Buffer buffer; - ItemPointerData itemPointerData; - char nulls[ Natts_pg_operator ]; - char replaces[ Natts_pg_operator ]; - Datum values[ Natts_pg_operator ]; - - static ScanKeyData opKey[1] = { - { 0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure }, - }; - - fmgr_info(ObjectIdEqualRegProcedure, - &opKey[0].sk_func, &opKey[0].sk_nargs); - - for (i = 0; i < Natts_pg_operator; ++i) { - values[i] = (Datum)NULL; - replaces[i] = ' '; - nulls[i] = ' '; - } - - pg_operator_desc = heap_openr(OperatorRelationName); - - /* check and update the commutator, if necessary */ - opKey[0].sk_argument = ObjectIdGetDatum(commId); - - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SelfTimeQual, - 1, - opKey); - - tup = heap_getnext(pg_operator_scan, 0, &buffer); - - /* if the commutator and negator are the same operator, do one update */ - if (commId == negId) { - if (HeapTupleIsValid(tup)) { - OperatorTupleForm t; - - t = (OperatorTupleForm) GETSTRUCT(tup); - if (!OidIsValid(t->oprcom) - || !OidIsValid(t->oprnegate)) { - - if (!OidIsValid(t->oprnegate)) { - values[Anum_pg_operator_oprnegate - 1] = - ObjectIdGetDatum(baseId); - replaces[ Anum_pg_operator_oprnegate - 1 ] = 'r'; - } - - if (!OidIsValid(t->oprcom)) { - values[Anum_pg_operator_oprcom - 1] = - ObjectIdGetDatum(baseId); - replaces[ Anum_pg_operator_oprcom - 1 ] = 'r'; + register i; + Relation pg_operator_desc; + HeapScanDesc pg_operator_scan; + HeapTuple tup; + Buffer buffer; + ItemPointerData itemPointerData; + char nulls[Natts_pg_operator]; + char replaces[Natts_pg_operator]; + Datum values[Natts_pg_operator]; + + static ScanKeyData opKey[1] = { + {0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure}, + }; + + fmgr_info(ObjectIdEqualRegProcedure, + &opKey[0].sk_func, &opKey[0].sk_nargs); + + for (i = 0; i < Natts_pg_operator; ++i) + { + values[i] = (Datum) NULL; + replaces[i] = ' '; + nulls[i] = ' '; + } + + pg_operator_desc = heap_openr(OperatorRelationName); + + /* check and update the commutator, if necessary */ + opKey[0].sk_argument = ObjectIdGetDatum(commId); + + pg_operator_scan = heap_beginscan(pg_operator_desc, + 0, + SelfTimeQual, + 1, + opKey); + + tup = heap_getnext(pg_operator_scan, 0, &buffer); + + /* if the commutator and negator are the same operator, do one update */ + if (commId == negId) + { + if (HeapTupleIsValid(tup)) + { + OperatorTupleForm t; + + t = (OperatorTupleForm) GETSTRUCT(tup); + if (!OidIsValid(t->oprcom) + || !OidIsValid(t->oprnegate)) + { + + if (!OidIsValid(t->oprnegate)) + { + values[Anum_pg_operator_oprnegate - 1] = + ObjectIdGetDatum(baseId); + replaces[Anum_pg_operator_oprnegate - 1] = 'r'; + } + + if (!OidIsValid(t->oprcom)) + { + values[Anum_pg_operator_oprcom - 1] = + ObjectIdGetDatum(baseId); + replaces[Anum_pg_operator_oprcom - 1] = 'r'; + } + + tup = heap_modifytuple(tup, + buffer, + pg_operator_desc, + values, + nulls, + replaces); + + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + + setheapoverride(true); + heap_replace(pg_operator_desc, &itemPointerData, tup); + setheapoverride(false); + + } } - + heap_endscan(pg_operator_scan); + + heap_close(pg_operator_desc); + + /* release the buffer properly */ + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + + return; + } + + /* if commutator and negator are different, do two updates */ + if (HeapTupleIsValid(tup) && + !(OidIsValid(((OperatorTupleForm) GETSTRUCT(tup))->oprcom))) + { + values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); + replaces[Anum_pg_operator_oprcom - 1] = 'r'; tup = heap_modifytuple(tup, - buffer, - pg_operator_desc, - values, - nulls, - replaces); - + buffer, + pg_operator_desc, + values, + nulls, + replaces); + ItemPointerCopy(&tup->t_ctid, &itemPointerData); - setheapoverride(true); heap_replace(pg_operator_desc, &itemPointerData, tup); setheapoverride(false); - } + values[Anum_pg_operator_oprcom - 1] = (Datum) NULL; + replaces[Anum_pg_operator_oprcom - 1] = ' '; + + /* release the buffer properly */ + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + + } + + /* check and update the negator, if necessary */ + opKey[0].sk_argument = ObjectIdGetDatum(negId); + + pg_operator_scan = heap_beginscan(pg_operator_desc, + 0, + SelfTimeQual, + 1, + opKey); + + tup = heap_getnext(pg_operator_scan, 0, &buffer); + if (HeapTupleIsValid(tup) && + !(OidIsValid(((OperatorTupleForm) GETSTRUCT(tup))->oprnegate))) + { + values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); + replaces[Anum_pg_operator_oprnegate - 1] = 'r'; + tup = heap_modifytuple(tup, + buffer, + pg_operator_desc, + values, + nulls, + replaces); + + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + + setheapoverride(true); + heap_replace(pg_operator_desc, &itemPointerData, tup); + setheapoverride(false); } - heap_endscan(pg_operator_scan); - - heap_close(pg_operator_desc); - - /* release the buffer properly */ - if (BufferIsValid(buffer)) - ReleaseBuffer(buffer); - - return; - } - - /* if commutator and negator are different, do two updates */ - if (HeapTupleIsValid(tup) && - !(OidIsValid(((OperatorTupleForm) GETSTRUCT(tup))->oprcom))) { - values[ Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); - replaces[ Anum_pg_operator_oprcom - 1] = 'r'; - tup = heap_modifytuple(tup, - buffer, - pg_operator_desc, - values, - nulls, - replaces); - - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - setheapoverride(true); - heap_replace(pg_operator_desc, &itemPointerData, tup); - setheapoverride(false); - - values[ Anum_pg_operator_oprcom - 1 ] = (Datum)NULL; - replaces[ Anum_pg_operator_oprcom - 1 ] = ' '; /* release the buffer properly */ if (BufferIsValid(buffer)) - ReleaseBuffer(buffer); - - } - - /* check and update the negator, if necessary */ - opKey[0].sk_argument = ObjectIdGetDatum(negId); - - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SelfTimeQual, - 1, - opKey); - - tup = heap_getnext(pg_operator_scan, 0, &buffer); - if (HeapTupleIsValid(tup) && - !(OidIsValid(((OperatorTupleForm) GETSTRUCT(tup))->oprnegate))) { - values[Anum_pg_operator_oprnegate-1] = ObjectIdGetDatum(baseId); - replaces[ Anum_pg_operator_oprnegate - 1 ] = 'r'; - tup = heap_modifytuple(tup, - buffer, - pg_operator_desc, - values, - nulls, - replaces); - - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - - setheapoverride(true); - heap_replace(pg_operator_desc, &itemPointerData, tup); - setheapoverride(false); - } - - /* release the buffer properly */ - if (BufferIsValid(buffer)) - ReleaseBuffer(buffer); - - heap_endscan(pg_operator_scan); - - heap_close(pg_operator_desc); + ReleaseBuffer(buffer); + + heap_endscan(pg_operator_scan); + + heap_close(pg_operator_desc); } @@ -883,196 +925,202 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId) * * Algorithm: * - * Since the commutator, negator, leftsortoperator, and rightsortoperator - * can be defined implicitly through OperatorCreate, must check before - * the main operator is added to see if they already exist. If they - * do not already exist, OperatorDef makes a "shell" for each undefined - * one, and then OperatorCreate must call OperatorDef again to fill in - * each shell. All this is necessary in order to get the right ObjectId's - * filled into the right fields. + * Since the commutator, negator, leftsortoperator, and rightsortoperator + * can be defined implicitly through OperatorCreate, must check before + * the main operator is added to see if they already exist. If they + * do not already exist, OperatorDef makes a "shell" for each undefined + * one, and then OperatorCreate must call OperatorDef again to fill in + * each shell. All this is necessary in order to get the right ObjectId's + * filled into the right fields. + * + * The "definedOk" flag indicates that OperatorDef can be called on + * the operator even though it already has an entry in the PG_OPERATOR + * relation. This allows shells to be filled in. The user cannot + * forward declare operators, this is strictly an internal capability. * - * The "definedOk" flag indicates that OperatorDef can be called on - * the operator even though it already has an entry in the PG_OPERATOR - * relation. This allows shells to be filled in. The user cannot - * forward declare operators, this is strictly an internal capability. + * When the shells are filled in by subsequent calls to OperatorDef, + * all the fields are the same as the definition of the original operator + * except that the target operator name and the original operatorName + * are switched. In the case of commutator and negator, special flags + * are set to indicate their status, telling the executor(?) that + * the operands are to be switched, or the outcome of the procedure + * negated. * - * When the shells are filled in by subsequent calls to OperatorDef, - * all the fields are the same as the definition of the original operator - * except that the target operator name and the original operatorName - * are switched. In the case of commutator and negator, special flags - * are set to indicate their status, telling the executor(?) that - * the operands are to be switched, or the outcome of the procedure - * negated. - * * ************************* NOTE NOTE NOTE ****************************** - * - * If the execution of this utility is interrupted, the pg_operator - * catalog may be left in an inconsistent state. Similarly, if - * something is removed from the pg_operator, pg_type, or pg_procedure - * catalog while this is executing, the results may be inconsistent. + * + * If the execution of this utility is interrupted, the pg_operator + * catalog may be left in an inconsistent state. Similarly, if + * something is removed from the pg_operator, pg_type, or pg_procedure + * catalog while this is executing, the results may be inconsistent. * ---------------------------------------------------------------- * - * "X" indicates an optional argument (i.e. one that can be NULL) - * operatorName; -- operator name - * leftTypeName; -- X left type name - * rightTypeName; -- X right type name - * procedureName; -- procedure for operator - * precedence; -- operator precedence - * isLeftAssociative; -- operator is left associative - * commutatorName; -- X commutator operator name - * negatorName; -- X negator operator name - * restrictionName; -- X restriction sel. procedure - * joinName; -- X join sel. procedure name - * canHash; -- operator hashes - * leftSortName; -- X left sort operator - * rightSortName; -- X right sort operator - * + * "X" indicates an optional argument (i.e. one that can be NULL) + * operatorName; -- operator name + * leftTypeName; -- X left type name + * rightTypeName; -- X right type name + * procedureName; -- procedure for operator + * precedence; -- operator precedence + * isLeftAssociative; -- operator is left associative + * commutatorName; -- X commutator operator name + * negatorName; -- X negator operator name + * restrictionName; -- X restriction sel. procedure + * joinName; -- X join sel. procedure name + * canHash; -- operator hashes + * leftSortName; -- X left sort operator + * rightSortName; -- X right sort operator + * */ void OperatorCreate(char *operatorName, - char *leftTypeName, - char *rightTypeName, - char *procedureName, - uint16 precedence, - bool isLeftAssociative, - char *commutatorName, - char *negatorName, - char *restrictionName, - char *joinName, - bool canHash, - char *leftSortName, - char *rightSortName) + char *leftTypeName, + char *rightTypeName, + char *procedureName, + uint16 precedence, + bool isLeftAssociative, + char *commutatorName, + char *negatorName, + char *restrictionName, + char *joinName, + bool canHash, + char *leftSortName, + char *rightSortName) { - Oid commObjectId, negObjectId; - Oid leftSortObjectId, rightSortObjectId; - int definedOK; - - if (!leftTypeName && !rightTypeName) - elog(WARN, "OperatorCreate : at least one of leftarg or rightarg must be defined"); - - /* ---------------- - * get the oid's of the operator's associated operators, if possible. - * ---------------- - */ - if (commutatorName) - commObjectId = OperatorGet(commutatorName, /* commute type order */ - rightTypeName, - leftTypeName); - else commObjectId = 0; - - if (negatorName) - negObjectId = OperatorGet(negatorName, - leftTypeName, - rightTypeName); - else negObjectId = 0; - - if (leftSortName) - leftSortObjectId = OperatorGet(leftSortName, - leftTypeName, - rightTypeName); - else leftSortObjectId = 0; - - if (rightSortName) - rightSortObjectId = OperatorGet(rightSortName, + Oid commObjectId, + negObjectId; + Oid leftSortObjectId, + rightSortObjectId; + int definedOK; + + if (!leftTypeName && !rightTypeName) + elog(WARN, "OperatorCreate : at least one of leftarg or rightarg must be defined"); + + /* ---------------- + * get the oid's of the operator's associated operators, if possible. + * ---------------- + */ + if (commutatorName) + commObjectId = OperatorGet(commutatorName, /* commute type order */ + rightTypeName, + leftTypeName); + else + commObjectId = 0; + + if (negatorName) + negObjectId = OperatorGet(negatorName, + leftTypeName, + rightTypeName); + else + negObjectId = 0; + + if (leftSortName) + leftSortObjectId = OperatorGet(leftSortName, + leftTypeName, + rightTypeName); + else + leftSortObjectId = 0; + + if (rightSortName) + rightSortObjectId = OperatorGet(rightSortName, + rightTypeName, + leftTypeName); + else + rightSortObjectId = 0; + + /* ---------------- + * Use OperatorDef() to define the specified operator and + * also create shells for the operator's associated operators + * if they don't already exist. + * + * This operator should not be defined yet. + * ---------------- + */ + definedOK = 0; + + OperatorDef(operatorName, + definedOK, + leftTypeName, + rightTypeName, + procedureName, + precedence, + isLeftAssociative, + commutatorName, + negatorName, + restrictionName, + joinName, + canHash, + leftSortName, + rightSortName); + + /* ---------------- + * Now fill in information in the operator's associated + * operators. + * + * These operators should be defined or have shells defined. + * ---------------- + */ + definedOK = 1; + + if (!OidIsValid(commObjectId) && commutatorName) + OperatorDef(commutatorName, + definedOK, + leftTypeName, /* should eventually */ + rightTypeName, /* commute order */ + procedureName, + precedence, + isLeftAssociative, + operatorName, /* commutator */ + negatorName, + restrictionName, + joinName, + canHash, + rightSortName, + leftSortName); + + if (negatorName && !OidIsValid(negObjectId)) + OperatorDef(negatorName, + definedOK, + leftTypeName, + rightTypeName, + procedureName, + precedence, + isLeftAssociative, + commutatorName, + operatorName, /* negator */ + restrictionName, + joinName, + canHash, + leftSortName, + rightSortName); + + if (leftSortName && !OidIsValid(leftSortObjectId)) + OperatorDef(leftSortName, + definedOK, + leftTypeName, + rightTypeName, + procedureName, + precedence, + isLeftAssociative, + commutatorName, + negatorName, + restrictionName, + joinName, + canHash, + operatorName, /* left sort */ + rightSortName); + + if (rightSortName && !OidIsValid(rightSortObjectId)) + OperatorDef(rightSortName, + definedOK, + leftTypeName, rightTypeName, - leftTypeName); - else rightSortObjectId = 0; - - /* ---------------- - * Use OperatorDef() to define the specified operator and - * also create shells for the operator's associated operators - * if they don't already exist. - * - * This operator should not be defined yet. - * ---------------- - */ - definedOK = 0; - - OperatorDef(operatorName, - definedOK, - leftTypeName, - rightTypeName, - procedureName, - precedence, - isLeftAssociative, - commutatorName, - negatorName, - restrictionName, - joinName, - canHash, - leftSortName, - rightSortName); - - /* ---------------- - * Now fill in information in the operator's associated - * operators. - * - * These operators should be defined or have shells defined. - * ---------------- - */ - definedOK = 1; - - if (!OidIsValid(commObjectId) && commutatorName) - OperatorDef(commutatorName, - definedOK, - leftTypeName, /* should eventually */ - rightTypeName, /* commute order */ - procedureName, - precedence, - isLeftAssociative, - operatorName, /* commutator */ - negatorName, - restrictionName, - joinName, - canHash, - rightSortName, - leftSortName); - - if (negatorName && !OidIsValid(negObjectId)) - OperatorDef(negatorName, - definedOK, - leftTypeName, - rightTypeName, - procedureName, - precedence, - isLeftAssociative, - commutatorName, - operatorName, /* negator */ - restrictionName, - joinName, - canHash, - leftSortName, - rightSortName); - - if (leftSortName && !OidIsValid(leftSortObjectId)) - OperatorDef(leftSortName, - definedOK, - leftTypeName, - rightTypeName, - procedureName, - precedence, - isLeftAssociative, - commutatorName, - negatorName, - restrictionName, - joinName, - canHash, - operatorName, /* left sort */ - rightSortName); - - if (rightSortName && !OidIsValid(rightSortObjectId)) - OperatorDef(rightSortName, - definedOK, - leftTypeName, - rightTypeName, - procedureName, - precedence, - isLeftAssociative, - commutatorName, - negatorName, - restrictionName, - joinName, - canHash, - leftSortName, - operatorName); /* right sort */ + procedureName, + precedence, + isLeftAssociative, + commutatorName, + negatorName, + restrictionName, + joinName, + canHash, + leftSortName, + operatorName); /* right sort */ } diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 947bdc6051b..1dd1b0867c3 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * pg_proc.c-- - * routines to support manipulation of the pg_proc relation + * routines to support manipulation of the pg_proc relation * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.5 1996/11/08 00:44:34 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.6 1997/09/07 04:40:30 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,231 +30,252 @@ #include <utils/lsyscache.h> #include <miscadmin.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* ---------------------------------------------------------------- - * ProcedureDefine + * ProcedureDefine * ---------------------------------------------------------------- */ Oid ProcedureCreate(char *procedureName, - bool returnsSet, - char *returnTypeName, - char *languageName, - char *prosrc, - char *probin, - bool canCache, - bool trusted, - int32 byte_pct, - int32 perbyte_cpu, - int32 percall_cpu, - int32 outin_ratio, - List *argList, - CommandDest dest) + bool returnsSet, + char *returnTypeName, + char *languageName, + char *prosrc, + char *probin, + bool canCache, + bool trusted, + int32 byte_pct, + int32 perbyte_cpu, + int32 percall_cpu, + int32 outin_ratio, + List * argList, + CommandDest dest) { - register i; - Relation rdesc; - HeapTuple tup; - bool defined; - uint16 parameterCount; - char nulls[ Natts_pg_proc ]; - Datum values[ Natts_pg_proc ]; - Oid languageObjectId; - Oid typeObjectId; - List *x; - QueryTreeList *querytree_list; - List *plan_list; - Oid typev[8]; - Oid relid; - Oid toid; - text *prosrctext; - TupleDesc tupDesc; - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(PointerIsValid(prosrc)); - Assert(PointerIsValid(probin)); - - parameterCount = 0; - memset(typev, 0, 8 * sizeof(Oid)); - foreach (x, argList) { - Value *t = lfirst(x); - - if (parameterCount == 8) - elog(WARN, "Procedures cannot take more than 8 arguments"); - - if (strcmp(strVal(t), "opaque") == 0) { - if (strcmp(languageName, "sql") == 0) { - elog(WARN, "ProcedureDefine: sql functions cannot take type \"opaque\""); - } - toid = 0; - } else { - toid = TypeGet(strVal(t), &defined); - - if (!OidIsValid(toid)) { - elog(WARN, "ProcedureCreate: arg type '%s' is not defined", - strVal(t)); - } - - if (!defined) { - elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell", - strVal(t)); - } - } - - typev[parameterCount++] = toid; - } - - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(procedureName), - UInt16GetDatum(parameterCount), - PointerGetDatum(typev), - 0); - - if (HeapTupleIsValid(tup)) - elog(WARN, "ProcedureCreate: procedure %s already exists with same arguments", - procedureName); - - if (!strcmp(languageName, "sql")) { - /* If this call is defining a set, check if the set is already - * defined by looking to see whether this call's function text - * matches a function already in pg_proc. If so just return the - * OID of the existing set. + register i; + Relation rdesc; + HeapTuple tup; + bool defined; + uint16 parameterCount; + char nulls[Natts_pg_proc]; + Datum values[Natts_pg_proc]; + Oid languageObjectId; + Oid typeObjectId; + List *x; + QueryTreeList *querytree_list; + List *plan_list; + Oid typev[8]; + Oid relid; + Oid toid; + text *prosrctext; + TupleDesc tupDesc; + + /* ---------------- + * sanity checks + * ---------------- */ - if (!strcmp(procedureName, GENERICSETNAME)) { - prosrctext = textin(prosrc); - tup = SearchSysCacheTuple(PROSRC, - PointerGetDatum(prosrctext), - 0,0,0); - if (HeapTupleIsValid(tup)) - return tup->t_oid; + Assert(PointerIsValid(prosrc)); + Assert(PointerIsValid(probin)); + + parameterCount = 0; + memset(typev, 0, 8 * sizeof(Oid)); + foreach(x, argList) + { + Value *t = lfirst(x); + + if (parameterCount == 8) + elog(WARN, "Procedures cannot take more than 8 arguments"); + + if (strcmp(strVal(t), "opaque") == 0) + { + if (strcmp(languageName, "sql") == 0) + { + elog(WARN, "ProcedureDefine: sql functions cannot take type \"opaque\""); + } + toid = 0; + } + else + { + toid = TypeGet(strVal(t), &defined); + + if (!OidIsValid(toid)) + { + elog(WARN, "ProcedureCreate: arg type '%s' is not defined", + strVal(t)); + } + + if (!defined) + { + elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell", + strVal(t)); + } + } + + typev[parameterCount++] = toid; + } + + tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(procedureName), + UInt16GetDatum(parameterCount), + PointerGetDatum(typev), + 0); + + if (HeapTupleIsValid(tup)) + elog(WARN, "ProcedureCreate: procedure %s already exists with same arguments", + procedureName); + + if (!strcmp(languageName, "sql")) + { + + /* + * If this call is defining a set, check if the set is already + * defined by looking to see whether this call's function text + * matches a function already in pg_proc. If so just return the + * OID of the existing set. + */ + if (!strcmp(procedureName, GENERICSETNAME)) + { + prosrctext = textin(prosrc); + tup = SearchSysCacheTuple(PROSRC, + PointerGetDatum(prosrctext), + 0, 0, 0); + if (HeapTupleIsValid(tup)) + return tup->t_oid; + } } - } - - tup = SearchSysCacheTuple(LANNAME, - PointerGetDatum(languageName), - 0,0,0); - - if (!HeapTupleIsValid(tup)) - elog(WARN, "ProcedureCreate: no such language %s", - languageName); - - languageObjectId = tup->t_oid; - - if (strcmp(returnTypeName, "opaque") == 0) { - if (strcmp(languageName, "sql") == 0) { - elog(WARN, "ProcedureCreate: sql functions cannot return type \"opaque\""); + + tup = SearchSysCacheTuple(LANNAME, + PointerGetDatum(languageName), + 0, 0, 0); + + if (!HeapTupleIsValid(tup)) + elog(WARN, "ProcedureCreate: no such language %s", + languageName); + + languageObjectId = tup->t_oid; + + if (strcmp(returnTypeName, "opaque") == 0) + { + if (strcmp(languageName, "sql") == 0) + { + elog(WARN, "ProcedureCreate: sql functions cannot return type \"opaque\""); + } + typeObjectId = 0; } - typeObjectId = 0; - } - - else { - typeObjectId = TypeGet(returnTypeName, &defined); - - if (!OidIsValid(typeObjectId)) { - elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined", - returnTypeName); + + else + { + typeObjectId = TypeGet(returnTypeName, &defined); + + if (!OidIsValid(typeObjectId)) + { + elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined", + returnTypeName); #if 0 - elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'", - returnTypeName); -#endif - typeObjectId = TypeShellMake(returnTypeName); - if (!OidIsValid(typeObjectId)) { - elog(WARN, "ProcedureCreate: could not create type '%s'", - returnTypeName); - } + elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'", + returnTypeName); +#endif + typeObjectId = TypeShellMake(returnTypeName); + if (!OidIsValid(typeObjectId)) + { + elog(WARN, "ProcedureCreate: could not create type '%s'", + returnTypeName); + } + } + + else if (!defined) + { + elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell", + returnTypeName); + } } - - else if (!defined) { - elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell", - returnTypeName); + + /* + * don't allow functions of complex types that have the same name as + * existing attributes of the type + */ + if (parameterCount == 1 && + (toid = TypeGet(strVal(lfirst(argList)), &defined)) && + defined && + (relid = typeid_get_relid(toid)) != 0 && + get_attnum(relid, procedureName) != InvalidAttrNumber) + elog(WARN, "method %s already an attribute of type %s", + procedureName, strVal(lfirst(argList))); + + + /* + * If this is a postquel procedure, we parse it here in order to be + * sure that it contains no syntax errors. We should store the plan + * in an Inversion file for use later, but for now, we just store the + * procedure's text in the prosrc attribute. + */ + + if (strcmp(languageName, "sql") == 0) + { + plan_list = pg_plan(prosrc, typev, parameterCount, + &querytree_list, dest); + + /* typecheck return value */ + pg_checkretval(typeObjectId, querytree_list); } - } - - /* don't allow functions of complex types that have the same name as - existing attributes of the type */ - if (parameterCount == 1 && - (toid = TypeGet(strVal(lfirst(argList)), &defined)) && - defined && - (relid = typeid_get_relid(toid)) != 0 && - get_attnum(relid, procedureName) != InvalidAttrNumber) - elog(WARN, "method %s already an attribute of type %s", - procedureName, strVal(lfirst(argList))); - - - /* - * If this is a postquel procedure, we parse it here in order to - * be sure that it contains no syntax errors. We should store - * the plan in an Inversion file for use later, but for now, we - * just store the procedure's text in the prosrc attribute. - */ - - if (strcmp(languageName, "sql") == 0) { - plan_list = pg_plan(prosrc, typev, parameterCount, - &querytree_list, dest); - - /* typecheck return value */ - pg_checkretval(typeObjectId, querytree_list); - } - - for (i = 0; i < Natts_pg_proc; ++i) { - nulls[i] = ' '; - values[i] = (Datum)NULL; - } - - i = 0; - values[i++] = PointerGetDatum(procedureName); - values[i++] = Int32GetDatum(GetUserId()); - values[i++] = ObjectIdGetDatum(languageObjectId); - - /* XXX isinherited is always false for now */ - - values[i++] = Int8GetDatum((bool) 0); - - /* XXX istrusted is always false for now */ - - values[i++] = Int8GetDatum(trusted); - values[i++] = Int8GetDatum(canCache); - values[i++] = UInt16GetDatum(parameterCount); - values[i++] = Int8GetDatum(returnsSet); - values[i++] = ObjectIdGetDatum(typeObjectId); - - values[i++] = (Datum) typev; - /* - * The following assignments of constants are made. The real values - * will have to be extracted from the arglist someday soon. - */ - values[i++] = Int32GetDatum(byte_pct); /* probyte_pct */ - values[i++] = Int32GetDatum(perbyte_cpu); /* properbyte_cpu */ - values[i++] = Int32GetDatum(percall_cpu); /* propercall_cpu */ - values[i++] = Int32GetDatum(outin_ratio); /* prooutin_ratio */ - - values[i++] = (Datum)fmgr(TextInRegProcedure, prosrc); /* prosrc */ - values[i++] = (Datum)fmgr(TextInRegProcedure, probin); /* probin */ - - rdesc = heap_openr(ProcedureRelationName); - - tupDesc = rdesc->rd_att; - tup = heap_formtuple(tupDesc, - values, - nulls); - - heap_insert(rdesc, tup); - - if (RelationGetRelationTupleForm(rdesc)->relhasindex) + + for (i = 0; i < Natts_pg_proc; ++i) { - Relation idescs[Num_pg_proc_indices]; - - CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_proc_indices, rdesc, tup); - CatalogCloseIndices(Num_pg_proc_indices, idescs); + nulls[i] = ' '; + values[i] = (Datum) NULL; } - heap_close(rdesc); - return tup->t_oid; -} + i = 0; + values[i++] = PointerGetDatum(procedureName); + values[i++] = Int32GetDatum(GetUserId()); + values[i++] = ObjectIdGetDatum(languageObjectId); + + /* XXX isinherited is always false for now */ + + values[i++] = Int8GetDatum((bool) 0); + + /* XXX istrusted is always false for now */ + + values[i++] = Int8GetDatum(trusted); + values[i++] = Int8GetDatum(canCache); + values[i++] = UInt16GetDatum(parameterCount); + values[i++] = Int8GetDatum(returnsSet); + values[i++] = ObjectIdGetDatum(typeObjectId); + + values[i++] = (Datum) typev; + + /* + * The following assignments of constants are made. The real values + * will have to be extracted from the arglist someday soon. + */ + values[i++] = Int32GetDatum(byte_pct); /* probyte_pct */ + values[i++] = Int32GetDatum(perbyte_cpu); /* properbyte_cpu */ + values[i++] = Int32GetDatum(percall_cpu); /* propercall_cpu */ + values[i++] = Int32GetDatum(outin_ratio); /* prooutin_ratio */ + + values[i++] = (Datum) fmgr(TextInRegProcedure, prosrc); /* prosrc */ + values[i++] = (Datum) fmgr(TextInRegProcedure, probin); /* probin */ + + rdesc = heap_openr(ProcedureRelationName); + + tupDesc = rdesc->rd_att; + tup = heap_formtuple(tupDesc, + values, + nulls); + + heap_insert(rdesc, tup); + + if (RelationGetRelationTupleForm(rdesc)->relhasindex) + { + Relation idescs[Num_pg_proc_indices]; + + CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_proc_indices, rdesc, tup); + CatalogCloseIndices(Num_pg_proc_indices, idescs); + } + heap_close(rdesc); + return tup->t_oid; +} diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index caf53b03ea6..9a31030421c 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * pg_type.c-- - * routines to support manipulation of the pg_type relation + * routines to support manipulation of the pg_type relation * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.7 1997/08/19 21:30:38 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.8 1997/09/07 04:40:31 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,572 +25,595 @@ #include <storage/lmgr.h> #include <miscadmin.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static Oid TypeShellMakeWithOpenRelation(Relation pg_type_desc, - char *typeName); +static Oid +TypeShellMakeWithOpenRelation(Relation pg_type_desc, + char *typeName); /* ---------------------------------------------------------------- - * TypeGetWithOpenRelation + * TypeGetWithOpenRelation * - * preforms a scan on pg_type for a type tuple with the - * given type name. + * preforms a scan on pg_type for a type tuple with the + * given type name. * ---------------------------------------------------------------- - * pg_type_desc -- reldesc for pg_type - * typeName -- name of type to be fetched - * defined -- has the type been defined? + * pg_type_desc -- reldesc for pg_type + * typeName -- name of type to be fetched + * defined -- has the type been defined? */ -static Oid +static Oid TypeGetWithOpenRelation(Relation pg_type_desc, - char* typeName, - bool *defined) + char *typeName, + bool * defined) { - HeapScanDesc scan; - HeapTuple tup; - - static ScanKeyData typeKey[1] = { - { 0, Anum_pg_type_typname, NameEqualRegProcedure } - }; - - /* ---------------- - * initialize the scan key and begin a scan of pg_type - * ---------------- - */ - fmgr_info(NameEqualRegProcedure, - &typeKey[0].sk_func, &typeKey[0].sk_nargs); - typeKey[0].sk_argument = PointerGetDatum(typeName); - - scan = heap_beginscan(pg_type_desc, - 0, - SelfTimeQual, - 1, - typeKey); - - /* ---------------- - * get the type tuple, if it exists. - * ---------------- - */ - tup = heap_getnext(scan, 0, (Buffer *) 0); - - /* ---------------- - * if no type tuple exists for the given type name, then - * end the scan and return appropriate information. - * ---------------- - */ - if (! HeapTupleIsValid(tup)) { + HeapScanDesc scan; + HeapTuple tup; + + static ScanKeyData typeKey[1] = { + {0, Anum_pg_type_typname, NameEqualRegProcedure} + }; + + /* ---------------- + * initialize the scan key and begin a scan of pg_type + * ---------------- + */ + fmgr_info(NameEqualRegProcedure, + &typeKey[0].sk_func, &typeKey[0].sk_nargs); + typeKey[0].sk_argument = PointerGetDatum(typeName); + + scan = heap_beginscan(pg_type_desc, + 0, + SelfTimeQual, + 1, + typeKey); + + /* ---------------- + * get the type tuple, if it exists. + * ---------------- + */ + tup = heap_getnext(scan, 0, (Buffer *) 0); + + /* ---------------- + * if no type tuple exists for the given type name, then + * end the scan and return appropriate information. + * ---------------- + */ + if (!HeapTupleIsValid(tup)) + { + heap_endscan(scan); + *defined = false; + return InvalidOid; + } + + /* ---------------- + * here, the type tuple does exist so we pull information from + * the typisdefined field of the tuple and return the tuple's + * oid, which is the oid of the type. + * ---------------- + */ heap_endscan(scan); - *defined = false; - return InvalidOid; - } - - /* ---------------- - * here, the type tuple does exist so we pull information from - * the typisdefined field of the tuple and return the tuple's - * oid, which is the oid of the type. - * ---------------- - */ - heap_endscan(scan); - *defined = (bool) ((TypeTupleForm) GETSTRUCT(tup))->typisdefined; - - return - tup->t_oid; + *defined = (bool) ((TypeTupleForm) GETSTRUCT(tup))->typisdefined; + + return + tup->t_oid; } /* ---------------------------------------------------------------- - * TypeGet + * TypeGet * - * Finds the ObjectId of a type, even if uncommitted; "defined" - * is only set if the type has actually been defined, i.e., if - * the type tuple is not a shell. + * Finds the ObjectId of a type, even if uncommitted; "defined" + * is only set if the type has actually been defined, i.e., if + * the type tuple is not a shell. * - * Note: the meat of this function is now in the function - * TypeGetWithOpenRelation(). -cim 6/15/90 + * Note: the meat of this function is now in the function + * TypeGetWithOpenRelation(). -cim 6/15/90 * - * Also called from util/remove.c + * Also called from util/remove.c * ---------------------------------------------------------------- */ Oid -TypeGet(char* typeName, /* name of type to be fetched */ - bool *defined) /* has the type been defined? */ +TypeGet(char *typeName, /* name of type to be fetched */ + bool * defined) /* has the type been defined? */ { - Relation pg_type_desc; - Oid typeoid; - - /* ---------------- - * open the pg_type relation - * ---------------- - */ - pg_type_desc = heap_openr(TypeRelationName); - - /* ---------------- - * scan the type relation for the information we want - * ---------------- - */ - typeoid = TypeGetWithOpenRelation(pg_type_desc, - typeName, - defined); - - /* ---------------- - * close the type relation and return the type oid. - * ---------------- - */ - heap_close(pg_type_desc); - - return - typeoid; + Relation pg_type_desc; + Oid typeoid; + + /* ---------------- + * open the pg_type relation + * ---------------- + */ + pg_type_desc = heap_openr(TypeRelationName); + + /* ---------------- + * scan the type relation for the information we want + * ---------------- + */ + typeoid = TypeGetWithOpenRelation(pg_type_desc, + typeName, + defined); + + /* ---------------- + * close the type relation and return the type oid. + * ---------------- + */ + heap_close(pg_type_desc); + + return + typeoid; } /* ---------------------------------------------------------------- - * TypeShellMakeWithOpenRelation + * TypeShellMakeWithOpenRelation * * ---------------------------------------------------------------- */ -static Oid +static Oid TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName) { - register int i; - HeapTuple tup; - Datum values[ Natts_pg_type ]; - char nulls[ Natts_pg_type ]; - Oid typoid; - TupleDesc tupDesc; - - /* ---------------- - * initialize our nulls[] and values[] arrays - * ---------------- - */ - for (i = 0; i < Natts_pg_type; ++i) { - nulls[i] = ' '; - values[i] = (Datum)NULL; /* redundant, but safe */ - } - - /* ---------------- - * initialize values[] with the type name and - * ---------------- - */ - i = 0; - values[i++] = (Datum) typeName; /* 1 */ - values[i++] = (Datum) InvalidOid; /* 2 */ - values[i++] = (Datum) (int16) 0; /* 3 */ - values[i++] = (Datum) (int16) 0; /* 4 */ - values[i++] = (Datum) (bool) 0; /* 5 */ - values[i++] = (Datum) (bool) 0; /* 6 */ - values[i++] = (Datum) (bool) 0; /* 7 */ - values[i++] = (Datum) (bool) 0; /* 8 */ - values[i++] = (Datum) InvalidOid; /* 9 */ - values[i++] = (Datum) InvalidOid; /* 10 */ - values[i++] = (Datum) InvalidOid; /* 11 */ - values[i++] = (Datum) InvalidOid; /* 12 */ - values[i++] = (Datum) InvalidOid; /* 13 */ - values[i++] = (Datum) InvalidOid; /* 14 */ - values[i++] = (Datum) 'i'; /* 15 */ - - /* - * ... and fill typdefault with a bogus value - */ - values[i++] = - (Datum)fmgr(TextInRegProcedure, typeName); /* 15 */ - - /* ---------------- - * create a new type tuple with FormHeapTuple - * ---------------- - */ - tupDesc = pg_type_desc->rd_att; - - tup = heap_formtuple(tupDesc, values, nulls); - - /* ---------------- - * insert the tuple in the relation and get the tuple's oid. - * ---------------- - */ - heap_insert(pg_type_desc, tup); - typoid = tup->t_oid; - - if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex) + register int i; + HeapTuple tup; + Datum values[Natts_pg_type]; + char nulls[Natts_pg_type]; + Oid typoid; + TupleDesc tupDesc; + + /* ---------------- + * initialize our nulls[] and values[] arrays + * ---------------- + */ + for (i = 0; i < Natts_pg_type; ++i) + { + nulls[i] = ' '; + values[i] = (Datum) NULL; /* redundant, but safe */ + } + + /* ---------------- + * initialize values[] with the type name and + * ---------------- + */ + i = 0; + values[i++] = (Datum) typeName; /* 1 */ + values[i++] = (Datum) InvalidOid; /* 2 */ + values[i++] = (Datum) (int16) 0; /* 3 */ + values[i++] = (Datum) (int16) 0; /* 4 */ + values[i++] = (Datum) (bool) 0; /* 5 */ + values[i++] = (Datum) (bool) 0; /* 6 */ + values[i++] = (Datum) (bool) 0; /* 7 */ + values[i++] = (Datum) (bool) 0; /* 8 */ + values[i++] = (Datum) InvalidOid; /* 9 */ + values[i++] = (Datum) InvalidOid; /* 10 */ + values[i++] = (Datum) InvalidOid; /* 11 */ + values[i++] = (Datum) InvalidOid; /* 12 */ + values[i++] = (Datum) InvalidOid; /* 13 */ + values[i++] = (Datum) InvalidOid; /* 14 */ + values[i++] = (Datum) 'i'; /* 15 */ + + /* + * ... and fill typdefault with a bogus value + */ + values[i++] = + (Datum) fmgr(TextInRegProcedure, typeName); /* 15 */ + + /* ---------------- + * create a new type tuple with FormHeapTuple + * ---------------- + */ + tupDesc = pg_type_desc->rd_att; + + tup = heap_formtuple(tupDesc, values, nulls); + + /* ---------------- + * insert the tuple in the relation and get the tuple's oid. + * ---------------- + */ + heap_insert(pg_type_desc, tup); + typoid = tup->t_oid; + + if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex) { - Relation idescs[Num_pg_type_indices]; - - CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup); - CatalogCloseIndices(Num_pg_type_indices, idescs); + Relation idescs[Num_pg_type_indices]; + + CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup); + CatalogCloseIndices(Num_pg_type_indices, idescs); } - /* ---------------- - * free the tuple and return the type-oid - * ---------------- - */ - pfree(tup); - - return - typoid; + /* ---------------- + * free the tuple and return the type-oid + * ---------------- + */ + pfree(tup); + + return + typoid; } /* ---------------------------------------------------------------- - * TypeShellMake + * TypeShellMake * - * This procedure inserts a "shell" tuple into the type - * relation. The type tuple inserted has invalid values - * and in particular, the "typisdefined" field is false. + * This procedure inserts a "shell" tuple into the type + * relation. The type tuple inserted has invalid values + * and in particular, the "typisdefined" field is false. * - * This is used so that a tuple exists in the catalogs. - * The invalid fields should be fixed up sometime after - * this routine is called, and then the "typeisdefined" - * field is set to true. -cim 6/15/90 + * This is used so that a tuple exists in the catalogs. + * The invalid fields should be fixed up sometime after + * this routine is called, and then the "typeisdefined" + * field is set to true. -cim 6/15/90 * ---------------------------------------------------------------- */ Oid TypeShellMake(char *typeName) { - Relation pg_type_desc; - Oid typoid; - - Assert(PointerIsValid(typeName)); - - /* ---------------- - * open pg_type - * ---------------- - */ - pg_type_desc = heap_openr(TypeRelationName); - - /* ---------------- - * insert the shell tuple - * ---------------- - */ - typoid = TypeShellMakeWithOpenRelation(pg_type_desc, typeName); - - /* ---------------- - * close pg_type and return the tuple's oid. - * ---------------- - */ - heap_close(pg_type_desc); - - return - typoid; + Relation pg_type_desc; + Oid typoid; + + Assert(PointerIsValid(typeName)); + + /* ---------------- + * open pg_type + * ---------------- + */ + pg_type_desc = heap_openr(TypeRelationName); + + /* ---------------- + * insert the shell tuple + * ---------------- + */ + typoid = TypeShellMakeWithOpenRelation(pg_type_desc, typeName); + + /* ---------------- + * close pg_type and return the tuple's oid. + * ---------------- + */ + heap_close(pg_type_desc); + + return + typoid; } /* ---------------------------------------------------------------- - * TypeCreate + * TypeCreate * - * This does all the necessary work needed to define a new type. + * This does all the necessary work needed to define a new type. * ---------------------------------------------------------------- */ Oid TypeCreate(char *typeName, - Oid relationOid, /* only for 'c'atalog typeTypes */ - int16 internalSize, - int16 externalSize, - char typeType, - char typDelim, - char *inputProcedure, - char *outputProcedure, - char *sendProcedure, - char *receiveProcedure, - char *elementTypeName, - char *defaultTypeValue, /* internal rep */ - bool passedByValue, - char alignment) + Oid relationOid, /* only for 'c'atalog typeTypes */ + int16 internalSize, + int16 externalSize, + char typeType, + char typDelim, + char *inputProcedure, + char *outputProcedure, + char *sendProcedure, + char *receiveProcedure, + char *elementTypeName, + char *defaultTypeValue, /* internal rep */ + bool passedByValue, + char alignment) { - register i, j; - Relation pg_type_desc; - HeapScanDesc pg_type_scan; - - Oid typeObjectId; - Oid elementObjectId = InvalidOid; - - HeapTuple tup; - char nulls[Natts_pg_type]; - char replaces[Natts_pg_type]; - Datum values[Natts_pg_type]; - - Buffer buffer; - char *procname; - char *procs[4]; - bool defined; - ItemPointerData itemPointerData; - TupleDesc tupDesc; - - Oid argList[8]; - - - static ScanKeyData typeKey[1] = { - { 0, Anum_pg_type_typname, NameEqualRegProcedure } - }; - - fmgr_info(NameEqualRegProcedure, - &typeKey[0].sk_func, &typeKey[0].sk_nargs); - - /* ---------------- - * check that the type is not already defined. - * ---------------- - */ - typeObjectId = TypeGet(typeName, &defined); - if (OidIsValid(typeObjectId) && defined) { - elog(WARN, "TypeCreate: type %s already defined", typeName); - } - - /* ---------------- - * if this type has an associated elementType, then we check that - * it is defined. - * ---------------- - */ - if (elementTypeName) { - elementObjectId = TypeGet(elementTypeName, &defined); - if (!defined) { - elog(WARN, "TypeCreate: type %s is not defined", elementTypeName); + register i, + j; + Relation pg_type_desc; + HeapScanDesc pg_type_scan; + + Oid typeObjectId; + Oid elementObjectId = InvalidOid; + + HeapTuple tup; + char nulls[Natts_pg_type]; + char replaces[Natts_pg_type]; + Datum values[Natts_pg_type]; + + Buffer buffer; + char *procname; + char *procs[4]; + bool defined; + ItemPointerData itemPointerData; + TupleDesc tupDesc; + + Oid argList[8]; + + + static ScanKeyData typeKey[1] = { + {0, Anum_pg_type_typname, NameEqualRegProcedure} + }; + + fmgr_info(NameEqualRegProcedure, + &typeKey[0].sk_func, &typeKey[0].sk_nargs); + + /* ---------------- + * check that the type is not already defined. + * ---------------- + */ + typeObjectId = TypeGet(typeName, &defined); + if (OidIsValid(typeObjectId) && defined) + { + elog(WARN, "TypeCreate: type %s already defined", typeName); + } + + /* ---------------- + * if this type has an associated elementType, then we check that + * it is defined. + * ---------------- + */ + if (elementTypeName) + { + elementObjectId = TypeGet(elementTypeName, &defined); + if (!defined) + { + elog(WARN, "TypeCreate: type %s is not defined", elementTypeName); + } + } + + /* ---------------- + * XXX comment me + * ---------------- + */ + if (externalSize == 0) + { + externalSize = -1; /* variable length */ + } + + /* ---------------- + * initialize arrays needed by FormHeapTuple + * ---------------- + */ + for (i = 0; i < Natts_pg_type; ++i) + { + nulls[i] = ' '; + replaces[i] = 'r'; + values[i] = (Datum) NULL; /* redundant, but nice */ } - } - - /* ---------------- - * XXX comment me - * ---------------- - */ - if (externalSize == 0) { - externalSize = -1; /* variable length */ - } - - /* ---------------- - * initialize arrays needed by FormHeapTuple - * ---------------- - */ - for (i = 0; i < Natts_pg_type; ++i) { - nulls[i] = ' '; - replaces[i] = 'r'; - values[i] = (Datum)NULL; /* redundant, but nice */ - } - - /* - * XXX - * - * Do this so that user-defined types have size -1 instead of zero if - * they are variable-length - this is so that everything else in the - * backend works. - */ - - if (internalSize == 0) - internalSize = -1; - - /* ---------------- - * initialize the values[] information - * ---------------- - */ - i = 0; - values[i++] = PointerGetDatum(typeName); /* 1 */ - values[i++] = (Datum) GetUserId(); /* 2 */ - values[i++] = (Datum) internalSize; /* 3 */ - values[i++] = (Datum) externalSize; /* 4 */ - values[i++] = (Datum) passedByValue; /* 5 */ - values[i++] = (Datum) typeType; /* 6 */ - values[i++] = (Datum) (bool) 1; /* 7 */ - values[i++] = (Datum) typDelim; /* 8 */ - values[i++] = (Datum) (typeType == 'c' ? relationOid : InvalidOid); /* 9 */ - values[i++] = (Datum) elementObjectId; /* 10 */ - - /* - * arguments to type input and output functions must be 0 - */ - memset(argList, 0, 8 * sizeof(Oid)); - - procs[0] = inputProcedure; - procs[1] = outputProcedure; - procs[2] = (receiveProcedure) ? receiveProcedure : inputProcedure; - procs[3] = (sendProcedure) ? sendProcedure : outputProcedure; - - for (j = 0; j < 4; ++j) { - procname = procs[j]; - - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(procname), - Int32GetDatum(1), - PointerGetDatum(argList), - 0); - - if (!HeapTupleIsValid(tup)) { - /* - * it is possible for the input/output procedure - * to take two arguments, where the second argument - * is the element type (eg array_in/array_out) - */ - if (OidIsValid(elementObjectId)) { + + /* + * XXX + * + * Do this so that user-defined types have size -1 instead of zero if + * they are variable-length - this is so that everything else in the + * backend works. + */ + + if (internalSize == 0) + internalSize = -1; + + /* ---------------- + * initialize the values[] information + * ---------------- + */ + i = 0; + values[i++] = PointerGetDatum(typeName); /* 1 */ + values[i++] = (Datum) GetUserId(); /* 2 */ + values[i++] = (Datum) internalSize; /* 3 */ + values[i++] = (Datum) externalSize; /* 4 */ + values[i++] = (Datum) passedByValue; /* 5 */ + values[i++] = (Datum) typeType; /* 6 */ + values[i++] = (Datum) (bool) 1; /* 7 */ + values[i++] = (Datum) typDelim; /* 8 */ + values[i++] = (Datum) (typeType == 'c' ? relationOid : InvalidOid); /* 9 */ + values[i++] = (Datum) elementObjectId; /* 10 */ + + /* + * arguments to type input and output functions must be 0 + */ + memset(argList, 0, 8 * sizeof(Oid)); + + procs[0] = inputProcedure; + procs[1] = outputProcedure; + procs[2] = (receiveProcedure) ? receiveProcedure : inputProcedure; + procs[3] = (sendProcedure) ? sendProcedure : outputProcedure; + + for (j = 0; j < 4; ++j) + { + procname = procs[j]; + tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(procname), - Int32GetDatum(2), - PointerGetDatum(argList), - 0); - } - if (!HeapTupleIsValid(tup)) { - func_error("TypeCreate", procname, 1, argList); - } + PointerGetDatum(procname), + Int32GetDatum(1), + PointerGetDatum(argList), + 0); + + if (!HeapTupleIsValid(tup)) + { + + /* + * it is possible for the input/output procedure to take two + * arguments, where the second argument is the element type + * (eg array_in/array_out) + */ + if (OidIsValid(elementObjectId)) + { + tup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(procname), + Int32GetDatum(2), + PointerGetDatum(argList), + 0); + } + if (!HeapTupleIsValid(tup)) + { + func_error("TypeCreate", procname, 1, argList); + } + } + + values[i++] = (Datum) tup->t_oid; /* 11 - 14 */ } - - values[i++] = (Datum)tup->t_oid; /* 11 - 14 */ - } - - /* ---------------- - * set default alignment - * ---------------- - */ - values[i++] = (Datum)alignment; /* 15 */ - - /* ---------------- - * initialize the default value for this type. - * ---------------- - */ - values[i] = (Datum)fmgr(TextInRegProcedure, /* 16 */ - PointerIsValid(defaultTypeValue) - ? defaultTypeValue : "-"); /* XXX default typdefault */ - - /* ---------------- - * open pg_type and begin a scan for the type name. - * ---------------- - */ - pg_type_desc = heap_openr(TypeRelationName); - - /* ----------------- - * Set a write lock initially so as not upgrade a read to a write - * when the heap_insert() or heap_replace() is called. - * ----------------- - */ - RelationSetLockForWrite(pg_type_desc); - - typeKey[0].sk_argument = PointerGetDatum(typeName); - pg_type_scan = heap_beginscan(pg_type_desc, - 0, - SelfTimeQual, - 1, - typeKey); - - /* ---------------- - * define the type either by adding a tuple to the type - * relation, or by updating the fields of the "shell" tuple - * already there. - * ---------------- - */ - tup = heap_getnext(pg_type_scan, 0, &buffer); - if (HeapTupleIsValid(tup)) { - tup = heap_modifytuple(tup, - buffer, - pg_type_desc, - values, - nulls, - replaces); - - /* XXX may not be necessary */ - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - - setheapoverride(true); - heap_replace(pg_type_desc, &itemPointerData, tup); - setheapoverride(false); - - typeObjectId = tup->t_oid; - } else { - tupDesc = pg_type_desc->rd_att; - tup = heap_formtuple(tupDesc, - values, - nulls); - - heap_insert(pg_type_desc, tup); - - typeObjectId = tup->t_oid; - } - - /* ---------------- - * finish up - * ---------------- - */ - heap_endscan(pg_type_scan); - - if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex) + /* ---------------- + * set default alignment + * ---------------- + */ + values[i++] = (Datum) alignment; /* 15 */ + + /* ---------------- + * initialize the default value for this type. + * ---------------- + */ + values[i] = (Datum) fmgr(TextInRegProcedure, /* 16 */ + PointerIsValid(defaultTypeValue) + ? defaultTypeValue : "-"); /* XXX default + * typdefault */ + + /* ---------------- + * open pg_type and begin a scan for the type name. + * ---------------- + */ + pg_type_desc = heap_openr(TypeRelationName); + + /* ----------------- + * Set a write lock initially so as not upgrade a read to a write + * when the heap_insert() or heap_replace() is called. + * ----------------- + */ + RelationSetLockForWrite(pg_type_desc); + + typeKey[0].sk_argument = PointerGetDatum(typeName); + pg_type_scan = heap_beginscan(pg_type_desc, + 0, + SelfTimeQual, + 1, + typeKey); + + /* ---------------- + * define the type either by adding a tuple to the type + * relation, or by updating the fields of the "shell" tuple + * already there. + * ---------------- + */ + tup = heap_getnext(pg_type_scan, 0, &buffer); + if (HeapTupleIsValid(tup)) + { + tup = heap_modifytuple(tup, + buffer, + pg_type_desc, + values, + nulls, + replaces); + + /* XXX may not be necessary */ + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + + setheapoverride(true); + heap_replace(pg_type_desc, &itemPointerData, tup); + setheapoverride(false); + + typeObjectId = tup->t_oid; + } + else + { + tupDesc = pg_type_desc->rd_att; + + tup = heap_formtuple(tupDesc, + values, + nulls); + + heap_insert(pg_type_desc, tup); + + typeObjectId = tup->t_oid; + } + + /* ---------------- + * finish up + * ---------------- + */ + heap_endscan(pg_type_scan); + + if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex) { - Relation idescs[Num_pg_type_indices]; - - CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup); - CatalogCloseIndices(Num_pg_type_indices, idescs); + Relation idescs[Num_pg_type_indices]; + + CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup); + CatalogCloseIndices(Num_pg_type_indices, idescs); } - RelationUnsetLockForWrite(pg_type_desc); - heap_close(pg_type_desc); - - - return - typeObjectId; + RelationUnsetLockForWrite(pg_type_desc); + heap_close(pg_type_desc); + + + return + typeObjectId; } /* ---------------------------------------------------------------- - * TypeRename + * TypeRename * - * This renames a type + * This renames a type * ---------------------------------------------------------------- */ void TypeRename(char *oldTypeName, char *newTypeName) { - Relation pg_type_desc; - Relation idescs[Num_pg_type_indices]; - Oid type_oid; - HeapTuple tup; - bool defined; - ItemPointerData itemPointerData; - - /* check that that the new type is not already defined */ - type_oid = TypeGet(newTypeName, &defined); - if (OidIsValid(type_oid) && defined) { - elog(WARN, "TypeRename: type %s already defined", newTypeName); - } - - /* get the type tuple from the catalog index scan manager */ - pg_type_desc = heap_openr(TypeRelationName); - tup = TypeNameIndexScan(pg_type_desc, oldTypeName); - - /* ---------------- - * change the name of the type - * ---------------- - */ - if (HeapTupleIsValid(tup)) { - - namestrcpy(& (((TypeTupleForm) GETSTRUCT(tup))->typname),newTypeName); - - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - - setheapoverride(true); - heap_replace(pg_type_desc, &itemPointerData, tup); - setheapoverride(false); - - /* update the system catalog indices */ - CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup); - CatalogCloseIndices(Num_pg_type_indices, idescs); - - /* all done */ - pfree(tup); - - } else { - elog(WARN, "TypeRename: type %s not defined", oldTypeName); - } - - /* finish up */ - heap_close(pg_type_desc); + Relation pg_type_desc; + Relation idescs[Num_pg_type_indices]; + Oid type_oid; + HeapTuple tup; + bool defined; + ItemPointerData itemPointerData; + + /* check that that the new type is not already defined */ + type_oid = TypeGet(newTypeName, &defined); + if (OidIsValid(type_oid) && defined) + { + elog(WARN, "TypeRename: type %s already defined", newTypeName); + } + + /* get the type tuple from the catalog index scan manager */ + pg_type_desc = heap_openr(TypeRelationName); + tup = TypeNameIndexScan(pg_type_desc, oldTypeName); + + /* ---------------- + * change the name of the type + * ---------------- + */ + if (HeapTupleIsValid(tup)) + { + + namestrcpy(&(((TypeTupleForm) GETSTRUCT(tup))->typname), newTypeName); + + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + + setheapoverride(true); + heap_replace(pg_type_desc, &itemPointerData, tup); + setheapoverride(false); + + /* update the system catalog indices */ + CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup); + CatalogCloseIndices(Num_pg_type_indices, idescs); + + /* all done */ + pfree(tup); + + } + else + { + elog(WARN, "TypeRename: type %s not defined", oldTypeName); + } + + /* finish up */ + heap_close(pg_type_desc); } /* * makeArrayTypeName(typeName); - * - given a base type name, make an array of type name out of it + * - given a base type name, make an array of type name out of it * - * the CALLER is responsible for pfreeing the + * the CALLER is responsible for pfreeing the */ -char* -makeArrayTypeName(char* typeName) +char * +makeArrayTypeName(char *typeName) { - char *arr; + char *arr; + + if (!typeName) + return NULL; + arr = palloc(strlen(typeName) + 2); + arr[0] = '_'; + strcpy(arr + 1, typeName); - if (!typeName) return NULL; - arr = palloc (strlen(typeName) + 2); - arr[0] = '_'; - strcpy(arr+1, typeName); + return arr; - return arr; - } diff --git a/src/backend/commands/_deadcode/version.c b/src/backend/commands/_deadcode/version.c index bac35cd4f87..c3eb6f47797 100644 --- a/src/backend/commands/_deadcode/version.c +++ b/src/backend/commands/_deadcode/version.c @@ -1,23 +1,23 @@ /*------------------------------------------------------------------------- * * version.c-- - * This file contains all the rules that govern all version semantics. + * This file contains all the rules that govern all version semantics. * * Copyright (c) 1994, Regents of the University of California * - * The version stuff has not been tested under postgres95 and probably doesn't - * work! - jolly 8/19/95 - * + * The version stuff has not been tested under postgres95 and probably doesn't + * work! - jolly 8/19/95 + * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.5 1997/08/19 21:30:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.6 1997/09/07 04:41:04 momjian Exp $ * * NOTES - * At the point the version is defined, 2 physical relations are created - * <vname>_added and <vname>_deleted. + * At the point the version is defined, 2 physical relations are created + * <vname>_added and <vname>_deleted. * - * In addition, 4 rules are defined which govern the semantics of versions - * w.r.t retrieves, appends, replaces and deletes. + * In addition, 4 rules are defined which govern the semantics of versions + * w.r.t retrieves, appends, replaces and deletes. * *------------------------------------------------------------------------- */ @@ -34,29 +34,31 @@ #define MAX_QUERY_LEN 1024 -char rule_buf[MAX_QUERY_LEN]; +char rule_buf[MAX_QUERY_LEN]; + #ifdef NOT_USED -static char attr_list[MAX_QUERY_LEN]; +static char attr_list[MAX_QUERY_LEN]; + #endif /* * problem: the version system assumes that the rules it declares will - * be fired in the order of declaration, it also assumes - * goh's silly instead semantics. Unfortunately, it is a pain - * to make the version system work with the new semantics. - * However the whole problem can be solved, and some nice - * functionality can be achieved if we get multiple action rules - * to work. So thats what I did -- glass + * be fired in the order of declaration, it also assumes + * goh's silly instead semantics. Unfortunately, it is a pain + * to make the version system work with the new semantics. + * However the whole problem can be solved, and some nice + * functionality can be achieved if we get multiple action rules + * to work. So thats what I did -- glass * * Well, at least they've been working for about 20 minutes. - * + * * So any comments in this code about 1 rule per transction are false...:) * */ /* - * This is needed because the rule system only allows - * *1* rule to be defined per transaction. + * This is needed because the rule system only allows + * *1* rule to be defined per transaction. * * NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO @@ -80,267 +82,282 @@ static char attr_list[MAX_QUERY_LEN]; * a strange memory bug instead of watching the "Get Smart" marathon * in NICK ! * DO NOT COMMIT THE XACT, just increase the Cid counter! - * _sp. + * _sp. */ #ifdef NOT_USED static void eval_as_new_xact(char *query) { - /* WARNING! do not uncomment the following lines WARNING! - * CommitTransactionCommand(); - * StartTransactionCommand(); - */ - CommandCounterIncrement(); - pg_eval(query, (char **) NULL, (Oid *) NULL, 0); + + /* + * WARNING! do not uncomment the following lines WARNING! + * CommitTransactionCommand(); StartTransactionCommand(); + */ + CommandCounterIncrement(); + pg_eval(query, (char **) NULL, (Oid *) NULL, 0); } + #endif /* - * Define a version. + * Define a version. */ #ifdef NOT_USED void DefineVersion(char *name, char *fromRelname, char *date) { - char *bname; - static char saved_basename[512]; - static char saved_snapshot[512]; - - if (date == NULL) { - /* no time ranges */ - bname = fromRelname; - strcpy(saved_basename, (char *) bname); - *saved_snapshot = (char)NULL; - } else { - /* version is a snapshot */ - bname = fromRelname; - strcpy(saved_basename, (char *) bname); - sprintf(saved_snapshot, "['%s']", date); - } - - - /* - * Calls the routine ``GetAttrList'' get the list of attributes - * from the base relation. - * Code is put here so that we only need to look up the attribute once for - * both appends and replaces. - */ - setAttrList(bname); - - VersionCreate (name, saved_basename); - VersionAppend (name, saved_basename); - VersionDelete (name, saved_basename,saved_snapshot); - VersionReplace (name, saved_basename,saved_snapshot); - VersionRetrieve (name, saved_basename, saved_snapshot); + char *bname; + static char saved_basename[512]; + static char saved_snapshot[512]; + + if (date == NULL) + { + /* no time ranges */ + bname = fromRelname; + strcpy(saved_basename, (char *) bname); + *saved_snapshot = (char) NULL; + } + else + { + /* version is a snapshot */ + bname = fromRelname; + strcpy(saved_basename, (char *) bname); + sprintf(saved_snapshot, "['%s']", date); + } + + + /* + * Calls the routine ``GetAttrList'' get the list of attributes from + * the base relation. Code is put here so that we only need to look up + * the attribute once for both appends and replaces. + */ + setAttrList(bname); + + VersionCreate(name, saved_basename); + VersionAppend(name, saved_basename); + VersionDelete(name, saved_basename, saved_snapshot); + VersionReplace(name, saved_basename, saved_snapshot); + VersionRetrieve(name, saved_basename, saved_snapshot); } + #endif /* - * Creates the deltas. + * Creates the deltas. */ #ifdef NOT_USED void VersionCreate(char *vname, char *bname) { - static char query_buf [MAX_QUERY_LEN]; - - /* - * Creating the dummy version relation for triggering rules. - */ - sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2", - vname, bname); - - pg_eval (query_buf, (char **) NULL, (Oid *) NULL, 0); - - /* - * Creating the ``v_added'' relation - */ - sprintf (query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2", - vname, bname); - eval_as_new_xact (query_buf); - - /* - * Creating the ``v_deleted'' relation. - */ - sprintf (query_buf, "CREATE TABLE %s_del (DOID oid)", vname); - eval_as_new_xact (query_buf); + static char query_buf[MAX_QUERY_LEN]; + + /* + * Creating the dummy version relation for triggering rules. + */ + sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2", + vname, bname); + + pg_eval(query_buf, (char **) NULL, (Oid *) NULL, 0); + + /* + * Creating the ``v_added'' relation + */ + sprintf(query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2", + vname, bname); + eval_as_new_xact(query_buf); + + /* + * Creating the ``v_deleted'' relation. + */ + sprintf(query_buf, "CREATE TABLE %s_del (DOID oid)", vname); + eval_as_new_xact(query_buf); } + #endif /* * Given the relation name, does a catalog lookup for that relation and * sets the global variable 'attr_list' with the list of attributes (names) - * for that relation. + * for that relation. */ #ifdef NOT_USED static void setAttrList(char *bname) { - Relation rdesc; - int i = 0; - int maxattrs = 0; - char *attrname; - char temp_buf[512]; - int notfirst = 0; - - rdesc = heap_openr(bname); - if (rdesc == NULL ) { - elog(WARN,"Unable to expand all -- amopenr failed "); - return; - } - maxattrs = RelationGetNumberOfAttributes(rdesc); - - attr_list[0] = '\0'; - - for ( i = maxattrs-1 ; i > -1 ; --i ) { - attrname = (rdesc->rd_att->attrs[i]->attname).data; - - if (notfirst == 1) { - sprintf(temp_buf, ", %s = new.%s", attrname, attrname); - } else { - sprintf(temp_buf, "%s = new.%s", attrname, attrname); - notfirst = 1; + Relation rdesc; + int i = 0; + int maxattrs = 0; + char *attrname; + char temp_buf[512]; + int notfirst = 0; + + rdesc = heap_openr(bname); + if (rdesc == NULL) + { + elog(WARN, "Unable to expand all -- amopenr failed "); + return; + } + maxattrs = RelationGetNumberOfAttributes(rdesc); + + attr_list[0] = '\0'; + + for (i = maxattrs - 1; i > -1; --i) + { + attrname = (rdesc->rd_att->attrs[i]->attname).data; + + if (notfirst == 1) + { + sprintf(temp_buf, ", %s = new.%s", attrname, attrname); + } + else + { + sprintf(temp_buf, "%s = new.%s", attrname, attrname); + notfirst = 1; + } + strcat(attr_list, temp_buf); } - strcat(attr_list, temp_buf); - } - - heap_close(rdesc); - - return; + + heap_close(rdesc); + + return; } + #endif /* * This routine defines the rule governing the append semantics of - * versions. All tuples appended to a version gets appended to the + * versions. All tuples appended to a version gets appended to the * <vname>_added relation. */ #ifdef NOT_USED static void VersionAppend(char *vname, char *bname) { - sprintf(rule_buf, - "define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)", - vname, vname, vname, attr_list); - - eval_as_new_xact(rule_buf); + sprintf(rule_buf, + "define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)", + vname, vname, vname, attr_list); + + eval_as_new_xact(rule_buf); } + #endif /* * This routine defines the rule governing the retrieval semantics of * versions. To retrieve tuples from a version , we need to: * - * 1. Retrieve all tuples in the <vname>_added relation. - * 2. Retrieve all tuples in the base relation which are not in - * the <vname>_del relation. + * 1. Retrieve all tuples in the <vname>_added relation. + * 2. Retrieve all tuples in the base relation which are not in + * the <vname>_del relation. */ #ifdef NOT_USED void VersionRetrieve(char *vname, char *bname, char *snapshot) { - - sprintf(rule_buf, - "define rewrite rule %s_retrieve is on SELECT to %s do instead\n\ + + sprintf(rule_buf, + "define rewrite rule %s_retrieve is on SELECT to %s do instead\n\ SELECT %s_1.oid, %s_1.* from _%s in %s%s, %s_1 in (%s_added | _%s) \ where _%s.oid !!= '%s_del.DOID'", - vname, vname, vname, vname, bname, - bname, snapshot, - vname, vname, bname, bname, vname); - - eval_as_new_xact(rule_buf); - - /* printf("%s\n",rule_buf); */ - + vname, vname, vname, vname, bname, + bname, snapshot, + vname, vname, bname, bname, vname); + + eval_as_new_xact(rule_buf); + + /* printf("%s\n",rule_buf); */ + } + #endif /* - * This routine defines the rules that govern the delete semantics of + * This routine defines the rules that govern the delete semantics of * versions. Two things happens when we delete a tuple from a version: * - * 1. If the tuple to be deleted was added to the version *after* - * the version was created, then we simply delete the tuple - * from the <vname>_added relation. - * 2. If the tuple to be deleted is actually in the base relation, - * then we have to mark that tuple as being deleted by adding - * it to the <vname>_del relation. + * 1. If the tuple to be deleted was added to the version *after* + * the version was created, then we simply delete the tuple + * from the <vname>_added relation. + * 2. If the tuple to be deleted is actually in the base relation, + * then we have to mark that tuple as being deleted by adding + * it to the <vname>_del relation. */ #ifdef NOT_USED void VersionDelete(char *vname, char *bname, char *snapshot) { - - sprintf(rule_buf, - "define rewrite rule %s_delete1 is on delete to %s do instead\n \ + + sprintf(rule_buf, + "define rewrite rule %s_delete1 is on delete to %s do instead\n \ [delete %s_added where current.oid = %s_added.oid\n \ append %s_del(DOID = current.oid) from _%s in %s%s \ where current.oid = _%s.oid] \n", - vname,vname,vname,vname,vname, -bname,bname,snapshot,bname); + vname, vname, vname, vname, vname, + bname, bname, snapshot, bname); - eval_as_new_xact(rule_buf); + eval_as_new_xact(rule_buf); #ifdef OLD_REWRITE - sprintf(rule_buf, - "define rewrite rule %s_delete2 is on delete to %s do instead \n \ + sprintf(rule_buf, + "define rewrite rule %s_delete2 is on delete to %s do instead \n \ append %s_del(DOID = current.oid) from _%s in %s%s \ where current.oid = _%s.oid \n", - vname,vname,vname,bname,bname,snapshot,bname); + vname, vname, vname, bname, bname, snapshot, bname); - eval_as_new_xact(rule_buf); -#endif /* OLD_REWRITE */ + eval_as_new_xact(rule_buf); +#endif /* OLD_REWRITE */ } + #endif /* - * This routine defines the rules that govern the update semantics - * of versions. To update a tuple in a version: + * This routine defines the rules that govern the update semantics + * of versions. To update a tuple in a version: * - * 1. If the tuple is in <vname>_added, we simply ``replace'' - * the tuple (as per postgres style). - * 2. if the tuple is in the base relation, then two things have to - * happen: - * 2.1 The tuple is marked ``deleted'' from the base relation by - * adding the tuple to the <vname>_del relation. - * 2.2 A copy of the tuple is appended to the <vname>_added relation + * 1. If the tuple is in <vname>_added, we simply ``replace'' + * the tuple (as per postgres style). + * 2. if the tuple is in the base relation, then two things have to + * happen: + * 2.1 The tuple is marked ``deleted'' from the base relation by + * adding the tuple to the <vname>_del relation. + * 2.2 A copy of the tuple is appended to the <vname>_added relation */ #ifdef NOT_USED void VersionReplace(char *vname, char *bname, char *snapshot) { - sprintf(rule_buf, - "define rewrite rule %s_replace1 is on replace to %s do instead \n\ + sprintf(rule_buf, + "define rewrite rule %s_replace1 is on replace to %s do instead \n\ [replace %s_added(%s) where current.oid = %s_added.oid \n\ append %s_del(DOID = current.oid) from _%s in %s%s \ where current.oid = _%s.oid\n\ append %s_added(%s) from _%s in %s%s \ where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]\n", - vname,vname,vname,attr_list,vname, - vname,bname,bname,snapshot,bname, -vname,attr_list,bname,bname,snapshot,vname,bname); + vname, vname, vname, attr_list, vname, + vname, bname, bname, snapshot, bname, + vname, attr_list, bname, bname, snapshot, vname, bname); - eval_as_new_xact(rule_buf); + eval_as_new_xact(rule_buf); -/* printf("%s\n",rule_buf); */ +/* printf("%s\n",rule_buf); */ #ifdef OLD_REWRITE - sprintf(rule_buf, - "define rewrite rule %s_replace2 is on replace to %s do \n\ + sprintf(rule_buf, + "define rewrite rule %s_replace2 is on replace to %s do \n\ append %s_del(DOID = current.oid) from _%s in %s%s \ where current.oid = _%s.oid\n", - vname,vname,vname,bname,bname,snapshot,bname); + vname, vname, vname, bname, bname, snapshot, bname); - eval_as_new_xact(rule_buf); + eval_as_new_xact(rule_buf); - sprintf(rule_buf, - "define rewrite rule %s_replace3 is on replace to %s do instead\n\ + sprintf(rule_buf, + "define rewrite rule %s_replace3 is on replace to %s do instead\n\ append %s_added(%s) from _%s in %s%s \ where current.oid !!= '%s_added.oid' and current.oid = \ _%s.oid\n", - vname,vname, vname,attr_list,bname,bname,snapshot,vname,bname); + vname, vname, vname, attr_list, bname, bname, snapshot, vname, bname); - eval_as_new_xact(rule_buf); -#endif /* OLD_REWRITE */ -/* printf("%s\n",rule_buf); */ + eval_as_new_xact(rule_buf); +#endif /* OLD_REWRITE */ +/* printf("%s\n",rule_buf); */ } diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 8a1e6d59b57..42d440a8676 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -1,35 +1,35 @@ /*------------------------------------------------------------------------- * * async.c-- - * Asynchronous notification + * Asynchronous notification * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.17 1997/08/19 21:30:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.18 1997/09/07 04:40:35 momjian Exp $ * *------------------------------------------------------------------------- */ /* New Async Notification Model: * 1. Multiple backends on same machine. Multiple backends listening on - * one relation. + * one relation. * * 2. One of the backend does a 'notify <relname>'. For all backends that - * are listening to this relation (all notifications take place at the - * end of commit), - * 2.a If the process is the same as the backend process that issued - * notification (we are notifying something that we are listening), - * signal the corresponding frontend over the comm channel using the - * out-of-band channel. - * 2.b For all other listening processes, we send kill(2) to wake up - * the listening backend. + * are listening to this relation (all notifications take place at the + * end of commit), + * 2.a If the process is the same as the backend process that issued + * notification (we are notifying something that we are listening), + * signal the corresponding frontend over the comm channel using the + * out-of-band channel. + * 2.b For all other listening processes, we send kill(2) to wake up + * the listening backend. * 3. Upon receiving a kill(2) signal from another backend process notifying - * that one of the relation that we are listening is being notified, - * we can be in either of two following states: - * 3.a We are sleeping, wake up and signal our frontend. - * 3.b We are in middle of another transaction, wait until the end of - * of the current transaction and signal our frontend. + * that one of the relation that we are listening is being notified, + * we can be in either of two following states: + * 3.a We are sleeping, wake up and signal our frontend. + * 3.b We are in middle of another transaction, wait until the end of + * of the current transaction and signal our frontend. * 4. Each frontend receives this notification and prcesses accordingly. * * -- jw, 12/28/93 @@ -42,16 +42,16 @@ * Model is: * 1. Multiple backends on same machine. * - * 2. Query on one backend sends stuff over an asynchronous portal by - * appending to a relation, and then doing an async. notification - * (which takes place after commit) to all listeners on this relation. + * 2. Query on one backend sends stuff over an asynchronous portal by + * appending to a relation, and then doing an async. notification + * (which takes place after commit) to all listeners on this relation. * - * 3. Async. notification results in all backends listening on relation - * to be woken up, by a process signal kill(2), with name of relation - * passed in shared memory. + * 3. Async. notification results in all backends listening on relation + * to be woken up, by a process signal kill(2), with name of relation + * passed in shared memory. * * 4. Each backend notifies its respective frontend over the comm - * channel using the out-of-band channel. + * channel using the out-of-band channel. * * 5. Each frontend receives this notification and processes accordingly. * @@ -62,7 +62,7 @@ #include <signal.h> #include <string.h> #include <errno.h> -#include <sys/types.h> /* Needed by in.h on Ultrix */ +#include <sys/types.h> /* Needed by in.h on Ultrix */ #include <netinet/in.h> #include <postgres.h> @@ -75,546 +75,585 @@ #include <catalog/pg_proc.h> #include <catalog/catname.h> #include <catalog/pg_listener.h> -#include <access/heapam.h> +#include <access/heapam.h> #include <storage/bufmgr.h> #include <nodes/memnodes.h> #include <utils/mcxt.h> #include <commands/async.h> #include <libpq/libpq.h> -#include <port-protos.h> /* for strdup() */ +#include <port-protos.h> /* for strdup() */ #include <storage/lmgr.h> -static int notifyFrontEndPending = 0; -static int notifyIssued = 0; -static Dllist *pendingNotifies = NULL; +static int notifyFrontEndPending = 0; +static int notifyIssued = 0; +static Dllist *pendingNotifies = NULL; -static int AsyncExistsPendingNotify(char *); -static void ClearPendingNotify(void); -static void Async_NotifyFrontEnd(void); -static void Async_Unlisten(char *relname, int pid); -static void Async_UnlistenOnExit(int code, char *relname); - +static int AsyncExistsPendingNotify(char *); +static void ClearPendingNotify(void); +static void Async_NotifyFrontEnd(void); +static void Async_Unlisten(char *relname, int pid); +static void Async_UnlistenOnExit(int code, char *relname); + /* *-------------------------------------------------------------- * Async_NotifyHandler -- * - * This is the signal handler for SIGUSR2. When the backend - * is signaled, the backend can be in two states. - * 1. If the backend is in the middle of another transaction, - * we set the flag, notifyFrontEndPending, and wait until - * the end of the transaction to notify the front end. - * 2. If the backend is not in the middle of another transaction, - * we notify the front end immediately. + * This is the signal handler for SIGUSR2. When the backend + * is signaled, the backend can be in two states. + * 1. If the backend is in the middle of another transaction, + * we set the flag, notifyFrontEndPending, and wait until + * the end of the transaction to notify the front end. + * 2. If the backend is not in the middle of another transaction, + * we notify the front end immediately. * - * -- jw, 12/28/93 + * -- jw, 12/28/93 * Results: - * none + * none * * Side effects: - * none + * none */ void Async_NotifyHandler(SIGNAL_ARGS) { - extern TransactionState CurrentTransactionState; - - if ((CurrentTransactionState->state == TRANS_DEFAULT) && - (CurrentTransactionState->blockState == TRANS_DEFAULT)) { + extern TransactionState CurrentTransactionState; + + if ((CurrentTransactionState->state == TRANS_DEFAULT) && + (CurrentTransactionState->blockState == TRANS_DEFAULT)) + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Waking up sleeping backend process"); + elog(DEBUG, "Waking up sleeping backend process"); #endif - Async_NotifyFrontEnd(); + Async_NotifyFrontEnd(); - }else { + } + else + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d", - CurrentTransactionState->state, - CurrentTransactionState->blockState); + elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d", + CurrentTransactionState->state, + CurrentTransactionState->blockState); #endif - notifyFrontEndPending = 1; - } + notifyFrontEndPending = 1; + } } /* *-------------------------------------------------------------- * Async_Notify -- * - * Adds the relation to the list of pending notifies. - * All notification happens at end of commit. - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * Adds the relation to the list of pending notifies. + * All notification happens at end of commit. + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * - * All notification of backend processes happens here, - * then each backend notifies its corresponding front end at - * the end of commit. + * All notification of backend processes happens here, + * then each backend notifies its corresponding front end at + * the end of commit. * - * This correspond to 'notify <relname>' command - * -- jw, 12/28/93 + * This correspond to 'notify <relname>' command + * -- jw, 12/28/93 * * Results: - * XXX + * XXX * * Side effects: - * All tuples for relname in pg_listener are updated. + * All tuples for relname in pg_listener are updated. * *-------------------------------------------------------------- */ void Async_Notify(char *relname) { - - HeapTuple lTuple, rTuple; - Relation lRel; - HeapScanDesc sRel; - TupleDesc tdesc; - ScanKeyData key; - Buffer b; - Datum d, value[3]; - bool isnull; - char repl[3], nulls[3]; - - char *notifyName; - + + HeapTuple lTuple, + rTuple; + Relation lRel; + HeapScanDesc sRel; + TupleDesc tdesc; + ScanKeyData key; + Buffer b; + Datum d, + value[3]; + bool isnull; + char repl[3], + nulls[3]; + + char *notifyName; + #ifdef ASYNC_DEBUG - elog(DEBUG,"Async_Notify: %s",relname); + elog(DEBUG, "Async_Notify: %s", relname); #endif - - if (!pendingNotifies) - pendingNotifies = DLNewList(); - - /* - * Allocate memory from the global malloc pool because it needs to be - * referenced also when the transaction is finished. DZ - 26-08-1996 - */ - notifyName = strdup(relname); - DLAddHead(pendingNotifies, DLNewElem(notifyName)); - - ScanKeyEntryInitialize(&key, 0, - Anum_pg_listener_relname, - NameEqualRegProcedure, - PointerGetDatum(notifyName)); - - lRel = heap_openr(ListenerRelationName); - tdesc = RelationGetTupleDescriptor(lRel); - RelationSetLockForWrite(lRel); - sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key); - - nulls[0] = nulls[1] = nulls[2] = ' '; - repl[0] = repl[1] = repl[2] = ' '; - repl[Anum_pg_listener_notify - 1] = 'r'; - value[0] = value[1] = value[2] = (Datum) 0; - value[Anum_pg_listener_notify - 1] = Int32GetDatum(1); - - while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b))) { - d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_notify, - tdesc, &isnull); - if (!DatumGetInt32(d)) { - rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl); - heap_replace(lRel, &lTuple->t_ctid, rTuple); + + if (!pendingNotifies) + pendingNotifies = DLNewList(); + + /* + * Allocate memory from the global malloc pool because it needs to be + * referenced also when the transaction is finished. DZ - 26-08-1996 + */ + notifyName = strdup(relname); + DLAddHead(pendingNotifies, DLNewElem(notifyName)); + + ScanKeyEntryInitialize(&key, 0, + Anum_pg_listener_relname, + NameEqualRegProcedure, + PointerGetDatum(notifyName)); + + lRel = heap_openr(ListenerRelationName); + tdesc = RelationGetTupleDescriptor(lRel); + RelationSetLockForWrite(lRel); + sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key); + + nulls[0] = nulls[1] = nulls[2] = ' '; + repl[0] = repl[1] = repl[2] = ' '; + repl[Anum_pg_listener_notify - 1] = 'r'; + value[0] = value[1] = value[2] = (Datum) 0; + value[Anum_pg_listener_notify - 1] = Int32GetDatum(1); + + while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b))) + { + d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_notify, + tdesc, &isnull); + if (!DatumGetInt32(d)) + { + rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl); + heap_replace(lRel, &lTuple->t_ctid, rTuple); + } + ReleaseBuffer(b); } - ReleaseBuffer(b); - } - heap_endscan(sRel); - RelationUnsetLockForWrite(lRel); - heap_close(lRel); - notifyIssued = 1; + heap_endscan(sRel); + RelationUnsetLockForWrite(lRel); + heap_close(lRel); + notifyIssued = 1; } /* *-------------------------------------------------------------- * Async_NotifyAtCommit -- * - * Signal our corresponding frontend process on relations that - * were notified. Signal all other backend process that - * are listening also. + * Signal our corresponding frontend process on relations that + * were notified. Signal all other backend process that + * are listening also. * - * -- jw, 12/28/93 + * -- jw, 12/28/93 * * Results: - * XXX + * XXX * * Side effects: - * Tuples in pg_listener that has our listenerpid are updated so - * that the notification is 0. We do not want to notify frontend - * more than once. + * Tuples in pg_listener that has our listenerpid are updated so + * that the notification is 0. We do not want to notify frontend + * more than once. * - * -- jw, 12/28/93 + * -- jw, 12/28/93 * *-------------------------------------------------------------- */ void Async_NotifyAtCommit() { - HeapTuple lTuple; - Relation lRel; - HeapScanDesc sRel; - TupleDesc tdesc; - ScanKeyData key; - Datum d; - int ourpid; - bool isnull; - Buffer b; - extern TransactionState CurrentTransactionState; - - if (!pendingNotifies) - pendingNotifies = DLNewList(); - - if ((CurrentTransactionState->state == TRANS_DEFAULT) && - (CurrentTransactionState->blockState == TRANS_DEFAULT)) { - - if (notifyIssued) { /* 'notify <relname>' issued by us */ - notifyIssued = 0; - StartTransactionCommand(); + HeapTuple lTuple; + Relation lRel; + HeapScanDesc sRel; + TupleDesc tdesc; + ScanKeyData key; + Datum d; + int ourpid; + bool isnull; + Buffer b; + extern TransactionState CurrentTransactionState; + + if (!pendingNotifies) + pendingNotifies = DLNewList(); + + if ((CurrentTransactionState->state == TRANS_DEFAULT) && + (CurrentTransactionState->blockState == TRANS_DEFAULT)) + { + + if (notifyIssued) + { /* 'notify <relname>' issued by us */ + notifyIssued = 0; + StartTransactionCommand(); #ifdef ASYNC_DEBUG - elog(DEBUG, "Async_NotifyAtCommit."); + elog(DEBUG, "Async_NotifyAtCommit."); #endif - ScanKeyEntryInitialize(&key, 0, - Anum_pg_listener_notify, - Integer32EqualRegProcedure, - Int32GetDatum(1)); - lRel = heap_openr(ListenerRelationName); - RelationSetLockForWrite(lRel); - sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key); - tdesc = RelationGetTupleDescriptor(lRel); - ourpid = getpid(); - - while (HeapTupleIsValid(lTuple = heap_getnext(sRel,0, &b))) { - d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname, - tdesc, &isnull); - - if (AsyncExistsPendingNotify((char *) DatumGetPointer(d))) { - d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_pid, - tdesc, &isnull); - - if (ourpid == DatumGetInt32(d)) { + ScanKeyEntryInitialize(&key, 0, + Anum_pg_listener_notify, + Integer32EqualRegProcedure, + Int32GetDatum(1)); + lRel = heap_openr(ListenerRelationName); + RelationSetLockForWrite(lRel); + sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key); + tdesc = RelationGetTupleDescriptor(lRel); + ourpid = getpid(); + + while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b))) + { + d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname, + tdesc, &isnull); + + if (AsyncExistsPendingNotify((char *) DatumGetPointer(d))) + { + d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_pid, + tdesc, &isnull); + + if (ourpid == DatumGetInt32(d)) + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1"); + elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1"); #endif - notifyFrontEndPending = 1; - } else { + notifyFrontEndPending = 1; + } + else + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Notifying others"); + elog(DEBUG, "Notifying others"); #endif #ifdef HAVE_KILL - if (kill(DatumGetInt32(d), SIGUSR2) < 0) { - if (errno == ESRCH) { - heap_delete(lRel, &lTuple->t_ctid); - } + if (kill(DatumGetInt32(d), SIGUSR2) < 0) + { + if (errno == ESRCH) + { + heap_delete(lRel, &lTuple->t_ctid); + } + } +#endif + } + } + ReleaseBuffer(b); } -#endif - } + heap_endscan(sRel); + RelationUnsetLockForWrite(lRel); + heap_close(lRel); + + CommitTransactionCommand(); + ClearPendingNotify(); } - ReleaseBuffer(b); - } - heap_endscan(sRel); - RelationUnsetLockForWrite(lRel); - heap_close(lRel); - CommitTransactionCommand(); - ClearPendingNotify(); - } - - if (notifyFrontEndPending) { /* we need to notify the frontend of - all pending notifies. */ - notifyFrontEndPending = 1; - Async_NotifyFrontEnd(); + if (notifyFrontEndPending) + { /* we need to notify the frontend of all + * pending notifies. */ + notifyFrontEndPending = 1; + Async_NotifyFrontEnd(); + } } - } } /* *-------------------------------------------------------------- * Async_NotifyAtAbort -- * - * Gets rid of pending notifies. List elements are automatically - * freed through memory context. - * + * Gets rid of pending notifies. List elements are automatically + * freed through memory context. + * * * Results: - * XXX + * XXX * * Side effects: - * XXX + * XXX * *-------------------------------------------------------------- */ void Async_NotifyAtAbort() { - extern TransactionState CurrentTransactionState; - - if (notifyIssued) { - ClearPendingNotify(); - } - notifyIssued = 0; - if (pendingNotifies) - DLFreeList(pendingNotifies); - pendingNotifies = DLNewList(); - - if ((CurrentTransactionState->state == TRANS_DEFAULT) && - (CurrentTransactionState->blockState == TRANS_DEFAULT)) { - if (notifyFrontEndPending) { /* don't forget to notify front end */ - Async_NotifyFrontEnd(); + extern TransactionState CurrentTransactionState; + + if (notifyIssued) + { + ClearPendingNotify(); + } + notifyIssued = 0; + if (pendingNotifies) + DLFreeList(pendingNotifies); + pendingNotifies = DLNewList(); + + if ((CurrentTransactionState->state == TRANS_DEFAULT) && + (CurrentTransactionState->blockState == TRANS_DEFAULT)) + { + if (notifyFrontEndPending) + { /* don't forget to notify front end */ + Async_NotifyFrontEnd(); + } } - } } /* *-------------------------------------------------------------- * Async_Listen -- * - * Register a backend (identified by its Unix PID) as listening - * on the specified relation. + * Register a backend (identified by its Unix PID) as listening + * on the specified relation. * - * This corresponds to the 'listen <relation>' command in SQL + * This corresponds to the 'listen <relation>' command in SQL * - * One listener per relation, pg_listener relation is keyed - * on (relname,pid) to provide multiple listeners in future. + * One listener per relation, pg_listener relation is keyed + * on (relname,pid) to provide multiple listeners in future. * * Results: - * pg_listeners is updated. + * pg_listeners is updated. * * Side effects: - * XXX + * XXX * *-------------------------------------------------------------- */ void Async_Listen(char *relname, int pid) { - Datum values[Natts_pg_listener]; - char nulls[Natts_pg_listener]; - TupleDesc tdesc; - HeapScanDesc s; - HeapTuple htup,tup; - Relation lDesc; - Buffer b; - Datum d; - int i; - bool isnull; - int alreadyListener = 0; - int ourPid = getpid(); - char *relnamei; - TupleDesc tupDesc; - + Datum values[Natts_pg_listener]; + char nulls[Natts_pg_listener]; + TupleDesc tdesc; + HeapScanDesc s; + HeapTuple htup, + tup; + Relation lDesc; + Buffer b; + Datum d; + int i; + bool isnull; + int alreadyListener = 0; + int ourPid = getpid(); + char *relnamei; + TupleDesc tupDesc; + #ifdef ASYNC_DEBUG - elog(DEBUG,"Async_Listen: %s",relname); + elog(DEBUG, "Async_Listen: %s", relname); #endif - for (i = 0 ; i < Natts_pg_listener; i++) { - nulls[i] = ' '; - values[i] = PointerGetDatum(NULL); - } - - i = 0; - values[i++] = (Datum) relname; - values[i++] = (Datum) pid; - values[i++] = (Datum) 0; /* no notifies pending */ - - lDesc = heap_openr(ListenerRelationName); - RelationSetLockForWrite(lDesc); - - /* is someone already listening. One listener per relation */ - tdesc = RelationGetTupleDescriptor(lDesc); - s = heap_beginscan(lDesc,0,NowTimeQual,0,(ScanKey)NULL); - while (HeapTupleIsValid(htup = heap_getnext(s,0,&b))) { - d = (Datum) heap_getattr(htup,b,Anum_pg_listener_relname,tdesc, - &isnull); - relnamei = DatumGetPointer(d); - if (!strncmp(relnamei,relname, NAMEDATALEN)) { - d = (Datum) heap_getattr(htup,b,Anum_pg_listener_pid,tdesc,&isnull); - pid = DatumGetInt32(d); - if (pid == ourPid) { - alreadyListener = 1; - } + for (i = 0; i < Natts_pg_listener; i++) + { + nulls[i] = ' '; + values[i] = PointerGetDatum(NULL); } - ReleaseBuffer(b); - } - heap_endscan(s); - - if (alreadyListener) { - elog(NOTICE, "Async_Listen: We are already listening on %s", - relname); - return; - } - - tupDesc = lDesc->rd_att; - tup = heap_formtuple(tupDesc, - values, - nulls); - heap_insert(lDesc, tup); - - pfree(tup); - /* if (alreadyListener) { - elog(NOTICE,"Async_Listen: already one listener on %s (possibly dead)",relname); - }*/ - - RelationUnsetLockForWrite(lDesc); - heap_close(lDesc); - - /* - * now that we are listening, we should make a note to ourselves - * to unlisten prior to dying. - */ - relnamei = malloc(NAMEDATALEN); /* persists to process exit */ - strNcpy(relnamei, relname, NAMEDATALEN-1); - on_exitpg(Async_UnlistenOnExit, (caddr_t) relnamei); + + i = 0; + values[i++] = (Datum) relname; + values[i++] = (Datum) pid; + values[i++] = (Datum) 0; /* no notifies pending */ + + lDesc = heap_openr(ListenerRelationName); + RelationSetLockForWrite(lDesc); + + /* is someone already listening. One listener per relation */ + tdesc = RelationGetTupleDescriptor(lDesc); + s = heap_beginscan(lDesc, 0, NowTimeQual, 0, (ScanKey) NULL); + while (HeapTupleIsValid(htup = heap_getnext(s, 0, &b))) + { + d = (Datum) heap_getattr(htup, b, Anum_pg_listener_relname, tdesc, + &isnull); + relnamei = DatumGetPointer(d); + if (!strncmp(relnamei, relname, NAMEDATALEN)) + { + d = (Datum) heap_getattr(htup, b, Anum_pg_listener_pid, tdesc, &isnull); + pid = DatumGetInt32(d); + if (pid == ourPid) + { + alreadyListener = 1; + } + } + ReleaseBuffer(b); + } + heap_endscan(s); + + if (alreadyListener) + { + elog(NOTICE, "Async_Listen: We are already listening on %s", + relname); + return; + } + + tupDesc = lDesc->rd_att; + tup = heap_formtuple(tupDesc, + values, + nulls); + heap_insert(lDesc, tup); + + pfree(tup); + + /* + * if (alreadyListener) { elog(NOTICE,"Async_Listen: already one + * listener on %s (possibly dead)",relname); } + */ + + RelationUnsetLockForWrite(lDesc); + heap_close(lDesc); + + /* + * now that we are listening, we should make a note to ourselves to + * unlisten prior to dying. + */ + relnamei = malloc(NAMEDATALEN); /* persists to process exit */ + strNcpy(relnamei, relname, NAMEDATALEN - 1); + on_exitpg(Async_UnlistenOnExit, (caddr_t) relnamei); } /* *-------------------------------------------------------------- * Async_Unlisten -- * - * Remove the backend from the list of listening backends - * for the specified relation. - * - * This would correspond to the 'unlisten <relation>' - * command, but there isn't one yet. + * Remove the backend from the list of listening backends + * for the specified relation. + * + * This would correspond to the 'unlisten <relation>' + * command, but there isn't one yet. * * Results: - * pg_listeners is updated. + * pg_listeners is updated. * * Side effects: - * XXX + * XXX * *-------------------------------------------------------------- */ static void Async_Unlisten(char *relname, int pid) { - Relation lDesc; - HeapTuple lTuple; - - lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname), - Int32GetDatum(pid), - 0,0); - lDesc = heap_openr(ListenerRelationName); - RelationSetLockForWrite(lDesc); - - if (lTuple != NULL) { - heap_delete(lDesc,&lTuple->t_ctid); - } - - RelationUnsetLockForWrite(lDesc); - heap_close(lDesc); + Relation lDesc; + HeapTuple lTuple; + + lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname), + Int32GetDatum(pid), + 0, 0); + lDesc = heap_openr(ListenerRelationName); + RelationSetLockForWrite(lDesc); + + if (lTuple != NULL) + { + heap_delete(lDesc, &lTuple->t_ctid); + } + + RelationUnsetLockForWrite(lDesc); + heap_close(lDesc); } static void Async_UnlistenOnExit(int code, /* from exitpg */ - char *relname) + char *relname) { - Async_Unlisten((char *) relname, getpid()); + Async_Unlisten((char *) relname, getpid()); } /* * -------------------------------------------------------------- * Async_NotifyFrontEnd -- * - * Perform an asynchronous notification to front end over - * portal comm channel. The name of the relation which contains the - * data is sent to the front end. + * Perform an asynchronous notification to front end over + * portal comm channel. The name of the relation which contains the + * data is sent to the front end. * - * We remove the notification flag from the pg_listener tuple - * associated with our process. + * We remove the notification flag from the pg_listener tuple + * associated with our process. * * Results: - * XXX + * XXX * * Side effects: * - * We make use of the out-of-band channel to transmit the - * notification to the front end. The actual data transfer takes - * place at the front end's request. + * We make use of the out-of-band channel to transmit the + * notification to the front end. The actual data transfer takes + * place at the front end's request. * * -------------------------------------------------------------- */ -GlobalMemory notifyContext = NULL; +GlobalMemory notifyContext = NULL; static void Async_NotifyFrontEnd() { - extern CommandDest whereToSendOutput; - HeapTuple lTuple, rTuple; - Relation lRel; - HeapScanDesc sRel; - TupleDesc tdesc; - ScanKeyData key[2]; - Datum d, value[3]; - char repl[3], nulls[3]; - Buffer b; - int ourpid; - bool isnull; - - notifyFrontEndPending = 0; - + extern CommandDest whereToSendOutput; + HeapTuple lTuple, + rTuple; + Relation lRel; + HeapScanDesc sRel; + TupleDesc tdesc; + ScanKeyData key[2]; + Datum d, + value[3]; + char repl[3], + nulls[3]; + Buffer b; + int ourpid; + bool isnull; + + notifyFrontEndPending = 0; + #ifdef ASYNC_DEBUG - elog(DEBUG, "Async_NotifyFrontEnd: notifying front end."); + elog(DEBUG, "Async_NotifyFrontEnd: notifying front end."); #endif - - StartTransactionCommand(); - ourpid = getpid(); - ScanKeyEntryInitialize(&key[0], 0, - Anum_pg_listener_notify, - Integer32EqualRegProcedure, - Int32GetDatum(1)); - ScanKeyEntryInitialize(&key[1], 0, - Anum_pg_listener_pid, - Integer32EqualRegProcedure, - Int32GetDatum(ourpid)); - lRel = heap_openr(ListenerRelationName); - RelationSetLockForWrite(lRel); - tdesc = RelationGetTupleDescriptor(lRel); - sRel = heap_beginscan(lRel, 0, NowTimeQual, 2, key); - - nulls[0] = nulls[1] = nulls[2] = ' '; - repl[0] = repl[1] = repl[2] = ' '; - repl[Anum_pg_listener_notify - 1] = 'r'; - value[0] = value[1] = value[2] = (Datum) 0; - value[Anum_pg_listener_notify - 1] = Int32GetDatum(0); - - while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0,&b))) { - d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname, - tdesc, &isnull); - rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl); - heap_replace(lRel, &lTuple->t_ctid, rTuple); - - /* notifying the front end */ - - if (whereToSendOutput == Remote) { - pq_putnchar("A", 1); - pq_putint(ourpid, 4); - pq_putstr(DatumGetName(d)->data); - pq_flush(); - } else { - elog(NOTICE, "Async_NotifyFrontEnd: no asynchronous notification to frontend on interactive sessions"); + + StartTransactionCommand(); + ourpid = getpid(); + ScanKeyEntryInitialize(&key[0], 0, + Anum_pg_listener_notify, + Integer32EqualRegProcedure, + Int32GetDatum(1)); + ScanKeyEntryInitialize(&key[1], 0, + Anum_pg_listener_pid, + Integer32EqualRegProcedure, + Int32GetDatum(ourpid)); + lRel = heap_openr(ListenerRelationName); + RelationSetLockForWrite(lRel); + tdesc = RelationGetTupleDescriptor(lRel); + sRel = heap_beginscan(lRel, 0, NowTimeQual, 2, key); + + nulls[0] = nulls[1] = nulls[2] = ' '; + repl[0] = repl[1] = repl[2] = ' '; + repl[Anum_pg_listener_notify - 1] = 'r'; + value[0] = value[1] = value[2] = (Datum) 0; + value[Anum_pg_listener_notify - 1] = Int32GetDatum(0); + + while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b))) + { + d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname, + tdesc, &isnull); + rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl); + heap_replace(lRel, &lTuple->t_ctid, rTuple); + + /* notifying the front end */ + + if (whereToSendOutput == Remote) + { + pq_putnchar("A", 1); + pq_putint(ourpid, 4); + pq_putstr(DatumGetName(d)->data); + pq_flush(); + } + else + { + elog(NOTICE, "Async_NotifyFrontEnd: no asynchronous notification to frontend on interactive sessions"); + } + ReleaseBuffer(b); } - ReleaseBuffer(b); - } - CommitTransactionCommand(); + CommitTransactionCommand(); } static int AsyncExistsPendingNotify(char *relname) { - Dlelem* p; - for (p = DLGetHead(pendingNotifies); - p != NULL; - p = DLGetSucc(p)) { - /* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */ - if (!strncmp(DLE_VAL(p), relname, NAMEDATALEN)) - return 1; - } - - return 0; + Dlelem *p; + + for (p = DLGetHead(pendingNotifies); + p != NULL; + p = DLGetSucc(p)) + { + /* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */ + if (!strncmp(DLE_VAL(p), relname, NAMEDATALEN)) + return 1; + } + + return 0; } static void ClearPendingNotify() { - Dlelem* p; - while ( (p = DLRemHead(pendingNotifies)) != NULL) - free(DLE_VAL(p)); -} + Dlelem *p; + while ((p = DLRemHead(pendingNotifies)) != NULL) + free(DLE_VAL(p)); +} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 65a9c041643..2b18cb46df0 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -1,20 +1,20 @@ /*------------------------------------------------------------------------- * * cluster.c-- - * Paul Brown's implementation of cluster index. + * Paul Brown's implementation of cluster index. * - * I am going to use the rename function as a model for this in the - * parser and executor, and the vacuum code as an example in this - * file. As I go - in contrast to the rest of postgres - there will - * be BUCKETS of comments. This is to allow reviewers to understand - * my (probably bogus) assumptions about the way this works. - * [pbrown '94] + * I am going to use the rename function as a model for this in the + * parser and executor, and the vacuum code as an example in this + * file. As I go - in contrast to the rest of postgres - there will + * be BUCKETS of comments. This is to allow reviewers to understand + * my (probably bogus) assumptions about the way this works. + * [pbrown '94] * * Copyright (c) 1994-5, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.13 1997/08/19 21:30:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.14 1997/09/07 04:40:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -47,307 +47,323 @@ #include <optimizer/internal.h> #ifndef NO_SECURITY #include <utils/acl.h> -#endif /* !NO_SECURITY */ +#endif /* !NO_SECURITY */ static Relation copy_heap(Oid OIDOldHeap); -static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap); -static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); +static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap); +static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); /* * cluster * - * Check that the relation is a relation in the appropriate user - * ACL. I will use the same security that limits users on the - * renamerel() function. + * Check that the relation is a relation in the appropriate user + * ACL. I will use the same security that limits users on the + * renamerel() function. * - * Check that the index specified is appropriate for the task - * ( ie it's an index over this relation ). This is trickier. + * Check that the index specified is appropriate for the task + * ( ie it's an index over this relation ). This is trickier. * - * Create a list of all the other indicies on this relation. Because - * the cluster will wreck all the tids, I'll need to destroy bogus - * indicies. The user will have to re-create them. Not nice, but - * I'm not a nice guy. The alternative is to try some kind of post - * destroy re-build. This may be possible. I'll check out what the - * index create functiond want in the way of paramaters. On the other - * hand, re-creating n indicies may blow out the space. + * Create a list of all the other indicies on this relation. Because + * the cluster will wreck all the tids, I'll need to destroy bogus + * indicies. The user will have to re-create them. Not nice, but + * I'm not a nice guy. The alternative is to try some kind of post + * destroy re-build. This may be possible. I'll check out what the + * index create functiond want in the way of paramaters. On the other + * hand, re-creating n indicies may blow out the space. * - * Create new (temporary) relations for the base heap and the new - * index. - * - * Exclusively lock the relations. - * - * Create new clustered index and base heap relation. + * Create new (temporary) relations for the base heap and the new + * index. + * + * Exclusively lock the relations. + * + * Create new clustered index and base heap relation. * */ void cluster(char oldrelname[], char oldindexname[]) { - Oid OIDOldHeap, OIDOldIndex, OIDNewHeap; - - Relation OldHeap, OldIndex; - Relation NewHeap; - - char NewIndexName[NAMEDATALEN]; - char NewHeapName[NAMEDATALEN]; - char saveoldrelname[NAMEDATALEN]; - char saveoldindexname[NAMEDATALEN]; - - - /* Save the old names because they will get lost when the old relations - * are destroyed. - */ - strcpy(saveoldrelname, oldrelname); - strcpy(saveoldindexname, oldindexname); - - /* - * - * I'm going to force all checking back into the commands.c function. - * - * Get the list if indicies for this relation. If the index we want - * is among them, do not add it to the 'kill' list, as it will be - * handled by the 'clean up' code which commits this transaction. - * - * I'm not using the SysCache, because this will happen but - * once, and the slow way is the sure way in this case. - * - */ - /* - * Like vacuum, cluster spans transactions, so I'm going to handle it in - * the same way. - */ - - /* matches the StartTransaction in PostgresMain() */ - - OldHeap = heap_openr(oldrelname); - if (!RelationIsValid(OldHeap)) { - elog(WARN, "cluster: unknown relation: \"%s\"", - oldrelname); - } - OIDOldHeap = OldHeap->rd_id; /* Get OID for the index scan */ - - OldIndex=index_openr(oldindexname);/* Open old index relation */ - if (!RelationIsValid(OldIndex)) { - elog(WARN, "cluster: unknown index: \"%s\"", - oldindexname); - } - OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */ - - heap_close(OldHeap); - index_close(OldIndex); - - /* - * I need to build the copies of the heap and the index. The Commit() - * between here is *very* bogus. If someone is appending stuff, they will - * get the lock after being blocked and add rows which won't be present in - * the new table. Bleagh! I'd be best to try and ensure that no-one's - * in the tables for the entire duration of this process with a pg_vlock. - */ - NewHeap = copy_heap(OIDOldHeap); - OIDNewHeap = NewHeap->rd_id; - strcpy(NewHeapName,NewHeap->rd_rel->relname.data); - - - /* To make the new heap visible (which is until now empty). */ - CommandCounterIncrement(); - - rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex); - - /* To flush the filled new heap (and the statistics about it). */ - CommandCounterIncrement(); - - /* Create new index over the tuples of the new heap. */ - copy_index(OIDOldIndex, OIDNewHeap); - sprintf(NewIndexName, "temp_%x", OIDOldIndex); - - /* - * make this really happen. Flush all the buffers. - * (Believe me, it is necessary ... ended up in a mess without it.) - */ - CommitTransactionCommand(); - StartTransactionCommand(); - - - /* Destroy old heap (along with its index) and rename new. */ - heap_destroy(oldrelname); - - renamerel(NewHeapName, saveoldrelname); - TypeRename(NewHeapName, saveoldrelname); - - renamerel(NewIndexName, saveoldindexname); - - /* - * Again flush all the buffers. - */ - CommitTransactionCommand(); - StartTransactionCommand(); + Oid OIDOldHeap, + OIDOldIndex, + OIDNewHeap; + + Relation OldHeap, + OldIndex; + Relation NewHeap; + + char NewIndexName[NAMEDATALEN]; + char NewHeapName[NAMEDATALEN]; + char saveoldrelname[NAMEDATALEN]; + char saveoldindexname[NAMEDATALEN]; + + + /* + * Save the old names because they will get lost when the old + * relations are destroyed. + */ + strcpy(saveoldrelname, oldrelname); + strcpy(saveoldindexname, oldindexname); + + /* + * I'm going to force all checking back into the commands.c function. + * + * Get the list if indicies for this relation. If the index we want is + * among them, do not add it to the 'kill' list, as it will be handled + * by the 'clean up' code which commits this transaction. + * + * I'm not using the SysCache, because this will happen but once, and the + * slow way is the sure way in this case. + * + */ + + /* + * Like vacuum, cluster spans transactions, so I'm going to handle it + * in the same way. + */ + + /* matches the StartTransaction in PostgresMain() */ + + OldHeap = heap_openr(oldrelname); + if (!RelationIsValid(OldHeap)) + { + elog(WARN, "cluster: unknown relation: \"%s\"", + oldrelname); + } + OIDOldHeap = OldHeap->rd_id;/* Get OID for the index scan */ + + OldIndex = index_openr(oldindexname); /* Open old index relation */ + if (!RelationIsValid(OldIndex)) + { + elog(WARN, "cluster: unknown index: \"%s\"", + oldindexname); + } + OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */ + + heap_close(OldHeap); + index_close(OldIndex); + + /* + * I need to build the copies of the heap and the index. The Commit() + * between here is *very* bogus. If someone is appending stuff, they + * will get the lock after being blocked and add rows which won't be + * present in the new table. Bleagh! I'd be best to try and ensure + * that no-one's in the tables for the entire duration of this process + * with a pg_vlock. + */ + NewHeap = copy_heap(OIDOldHeap); + OIDNewHeap = NewHeap->rd_id; + strcpy(NewHeapName, NewHeap->rd_rel->relname.data); + + + /* To make the new heap visible (which is until now empty). */ + CommandCounterIncrement(); + + rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex); + + /* To flush the filled new heap (and the statistics about it). */ + CommandCounterIncrement(); + + /* Create new index over the tuples of the new heap. */ + copy_index(OIDOldIndex, OIDNewHeap); + sprintf(NewIndexName, "temp_%x", OIDOldIndex); + + /* + * make this really happen. Flush all the buffers. (Believe me, it is + * necessary ... ended up in a mess without it.) + */ + CommitTransactionCommand(); + StartTransactionCommand(); + + + /* Destroy old heap (along with its index) and rename new. */ + heap_destroy(oldrelname); + + renamerel(NewHeapName, saveoldrelname); + TypeRename(NewHeapName, saveoldrelname); + + renamerel(NewIndexName, saveoldindexname); + + /* + * Again flush all the buffers. + */ + CommitTransactionCommand(); + StartTransactionCommand(); } -static Relation +static Relation copy_heap(Oid OIDOldHeap) { - char NewName[NAMEDATALEN]; - TupleDesc OldHeapDesc, tupdesc; - Oid OIDNewHeap; - Relation NewHeap, OldHeap; - - /* - * Create a new heap relation with a temporary name, which has the - * same tuple description as the old one. - */ - sprintf(NewName,"temp_%x", OIDOldHeap); - - OldHeap= heap_open(OIDOldHeap); - OldHeapDesc= RelationGetTupleDescriptor(OldHeap); - - /* - * Need to make a copy of the tuple descriptor, heap_create modifies - * it. - */ - - tupdesc = CreateTupleDescCopy(OldHeapDesc); - - OIDNewHeap=heap_create(NewName, - NULL, - OldHeap->rd_rel->relarch, - OldHeap->rd_rel->relsmgr, - tupdesc); - - if (!OidIsValid(OIDNewHeap)) - elog(WARN,"clusterheap: cannot create temporary heap relation\n"); - - NewHeap=heap_open(OIDNewHeap); - - heap_close(NewHeap); - heap_close(OldHeap); - - return NewHeap; + char NewName[NAMEDATALEN]; + TupleDesc OldHeapDesc, + tupdesc; + Oid OIDNewHeap; + Relation NewHeap, + OldHeap; + + /* + * Create a new heap relation with a temporary name, which has the + * same tuple description as the old one. + */ + sprintf(NewName, "temp_%x", OIDOldHeap); + + OldHeap = heap_open(OIDOldHeap); + OldHeapDesc = RelationGetTupleDescriptor(OldHeap); + + /* + * Need to make a copy of the tuple descriptor, heap_create modifies + * it. + */ + + tupdesc = CreateTupleDescCopy(OldHeapDesc); + + OIDNewHeap = heap_create(NewName, + NULL, + OldHeap->rd_rel->relarch, + OldHeap->rd_rel->relsmgr, + tupdesc); + + if (!OidIsValid(OIDNewHeap)) + elog(WARN, "clusterheap: cannot create temporary heap relation\n"); + + NewHeap = heap_open(OIDNewHeap); + + heap_close(NewHeap); + heap_close(OldHeap); + + return NewHeap; } static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap) { - Relation OldIndex, NewHeap; - HeapTuple Old_pg_index_Tuple, Old_pg_index_relation_Tuple, pg_proc_Tuple; - IndexTupleForm Old_pg_index_Form; - Form_pg_class Old_pg_index_relation_Form; - Form_pg_proc pg_proc_Form; - char *NewIndexName; - AttrNumber *attnumP; - int natts; - FuncIndexInfo * finfo; - - NewHeap = heap_open(OIDNewHeap); - OldIndex = index_open(OIDOldIndex); - - /* - * OK. Create a new (temporary) index for the one that's already - * here. To do this I get the info from pg_index, re-build the - * FunctInfo if I have to, and add a new index with a temporary - * name. - */ - Old_pg_index_Tuple = - SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(OldIndex->rd_id), - 0,0,0); - - Assert(Old_pg_index_Tuple); - Old_pg_index_Form = (IndexTupleForm)GETSTRUCT(Old_pg_index_Tuple); - - Old_pg_index_relation_Tuple = - SearchSysCacheTuple(RELOID, - ObjectIdGetDatum(OldIndex->rd_id), - 0,0,0); - - Assert(Old_pg_index_relation_Tuple); - Old_pg_index_relation_Form = - (Form_pg_class)GETSTRUCT(Old_pg_index_relation_Tuple); - - NewIndexName = palloc(NAMEDATALEN); /* XXX */ - sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */ - - /* - * Ugly as it is, the only way I have of working out the number of - * attribues is to count them. Mostly there'll be just one but - * I've got to be sure. - */ - for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0; - *attnumP != InvalidAttrNumber; - attnumP++, natts++); - - /* - * If this is a functional index, I need to rebuild the functional - * component to pass it to the defining procedure. - */ - if (Old_pg_index_Form->indproc != InvalidOid) { - finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo)); - FIgetnArgs(finfo) = natts; - FIgetProcOid(finfo) = Old_pg_index_Form->indproc; - - pg_proc_Tuple = - SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(Old_pg_index_Form->indproc), - 0,0,0); - - Assert(pg_proc_Tuple); - pg_proc_Form = (Form_pg_proc)GETSTRUCT(pg_proc_Tuple); - namecpy(&(finfo->funcName), &(pg_proc_Form->proname)); - } else { - finfo = (FuncIndexInfo *) NULL; - natts = 1; - } - - index_create((NewHeap->rd_rel->relname).data, - NewIndexName, - finfo, - NULL, /* type info is in the old index */ - Old_pg_index_relation_Form->relam, - natts, - Old_pg_index_Form->indkey, - Old_pg_index_Form->indclass, - (uint16)0, (Datum) NULL, NULL, - Old_pg_index_Form->indislossy, - Old_pg_index_Form->indisunique); - - heap_close(OldIndex); - heap_close(NewHeap); + Relation OldIndex, + NewHeap; + HeapTuple Old_pg_index_Tuple, + Old_pg_index_relation_Tuple, + pg_proc_Tuple; + IndexTupleForm Old_pg_index_Form; + Form_pg_class Old_pg_index_relation_Form; + Form_pg_proc pg_proc_Form; + char *NewIndexName; + AttrNumber *attnumP; + int natts; + FuncIndexInfo *finfo; + + NewHeap = heap_open(OIDNewHeap); + OldIndex = index_open(OIDOldIndex); + + /* + * OK. Create a new (temporary) index for the one that's already here. + * To do this I get the info from pg_index, re-build the FunctInfo if + * I have to, and add a new index with a temporary name. + */ + Old_pg_index_Tuple = + SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(OldIndex->rd_id), + 0, 0, 0); + + Assert(Old_pg_index_Tuple); + Old_pg_index_Form = (IndexTupleForm) GETSTRUCT(Old_pg_index_Tuple); + + Old_pg_index_relation_Tuple = + SearchSysCacheTuple(RELOID, + ObjectIdGetDatum(OldIndex->rd_id), + 0, 0, 0); + + Assert(Old_pg_index_relation_Tuple); + Old_pg_index_relation_Form = + (Form_pg_class) GETSTRUCT(Old_pg_index_relation_Tuple); + + NewIndexName = palloc(NAMEDATALEN); /* XXX */ + sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */ + + /* + * Ugly as it is, the only way I have of working out the number of + * attribues is to count them. Mostly there'll be just one but I've + * got to be sure. + */ + for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0; + *attnumP != InvalidAttrNumber; + attnumP++, natts++); + + /* + * If this is a functional index, I need to rebuild the functional + * component to pass it to the defining procedure. + */ + if (Old_pg_index_Form->indproc != InvalidOid) + { + finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo)); + FIgetnArgs(finfo) = natts; + FIgetProcOid(finfo) = Old_pg_index_Form->indproc; + + pg_proc_Tuple = + SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(Old_pg_index_Form->indproc), + 0, 0, 0); + + Assert(pg_proc_Tuple); + pg_proc_Form = (Form_pg_proc) GETSTRUCT(pg_proc_Tuple); + namecpy(&(finfo->funcName), &(pg_proc_Form->proname)); + } + else + { + finfo = (FuncIndexInfo *) NULL; + natts = 1; + } + + index_create((NewHeap->rd_rel->relname).data, + NewIndexName, + finfo, + NULL, /* type info is in the old index */ + Old_pg_index_relation_Form->relam, + natts, + Old_pg_index_Form->indkey, + Old_pg_index_Form->indclass, + (uint16) 0, (Datum) NULL, NULL, + Old_pg_index_Form->indislossy, + Old_pg_index_Form->indisunique); + + heap_close(OldIndex); + heap_close(NewHeap); } static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) { - Relation LocalNewHeap, LocalOldHeap, LocalOldIndex; - IndexScanDesc ScanDesc; - RetrieveIndexResult ScanResult; - ItemPointer HeapTid; - HeapTuple LocalHeapTuple; - Buffer LocalBuffer; - Oid OIDNewHeapInsert; - - /* - * Open the relations I need. Scan through the OldHeap on the OldIndex and - * insert each tuple into the NewHeap. - */ - LocalNewHeap=(Relation)heap_open(OIDNewHeap); - LocalOldHeap=(Relation)heap_open(OIDOldHeap); - LocalOldIndex=(Relation)index_open(OIDOldIndex); - - ScanDesc=index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL); - - while ((ScanResult = - index_getnext(ScanDesc, ForwardScanDirection)) != NULL) { - - HeapTid = &ScanResult->heap_iptr; - LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer); - OIDNewHeapInsert = - heap_insert(LocalNewHeap, LocalHeapTuple); - pfree(ScanResult); - ReleaseBuffer(LocalBuffer); - } - index_endscan(ScanDesc); - - index_close(LocalOldIndex); - heap_close(LocalOldHeap); - heap_close(LocalNewHeap); + Relation LocalNewHeap, + LocalOldHeap, + LocalOldIndex; + IndexScanDesc ScanDesc; + RetrieveIndexResult ScanResult; + ItemPointer HeapTid; + HeapTuple LocalHeapTuple; + Buffer LocalBuffer; + Oid OIDNewHeapInsert; + + /* + * Open the relations I need. Scan through the OldHeap on the OldIndex + * and insert each tuple into the NewHeap. + */ + LocalNewHeap = (Relation) heap_open(OIDNewHeap); + LocalOldHeap = (Relation) heap_open(OIDOldHeap); + LocalOldIndex = (Relation) index_open(OIDOldIndex); + + ScanDesc = index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL); + + while ((ScanResult = + index_getnext(ScanDesc, ForwardScanDirection)) != NULL) + { + + HeapTid = &ScanResult->heap_iptr; + LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer); + OIDNewHeapInsert = + heap_insert(LocalNewHeap, LocalHeapTuple); + pfree(ScanResult); + ReleaseBuffer(LocalBuffer); + } + index_endscan(ScanDesc); + + index_close(LocalOldIndex); + heap_close(LocalOldHeap); + heap_close(LocalNewHeap); } - diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 376bd3ae5fa..7af9b37c072 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -1,29 +1,29 @@ /*------------------------------------------------------------------------- * * command.c-- - * random postgres portal and utility support code + * random postgres portal and utility support code * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.13 1997/08/22 14:22:07 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.14 1997/09/07 04:40:38 momjian Exp $ * * NOTES - * The PortalExecutorHeapMemory crap needs to be eliminated - * by designing a better executor / portal processing memory - * interface. - * - * The PerformAddAttribute() code, like most of the relation - * manipulating code in the commands/ directory, should go - * someplace closer to the lib/catalog code. - * + * The PortalExecutorHeapMemory crap needs to be eliminated + * by designing a better executor / portal processing memory + * interface. + * + * The PerformAddAttribute() code, like most of the relation + * manipulating code in the commands/ directory, should go + * someplace closer to the lib/catalog code. + * *------------------------------------------------------------------------- */ #include <postgres.h> #include <access/relscan.h> -#include <utils/portal.h> +#include <utils/portal.h> #include <commands/command.h> #include <utils/mcxt.h> #include <executor/executor.h> @@ -31,7 +31,7 @@ #include <catalog/indexing.h> #include <utils/syscache.h> #include <catalog/catalog.h> -#include <access/heapam.h> +#include <access/heapam.h> #include <utils/array.h> #include <utils/acl.h> #include <optimizer/prep.h> @@ -41,443 +41,468 @@ #include <utils/builtins.h> /* ---------------- - * PortalExecutorHeapMemory stuff + * PortalExecutorHeapMemory stuff * - * This is where the XXXSuperDuperHacky code was. -cim 3/15/90 + * This is where the XXXSuperDuperHacky code was. -cim 3/15/90 * ---------------- */ -MemoryContext PortalExecutorHeapMemory = NULL; +MemoryContext PortalExecutorHeapMemory = NULL; /* -------------------------------- - * PortalCleanup + * PortalCleanup * -------------------------------- */ void PortalCleanup(Portal portal) { - MemoryContext context; - - /* ---------------- - * sanity checks - * ---------------- - */ - AssertArg(PortalIsValid(portal)); - AssertArg(portal->cleanup == PortalCleanup); - - /* ---------------- - * set proper portal-executor context before calling ExecMain. - * ---------------- - */ - context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal)); - PortalExecutorHeapMemory = (MemoryContext) - PortalGetHeapMemory(portal); - - /* ---------------- - * tell the executor to shutdown the query - * ---------------- - */ - ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal)); - - /* ---------------- - * switch back to previous context - * ---------------- - */ - MemoryContextSwitchTo(context); - PortalExecutorHeapMemory = (MemoryContext) NULL; + MemoryContext context; + + /* ---------------- + * sanity checks + * ---------------- + */ + AssertArg(PortalIsValid(portal)); + AssertArg(portal->cleanup == PortalCleanup); + + /* ---------------- + * set proper portal-executor context before calling ExecMain. + * ---------------- + */ + context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal)); + PortalExecutorHeapMemory = (MemoryContext) + PortalGetHeapMemory(portal); + + /* ---------------- + * tell the executor to shutdown the query + * ---------------- + */ + ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal)); + + /* ---------------- + * switch back to previous context + * ---------------- + */ + MemoryContextSwitchTo(context); + PortalExecutorHeapMemory = (MemoryContext) NULL; } /* -------------------------------- - * PerformPortalFetch + * PerformPortalFetch * -------------------------------- */ void PerformPortalFetch(char *name, - bool forward, - int count, - char *tag, - CommandDest dest) + bool forward, + int count, + char *tag, + CommandDest dest) { - Portal portal; - int feature; - QueryDesc *queryDesc; - MemoryContext context; - - /* ---------------- - * sanity checks - * ---------------- - */ - if (name == NULL) { - elog(NOTICE, "PerformPortalFetch: blank portal unsupported"); - return; - } - - /* ---------------- - * get the portal from the portal name - * ---------------- - */ - portal = GetPortalByName(name); - if (! PortalIsValid(portal)) { - elog(NOTICE, "PerformPortalFetch: portal \"%s\" not found", - name); - return; - } - - /* ---------------- - * switch into the portal context - * ---------------- - */ - context= MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal)); - - AssertState(context == - (MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL))); - - /* ---------------- - * setup "feature" to tell the executor what direction and - * how many tuples to fetch. - * ---------------- - */ - if (forward) - feature = EXEC_FOR; - else - feature = EXEC_BACK; - - /* ---------------- - * tell the destination to prepare to recieve some tuples - * ---------------- - */ - queryDesc = PortalGetQueryDesc(portal); - BeginCommand(name, - queryDesc->operation, - portal->attinfo,/* QueryDescGetTypeInfo(queryDesc), */ - false, /* portal fetches don't end up in relations */ - false, /* this is a portal fetch, not a "retrieve portal" */ - tag, - dest); - - /* ---------------- - * execute the portal fetch operation - * ---------------- - */ - PortalExecutorHeapMemory = (MemoryContext) - PortalGetHeapMemory(portal); - - ExecutorRun(queryDesc, PortalGetState(portal), feature, count); - - /* ---------------- - * Note: the "end-of-command" tag is returned by higher-level - * utility code - * - * Return blank portal for now. - * Otherwise, this named portal will be cleaned. - * Note: portals will only be supported within a BEGIN...END - * block in the near future. Later, someone will fix it to - * do what is possible across transaction boundries. - * ---------------- - */ - MemoryContextSwitchTo( - (MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL))); + Portal portal; + int feature; + QueryDesc *queryDesc; + MemoryContext context; + + /* ---------------- + * sanity checks + * ---------------- + */ + if (name == NULL) + { + elog(NOTICE, "PerformPortalFetch: blank portal unsupported"); + return; + } + + /* ---------------- + * get the portal from the portal name + * ---------------- + */ + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + { + elog(NOTICE, "PerformPortalFetch: portal \"%s\" not found", + name); + return; + } + + /* ---------------- + * switch into the portal context + * ---------------- + */ + context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal)); + + AssertState(context == + (MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL))); + + /* ---------------- + * setup "feature" to tell the executor what direction and + * how many tuples to fetch. + * ---------------- + */ + if (forward) + feature = EXEC_FOR; + else + feature = EXEC_BACK; + + /* ---------------- + * tell the destination to prepare to recieve some tuples + * ---------------- + */ + queryDesc = PortalGetQueryDesc(portal); + BeginCommand(name, + queryDesc->operation, + portal->attinfo, /* QueryDescGetTypeInfo(queryDesc), + * */ + false, /* portal fetches don't end up in + * relations */ + false, /* this is a portal fetch, not a "retrieve + * portal" */ + tag, + dest); + + /* ---------------- + * execute the portal fetch operation + * ---------------- + */ + PortalExecutorHeapMemory = (MemoryContext) + PortalGetHeapMemory(portal); + + ExecutorRun(queryDesc, PortalGetState(portal), feature, count); + + /* ---------------- + * Note: the "end-of-command" tag is returned by higher-level + * utility code + * + * Return blank portal for now. + * Otherwise, this named portal will be cleaned. + * Note: portals will only be supported within a BEGIN...END + * block in the near future. Later, someone will fix it to + * do what is possible across transaction boundries. + * ---------------- + */ + MemoryContextSwitchTo( + (MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL))); } /* -------------------------------- - * PerformPortalClose + * PerformPortalClose * -------------------------------- */ void PerformPortalClose(char *name, CommandDest dest) { - Portal portal; - - /* ---------------- - * sanity checks - * ---------------- - */ - if (name == NULL) { - elog(NOTICE, "PerformPortalClose: blank portal unsupported"); - return; - } - - /* ---------------- - * get the portal from the portal name - * ---------------- - */ - portal = GetPortalByName(name); - if (! PortalIsValid(portal)) { - elog(NOTICE, "PerformPortalClose: portal \"%s\" not found", - name); - return; - } - - /* ---------------- - * Note: PortalCleanup is called as a side-effect - * ---------------- - */ - PortalDestroy(&portal); + Portal portal; + + /* ---------------- + * sanity checks + * ---------------- + */ + if (name == NULL) + { + elog(NOTICE, "PerformPortalClose: blank portal unsupported"); + return; + } + + /* ---------------- + * get the portal from the portal name + * ---------------- + */ + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + { + elog(NOTICE, "PerformPortalClose: portal \"%s\" not found", + name); + return; + } + + /* ---------------- + * Note: PortalCleanup is called as a side-effect + * ---------------- + */ + PortalDestroy(&portal); } /* ---------------- - * PerformAddAttribute + * PerformAddAttribute * - * adds an additional attribute to a relation + * adds an additional attribute to a relation * - * Adds attribute field(s) to a relation. Each new attribute - * is given attnums in sequential order and is added to the - * ATTRIBUTE relation. If the AMI fails, defunct tuples will - * remain in the ATTRIBUTE relation for later vacuuming. - * Later, there may be some reserved attribute names??? + * Adds attribute field(s) to a relation. Each new attribute + * is given attnums in sequential order and is added to the + * ATTRIBUTE relation. If the AMI fails, defunct tuples will + * remain in the ATTRIBUTE relation for later vacuuming. + * Later, there may be some reserved attribute names??? * - * (If needed, can instead use elog to handle exceptions.) + * (If needed, can instead use elog to handle exceptions.) * - * Note: - * Initial idea of ordering the tuple attributes so that all - * the variable length domains occured last was scratched. Doing - * so would not speed access too much (in general) and would create - * many complications in formtuple, amgetattr, and addattribute. + * Note: + * Initial idea of ordering the tuple attributes so that all + * the variable length domains occured last was scratched. Doing + * so would not speed access too much (in general) and would create + * many complications in formtuple, amgetattr, and addattribute. * - * scan attribute catalog for name conflict (within rel) - * scan type catalog for absence of data type (if not arg) - * create attnum magically??? - * create attribute tuple - * insert attribute in attribute catalog - * modify reldesc - * create new relation tuple - * insert new relation in relation catalog - * delete original relation from relation catalog + * scan attribute catalog for name conflict (within rel) + * scan type catalog for absence of data type (if not arg) + * create attnum magically??? + * create attribute tuple + * insert attribute in attribute catalog + * modify reldesc + * create new relation tuple + * insert new relation in relation catalog + * delete original relation from relation catalog * ---------------- */ void PerformAddAttribute(char *relationName, - char *userName, - bool inherits, - ColumnDef *colDef) -{ - Relation relrdesc, attrdesc; - HeapScanDesc attsdesc; - HeapTuple reltup; - HeapTuple attributeTuple; - AttributeTupleForm attribute; - FormData_pg_attribute attributeD; - int i; - int minattnum, maxatts; - HeapTuple tup; - ScanKeyData key[2]; - ItemPointerData oldTID; - Relation idescs[Num_pg_attr_indices]; - Relation ridescs[Num_pg_class_indices]; - bool hasindex; - - /* - * permissions checking. this would normally be done in utility.c, - * but this particular routine is recursive. - * - * normally, only the owner of a class can change its schema. - */ - if (IsSystemRelationName(relationName)) - elog(WARN, "PerformAddAttribute: class \"%s\" is a system catalog", - relationName); + char *userName, + bool inherits, + ColumnDef * colDef) +{ + Relation relrdesc, + attrdesc; + HeapScanDesc attsdesc; + HeapTuple reltup; + HeapTuple attributeTuple; + AttributeTupleForm attribute; + FormData_pg_attribute attributeD; + int i; + int minattnum, + maxatts; + HeapTuple tup; + ScanKeyData key[2]; + ItemPointerData oldTID; + Relation idescs[Num_pg_attr_indices]; + Relation ridescs[Num_pg_class_indices]; + bool hasindex; + + /* + * permissions checking. this would normally be done in utility.c, + * but this particular routine is recursive. + * + * normally, only the owner of a class can change its schema. + */ + if (IsSystemRelationName(relationName)) + elog(WARN, "PerformAddAttribute: class \"%s\" is a system catalog", + relationName); #ifndef NO_SECURITY - if (!pg_ownercheck(userName, relationName, RELNAME)) - elog(WARN, "PerformAddAttribute: you do not own class \"%s\"", - relationName); + if (!pg_ownercheck(userName, relationName, RELNAME)) + elog(WARN, "PerformAddAttribute: you do not own class \"%s\"", + relationName); #endif - /* - * we can't add a not null attribute - */ - if (colDef->is_not_null) - elog(WARN,"Can't add a not null attribute to a existent relation"); - if (colDef->defval) - elog(WARN,"ADD ATTRIBUTE: DEFAULT is not implemented, yet"); - /* - * if the first element in the 'schema' list is a "*" then we are - * supposed to add this attribute to all classes that inherit from - * 'relationName' (as well as to 'relationName'). - * - * any permissions or problems with duplicate attributes will cause - * the whole transaction to abort, which is what we want -- all or - * nothing. - */ - if (colDef != NULL) { - if (inherits) { - Oid myrelid, childrelid; - List *child, *children; - - relrdesc = heap_openr(relationName); - if (!RelationIsValid(relrdesc)) { - elog(WARN, "PerformAddAttribute: unknown relation: \"%s\"", - relationName); - } - myrelid = relrdesc->rd_id; - heap_close(relrdesc); - - /* this routine is actually in the planner */ - children = find_all_inheritors(lconsi(myrelid,NIL), NIL); - - /* - * find_all_inheritors does the recursive search of the - * inheritance hierarchy, so all we have to do is process - * all of the relids in the list that it returns. - */ - foreach (child, children) { - childrelid = lfirsti(child); - if (childrelid == myrelid) - continue; - relrdesc = heap_open(childrelid); - if (!RelationIsValid(relrdesc)) { - elog(WARN, "PerformAddAttribute: can't find catalog entry for inheriting class with oid %d", - childrelid); + + /* + * we can't add a not null attribute + */ + if (colDef->is_not_null) + elog(WARN, "Can't add a not null attribute to a existent relation"); + if (colDef->defval) + elog(WARN, "ADD ATTRIBUTE: DEFAULT is not implemented, yet"); + + /* + * if the first element in the 'schema' list is a "*" then we are + * supposed to add this attribute to all classes that inherit from + * 'relationName' (as well as to 'relationName'). + * + * any permissions or problems with duplicate attributes will cause the + * whole transaction to abort, which is what we want -- all or + * nothing. + */ + if (colDef != NULL) + { + if (inherits) + { + Oid myrelid, + childrelid; + List *child, + *children; + + relrdesc = heap_openr(relationName); + if (!RelationIsValid(relrdesc)) + { + elog(WARN, "PerformAddAttribute: unknown relation: \"%s\"", + relationName); + } + myrelid = relrdesc->rd_id; + heap_close(relrdesc); + + /* this routine is actually in the planner */ + children = find_all_inheritors(lconsi(myrelid, NIL), NIL); + + /* + * find_all_inheritors does the recursive search of the + * inheritance hierarchy, so all we have to do is process all + * of the relids in the list that it returns. + */ + foreach(child, children) + { + childrelid = lfirsti(child); + if (childrelid == myrelid) + continue; + relrdesc = heap_open(childrelid); + if (!RelationIsValid(relrdesc)) + { + elog(WARN, "PerformAddAttribute: can't find catalog entry for inheriting class with oid %d", + childrelid); + } + PerformAddAttribute((relrdesc->rd_rel->relname).data, + userName, false, colDef); + heap_close(relrdesc); + } } - PerformAddAttribute((relrdesc->rd_rel->relname).data, - userName, false, colDef); + } + + relrdesc = heap_openr(RelationRelationName); + reltup = ClassNameIndexScan(relrdesc, relationName); + + if (!PointerIsValid(reltup)) + { heap_close(relrdesc); - } + elog(WARN, "PerformAddAttribute: relation \"%s\" not found", + relationName); } - } - - relrdesc = heap_openr(RelationRelationName); - reltup = ClassNameIndexScan(relrdesc, relationName); - - if (!PointerIsValid(reltup)) { - heap_close(relrdesc); - elog(WARN, "PerformAddAttribute: relation \"%s\" not found", - relationName); - } - /* - * XXX is the following check sufficient? - */ - if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX) { - elog(WARN, "PerformAddAttribute: index relation \"%s\" not changed", - relationName); - return; - } - - minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts; - maxatts = minattnum + 1; - if (maxatts > MaxHeapAttributeNumber) { - pfree(reltup); /* XXX temp */ - heap_close(relrdesc); /* XXX temp */ - elog(WARN, "PerformAddAttribute: relations limited to %d attributes", - MaxHeapAttributeNumber); - return; - } - - attrdesc = heap_openr(AttributeRelationName); - - Assert(attrdesc); - Assert(RelationGetRelationTupleForm(attrdesc)); - - /* - * Open all (if any) pg_attribute indices - */ - hasindex = RelationGetRelationTupleForm(attrdesc)->relhasindex; - if (hasindex) - CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); - - ScanKeyEntryInitialize(&key[0], - (bits16) NULL, - (AttrNumber) Anum_pg_attribute_attrelid, - (RegProcedure)ObjectIdEqualRegProcedure, - (Datum) reltup->t_oid); - - ScanKeyEntryInitialize(&key[1], - (bits16) NULL, - (AttrNumber) Anum_pg_attribute_attname, - (RegProcedure)NameEqualRegProcedure, - (Datum) NULL); - - attributeD.attrelid = reltup->t_oid; - attributeD.attdisbursion = 0; /* XXX temporary */ - attributeD.attcacheoff = -1; - - attributeTuple = heap_addheader(Natts_pg_attribute, - sizeof attributeD, - (char *)&attributeD); - - attribute = (AttributeTupleForm)GETSTRUCT(attributeTuple); - - i = 1 + minattnum; - - { - HeapTuple typeTuple; - TypeTupleForm form; - char *p; - int attnelems; - + /* - * XXX use syscache here as an optimization + * XXX is the following check sufficient? */ - key[1].sk_argument = (Datum)colDef->colname; - attsdesc = heap_beginscan(attrdesc, 0, NowTimeQual, 2, key); - - - tup = heap_getnext(attsdesc, 0, (Buffer *) NULL); - if (HeapTupleIsValid(tup)) { - pfree(reltup); /* XXX temp */ - heap_endscan(attsdesc); /* XXX temp */ - heap_close(attrdesc); /* XXX temp */ - heap_close(relrdesc); /* XXX temp */ - elog(WARN, "PerformAddAttribute: attribute \"%s\" already exists in class \"%s\"", - key[1].sk_argument, - relationName); - return; + if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX) + { + elog(WARN, "PerformAddAttribute: index relation \"%s\" not changed", + relationName); + return; + } + + minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts; + maxatts = minattnum + 1; + if (maxatts > MaxHeapAttributeNumber) + { + pfree(reltup); /* XXX temp */ + heap_close(relrdesc); /* XXX temp */ + elog(WARN, "PerformAddAttribute: relations limited to %d attributes", + MaxHeapAttributeNumber); + return; } - heap_endscan(attsdesc); - + + attrdesc = heap_openr(AttributeRelationName); + + Assert(attrdesc); + Assert(RelationGetRelationTupleForm(attrdesc)); + /* - * check to see if it is an array attribute. + * Open all (if any) pg_attribute indices */ - - p = colDef->typename->name; - - if (colDef->typename->arrayBounds) - { - attnelems = length(colDef->typename->arrayBounds); - p = makeArrayTypeName(colDef->typename->name); - } - else - attnelems = 0; - - typeTuple = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(p), - 0,0,0); - form = (TypeTupleForm)GETSTRUCT(typeTuple); - - if (!HeapTupleIsValid(typeTuple)) { - elog(WARN, "Add: type \"%s\" nonexistent", p); + hasindex = RelationGetRelationTupleForm(attrdesc)->relhasindex; + if (hasindex) + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); + + ScanKeyEntryInitialize(&key[0], + (bits16) NULL, + (AttrNumber) Anum_pg_attribute_attrelid, + (RegProcedure) ObjectIdEqualRegProcedure, + (Datum) reltup->t_oid); + + ScanKeyEntryInitialize(&key[1], + (bits16) NULL, + (AttrNumber) Anum_pg_attribute_attname, + (RegProcedure) NameEqualRegProcedure, + (Datum) NULL); + + attributeD.attrelid = reltup->t_oid; + attributeD.attdisbursion = 0; /* XXX temporary */ + attributeD.attcacheoff = -1; + + attributeTuple = heap_addheader(Natts_pg_attribute, + sizeof attributeD, + (char *) &attributeD); + + attribute = (AttributeTupleForm) GETSTRUCT(attributeTuple); + + i = 1 + minattnum; + + { + HeapTuple typeTuple; + TypeTupleForm form; + char *p; + int attnelems; + + /* + * XXX use syscache here as an optimization + */ + key[1].sk_argument = (Datum) colDef->colname; + attsdesc = heap_beginscan(attrdesc, 0, NowTimeQual, 2, key); + + + tup = heap_getnext(attsdesc, 0, (Buffer *) NULL); + if (HeapTupleIsValid(tup)) + { + pfree(reltup); /* XXX temp */ + heap_endscan(attsdesc); /* XXX temp */ + heap_close(attrdesc); /* XXX temp */ + heap_close(relrdesc); /* XXX temp */ + elog(WARN, "PerformAddAttribute: attribute \"%s\" already exists in class \"%s\"", + key[1].sk_argument, + relationName); + return; + } + heap_endscan(attsdesc); + + /* + * check to see if it is an array attribute. + */ + + p = colDef->typename->name; + + if (colDef->typename->arrayBounds) + { + attnelems = length(colDef->typename->arrayBounds); + p = makeArrayTypeName(colDef->typename->name); + } + else + attnelems = 0; + + typeTuple = SearchSysCacheTuple(TYPNAME, + PointerGetDatum(p), + 0, 0, 0); + form = (TypeTupleForm) GETSTRUCT(typeTuple); + + if (!HeapTupleIsValid(typeTuple)) + { + elog(WARN, "Add: type \"%s\" nonexistent", p); + } + namestrcpy(&(attribute->attname), (char *) key[1].sk_argument); + attribute->atttypid = typeTuple->t_oid; + if (colDef->typename->typlen > 0) + attribute->attlen = colDef->typename->typlen; + else +/* bpchar, varchar, text */ + attribute->attlen = form->typlen; + attribute->attnum = i; + attribute->attbyval = form->typbyval; + attribute->attnelems = attnelems; + attribute->attcacheoff = -1; + attribute->attisset = (bool) (form->typtype == 'c'); + attribute->attalign = form->typalign; + attribute->attnotnull = false; + + heap_insert(attrdesc, attributeTuple); + if (hasindex) + CatalogIndexInsert(idescs, + Num_pg_attr_indices, + attrdesc, + attributeTuple); } - namestrcpy(&(attribute->attname), (char*) key[1].sk_argument); - attribute->atttypid = typeTuple->t_oid; - if (colDef->typename->typlen > 0) - attribute->attlen = colDef->typename->typlen; - else /* bpchar, varchar, text */ - attribute->attlen = form->typlen; - attribute->attnum = i; - attribute->attbyval = form->typbyval; - attribute->attnelems = attnelems; - attribute->attcacheoff = -1; - attribute->attisset = (bool) (form->typtype == 'c'); - attribute->attalign = form->typalign; - attribute->attnotnull = false; - - heap_insert(attrdesc, attributeTuple); + if (hasindex) - CatalogIndexInsert(idescs, - Num_pg_attr_indices, - attrdesc, - attributeTuple); - } - - if (hasindex) - CatalogCloseIndices(Num_pg_attr_indices, idescs); - heap_close(attrdesc); - - ((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts; - oldTID = reltup->t_ctid; - heap_replace(relrdesc, &oldTID, reltup); - - /* keep catalog indices current */ - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, reltup); - CatalogCloseIndices(Num_pg_class_indices, ridescs); - - pfree(reltup); - heap_close(relrdesc); + CatalogCloseIndices(Num_pg_attr_indices, idescs); + heap_close(attrdesc); + + ((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts; + oldTID = reltup->t_ctid; + heap_replace(relrdesc, &oldTID, reltup); + + /* keep catalog indices current */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); + CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, reltup); + CatalogCloseIndices(Num_pg_class_indices, ridescs); + + pfree(reltup); + heap_close(relrdesc); } diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 687cd1eb12e..795e9f5584f 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.29 1997/09/04 13:18:59 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.30 1997/09/07 04:40:40 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -42,736 +42,857 @@ /* non-export function prototypes */ -static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim); -static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim); -static Oid GetOutputFunction(Oid type); -static Oid GetTypeElement(Oid type); -static Oid GetInputFunction(Oid type); -static Oid IsTypeByVal(Oid type); -static void GetIndexRelations(Oid main_relation_oid, - int *n_indices, - Relation **index_rels); +static void CopyTo(Relation rel, bool binary, bool oids, FILE * fp, char *delim); +static void CopyFrom(Relation rel, bool binary, bool oids, FILE * fp, char *delim); +static Oid GetOutputFunction(Oid type); +static Oid GetTypeElement(Oid type); +static Oid GetInputFunction(Oid type); +static Oid IsTypeByVal(Oid type); +static void +GetIndexRelations(Oid main_relation_oid, + int *n_indices, + Relation ** index_rels); + #ifdef COPY_PATCH -static void CopyReadNewline(FILE *fp, int *newline); -static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline); +static void CopyReadNewline(FILE * fp, int *newline); +static char *CopyReadAttribute(FILE * fp, bool * isnull, char *delim, int *newline); + #else -static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim); +static char *CopyReadAttribute(FILE * fp, bool * isnull, char *delim); + #endif -static void CopyAttributeOut(FILE *fp, char *string, char *delim); -static int CountTuples(Relation relation); +static void CopyAttributeOut(FILE * fp, char *string, char *delim); +static int CountTuples(Relation relation); -extern FILE *Pfout, *Pfin; +extern FILE *Pfout, + *Pfin; #ifdef COPY_DEBUG -static int lineno; +static int lineno; + #endif - + /* - * DoCopy executes a the SQL COPY statement. + * DoCopy executes a the SQL COPY statement. */ void -DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, - char *filename, char *delim) { +DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, + char *filename, char *delim) +{ /*---------------------------------------------------------------------------- Either unload or reload contents of class <relname>, depending on <from>. If <pipe> is false, transfer is between the class and the file named <filename>. Otherwise, transfer is between the class and our regular - input/output stream. The latter could be either stdin/stdout or a + input/output stream. The latter could be either stdin/stdout or a socket, depending on whether we're running under Postmaster control. Iff <binary>, unload or reload in the binary format, as opposed to the - more wasteful but more robust and portable text format. + more wasteful but more robust and portable text format. - If in the text format, delimit columns with delimiter <delim>. + If in the text format, delimit columns with delimiter <delim>. When loading in the text format from an input stream (as opposed to - a file), recognize a "." on a line by itself as EOF. Also recognize + a file), recognize a "." on a line by itself as EOF. Also recognize a stream EOF. When unloading in the text format to an output stream, write a "." on a line by itself at the end of the data. Iff <oids>, unload or reload the format that includes OID information. Do not allow a Postgres user without superuser privilege to read from - or write to a file. + or write to a file. Do not allow the copy if user doesn't have proper permission to access the class. ----------------------------------------------------------------------------*/ - FILE *fp; - Relation rel; - extern char *UserName; /* defined in global.c */ - const AclMode required_access = from ? ACL_WR : ACL_RD; - int result; - - rel = heap_openr(relname); - if (rel == NULL) elog(WARN, "COPY command failed. Class %s " - "does not exist.", relname); - - result = pg_aclcheck(relname, UserName, required_access); - if(result != ACLCHECK_OK) - elog(WARN, "%s: %s", relname, aclcheck_error_strings[result]); - /* Above should not return */ - else if (!superuser() && !pipe) - elog(WARN, "You must have Postgres superuser privilege to do a COPY " - "directly to or from a file. Anyone can COPY to stdout or " - "from stdin. Psql's \\copy command also works for anyone."); - /* Above should not return. */ - else { - if (from) { /* copy from file to database */ - if ( rel->rd_rel->relkind == RELKIND_SEQUENCE ) - elog (WARN, "You can't change sequence relation %s", relname); - if (pipe) { - if (IsUnderPostmaster) { - ReceiveCopyBegin(); - fp = Pfin; - } else fp = stdin; - } else { - fp = AllocateFile(filename, "r"); - if (fp == NULL) - elog(WARN, "COPY command, running in backend with " - "effective uid %d, could not open file '%s' for " - "reading. Errno = %s (%d).", - geteuid(), filename, strerror(errno), errno); - /* Above should not return */ - } - CopyFrom(rel, binary, oids, fp, delim); - } else { /* copy from database to file */ - if (pipe) { - if (IsUnderPostmaster) { - SendCopyBegin(); - fp = Pfout; - } else fp = stdout; - } else { - mode_t oumask; /* Pre-existing umask value */ - oumask = umask((mode_t) 0); - fp = AllocateFile(filename, "w"); - umask(oumask); - if (fp == NULL) - elog(WARN, "COPY command, running in backend with " - "effective uid %d, could not open file '%s' for " - "writing. Errno = %s (%d).", - geteuid(), filename, strerror(errno), errno); - /* Above should not return */ - } - CopyTo(rel, binary, oids, fp, delim); - } - if (!pipe) - FreeFile(fp); - else if (!from && !binary) { - fputs("\\.\n", fp); - if (IsUnderPostmaster) fflush(Pfout); - } - } + FILE *fp; + Relation rel; + extern char *UserName; /* defined in global.c */ + const AclMode required_access = from ? ACL_WR : ACL_RD; + int result; + + rel = heap_openr(relname); + if (rel == NULL) + elog(WARN, "COPY command failed. Class %s " + "does not exist.", relname); + + result = pg_aclcheck(relname, UserName, required_access); + if (result != ACLCHECK_OK) + elog(WARN, "%s: %s", relname, aclcheck_error_strings[result]); + /* Above should not return */ + else if (!superuser() && !pipe) + elog(WARN, "You must have Postgres superuser privilege to do a COPY " + "directly to or from a file. Anyone can COPY to stdout or " + "from stdin. Psql's \\copy command also works for anyone."); + /* Above should not return. */ + else + { + if (from) + { /* copy from file to database */ + if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + elog(WARN, "You can't change sequence relation %s", relname); + if (pipe) + { + if (IsUnderPostmaster) + { + ReceiveCopyBegin(); + fp = Pfin; + } + else + fp = stdin; + } + else + { + fp = AllocateFile(filename, "r"); + if (fp == NULL) + elog(WARN, "COPY command, running in backend with " + "effective uid %d, could not open file '%s' for " + "reading. Errno = %s (%d).", + geteuid(), filename, strerror(errno), errno); + /* Above should not return */ + } + CopyFrom(rel, binary, oids, fp, delim); + } + else + { /* copy from database to file */ + if (pipe) + { + if (IsUnderPostmaster) + { + SendCopyBegin(); + fp = Pfout; + } + else + fp = stdout; + } + else + { + mode_t oumask; /* Pre-existing umask value */ + + oumask = umask((mode_t) 0); + fp = AllocateFile(filename, "w"); + umask(oumask); + if (fp == NULL) + elog(WARN, "COPY command, running in backend with " + "effective uid %d, could not open file '%s' for " + "writing. Errno = %s (%d).", + geteuid(), filename, strerror(errno), errno); + /* Above should not return */ + } + CopyTo(rel, binary, oids, fp, delim); + } + if (!pipe) + FreeFile(fp); + else if (!from && !binary) + { + fputs("\\.\n", fp); + if (IsUnderPostmaster) + fflush(Pfout); + } + } } static void -CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim) +CopyTo(Relation rel, bool binary, bool oids, FILE * fp, char *delim) { - HeapTuple tuple; - HeapScanDesc scandesc; - - int32 attr_count, i; - AttributeTupleForm *attr; - func_ptr *out_functions; - int dummy; - Oid out_func_oid; - Oid *elements; - Datum value; - bool isnull; /* The attribute we are copying is null */ - char *nulls; - /* <nulls> is a (dynamically allocated) array with one character - per attribute in the instance being copied. nulls[I-1] is - 'n' if Attribute Number I is null, and ' ' otherwise. - - <nulls> is meaningful only if we are doing a binary copy. - */ - char *string; - int32 ntuples; - TupleDesc tupDesc; - - scandesc = heap_beginscan(rel, 0, NULL, 0, NULL); - - attr_count = rel->rd_att->natts; - attr = rel->rd_att->attrs; - tupDesc = rel->rd_att; - - if (!binary) { - out_functions = (func_ptr *) palloc(attr_count * sizeof(func_ptr)); - elements = (Oid *) palloc(attr_count * sizeof(Oid)); - for (i = 0; i < attr_count; i++) { - out_func_oid = (Oid) GetOutputFunction(attr[i]->atttypid); - fmgr_info(out_func_oid, &out_functions[i], &dummy); - elements[i] = GetTypeElement(attr[i]->atttypid); - } - nulls = NULL; /* meaningless, but compiler doesn't know that */ - }else { - elements = NULL; - out_functions = NULL; - nulls = (char *) palloc(attr_count); - for (i = 0; i < attr_count; i++) nulls[i] = ' '; - - /* XXX expensive */ - - ntuples = CountTuples(rel); - fwrite(&ntuples, sizeof(int32), 1, fp); - } - - for (tuple = heap_getnext(scandesc, 0, NULL); - tuple != NULL; - tuple = heap_getnext(scandesc, 0, NULL)) { - - if (oids && !binary) { - fputs(oidout(tuple->t_oid),fp); - fputc(delim[0], fp); - } - - for (i = 0; i < attr_count; i++) { - value = (Datum) - heap_getattr(tuple, InvalidBuffer, i+1, tupDesc, &isnull); - if (!binary) { - if (!isnull) { - string = (char *) (out_functions[i]) (value, elements[i]); - CopyAttributeOut(fp, string, delim); - pfree(string); - } - else - fputs("\\N", fp); /* null indicator */ - - if (i == attr_count - 1) { - fputc('\n', fp); - }else { - /* when copying out, only use the first char of the delim - string */ - fputc(delim[0], fp); - } - }else { - /* - * only interesting thing heap_getattr tells us in this case - * is if we have a null attribute or not. - */ - if (isnull) nulls[i] = 'n'; - } - } - - if (binary) { - int32 null_ct = 0, length; - - for (i = 0; i < attr_count; i++) { - if (nulls[i] == 'n') null_ct++; - } - - length = tuple->t_len - tuple->t_hoff; - fwrite(&length, sizeof(int32), 1, fp); - if (oids) - fwrite((char *) &tuple->t_oid, sizeof(int32), 1, fp); - - fwrite(&null_ct, sizeof(int32), 1, fp); - if (null_ct > 0) { - for (i = 0; i < attr_count; i++) { - if (nulls[i] == 'n') { - fwrite(&i, sizeof(int32), 1, fp); - nulls[i] = ' '; - } - } - } - fwrite((char *) tuple + tuple->t_hoff, length, 1, fp); - } - } - - heap_endscan(scandesc); - if (binary) { - pfree(nulls); - }else { - pfree(out_functions); - pfree(elements); - } - - heap_close(rel); + HeapTuple tuple; + HeapScanDesc scandesc; + + int32 attr_count, + i; + AttributeTupleForm *attr; + func_ptr *out_functions; + int dummy; + Oid out_func_oid; + Oid *elements; + Datum value; + bool isnull; /* The attribute we are copying is null */ + char *nulls; + + /* + * <nulls> is a (dynamically allocated) array with one character per + * attribute in the instance being copied. nulls[I-1] is 'n' if + * Attribute Number I is null, and ' ' otherwise. + * + * <nulls> is meaningful only if we are doing a binary copy. + */ + char *string; + int32 ntuples; + TupleDesc tupDesc; + + scandesc = heap_beginscan(rel, 0, NULL, 0, NULL); + + attr_count = rel->rd_att->natts; + attr = rel->rd_att->attrs; + tupDesc = rel->rd_att; + + if (!binary) + { + out_functions = (func_ptr *) palloc(attr_count * sizeof(func_ptr)); + elements = (Oid *) palloc(attr_count * sizeof(Oid)); + for (i = 0; i < attr_count; i++) + { + out_func_oid = (Oid) GetOutputFunction(attr[i]->atttypid); + fmgr_info(out_func_oid, &out_functions[i], &dummy); + elements[i] = GetTypeElement(attr[i]->atttypid); + } + nulls = NULL; /* meaningless, but compiler doesn't know + * that */ + } + else + { + elements = NULL; + out_functions = NULL; + nulls = (char *) palloc(attr_count); + for (i = 0; i < attr_count; i++) + nulls[i] = ' '; + + /* XXX expensive */ + + ntuples = CountTuples(rel); + fwrite(&ntuples, sizeof(int32), 1, fp); + } + + for (tuple = heap_getnext(scandesc, 0, NULL); + tuple != NULL; + tuple = heap_getnext(scandesc, 0, NULL)) + { + + if (oids && !binary) + { + fputs(oidout(tuple->t_oid), fp); + fputc(delim[0], fp); + } + + for (i = 0; i < attr_count; i++) + { + value = (Datum) + heap_getattr(tuple, InvalidBuffer, i + 1, tupDesc, &isnull); + if (!binary) + { + if (!isnull) + { + string = (char *) (out_functions[i]) (value, elements[i]); + CopyAttributeOut(fp, string, delim); + pfree(string); + } + else + fputs("\\N", fp); /* null indicator */ + + if (i == attr_count - 1) + { + fputc('\n', fp); + } + else + { + + /* + * when copying out, only use the first char of the + * delim string + */ + fputc(delim[0], fp); + } + } + else + { + + /* + * only interesting thing heap_getattr tells us in this + * case is if we have a null attribute or not. + */ + if (isnull) + nulls[i] = 'n'; + } + } + + if (binary) + { + int32 null_ct = 0, + length; + + for (i = 0; i < attr_count; i++) + { + if (nulls[i] == 'n') + null_ct++; + } + + length = tuple->t_len - tuple->t_hoff; + fwrite(&length, sizeof(int32), 1, fp); + if (oids) + fwrite((char *) &tuple->t_oid, sizeof(int32), 1, fp); + + fwrite(&null_ct, sizeof(int32), 1, fp); + if (null_ct > 0) + { + for (i = 0; i < attr_count; i++) + { + if (nulls[i] == 'n') + { + fwrite(&i, sizeof(int32), 1, fp); + nulls[i] = ' '; + } + } + } + fwrite((char *) tuple + tuple->t_hoff, length, 1, fp); + } + } + + heap_endscan(scandesc); + if (binary) + { + pfree(nulls); + } + else + { + pfree(out_functions); + pfree(elements); + } + + heap_close(rel); } static void -CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim) +CopyFrom(Relation rel, bool binary, bool oids, FILE * fp, char *delim) { - HeapTuple tuple; - AttrNumber attr_count; - AttributeTupleForm *attr; - func_ptr *in_functions; - int i, dummy; - Oid in_func_oid; - Datum *values; - char *nulls, *index_nulls; - bool *byval; - bool isnull; - bool has_index; - int done = 0; - char *string = NULL, *ptr; - Relation *index_rels; - int32 len, null_ct, null_id; - int32 ntuples, tuples_read = 0; - bool reading_to_eof = true; - Oid *elements; - FuncIndexInfo *finfo, **finfoP = NULL; - TupleDesc *itupdescArr; - HeapTuple pgIndexTup; - IndexTupleForm *pgIndexP = NULL; - int *indexNatts = NULL; - char *predString; - Node **indexPred = NULL; - TupleDesc rtupdesc; - ExprContext *econtext = NULL; + HeapTuple tuple; + AttrNumber attr_count; + AttributeTupleForm *attr; + func_ptr *in_functions; + int i, + dummy; + Oid in_func_oid; + Datum *values; + char *nulls, + *index_nulls; + bool *byval; + bool isnull; + bool has_index; + int done = 0; + char *string = NULL, + *ptr; + Relation *index_rels; + int32 len, + null_ct, + null_id; + int32 ntuples, + tuples_read = 0; + bool reading_to_eof = true; + Oid *elements; + FuncIndexInfo *finfo, + **finfoP = NULL; + TupleDesc *itupdescArr; + HeapTuple pgIndexTup; + IndexTupleForm *pgIndexP = NULL; + int *indexNatts = NULL; + char *predString; + Node **indexPred = NULL; + TupleDesc rtupdesc; + ExprContext *econtext = NULL; + #ifndef OMIT_PARTIAL_INDEX - TupleTable tupleTable; - TupleTableSlot *slot = NULL; + TupleTable tupleTable; + TupleTableSlot *slot = NULL; + #endif - int natts; - AttrNumber *attnumP; - Datum *idatum; - int n_indices; - InsertIndexResult indexRes; - TupleDesc tupDesc; - Oid loaded_oid; - bool skip_tuple = false; - - tupDesc = RelationGetTupleDescriptor(rel); - attr = tupDesc->attrs; - attr_count = tupDesc->natts; - - has_index = false; - - /* - * This may be a scalar or a functional index. We initialize all - * kinds of arrays here to avoid doing extra work at every tuple - * copy. - */ - - if (rel->rd_rel->relhasindex) { - GetIndexRelations(rel->rd_id, &n_indices, &index_rels); - if (n_indices > 0) { - has_index = true; - itupdescArr = - (TupleDesc *)palloc(n_indices * sizeof(TupleDesc)); - pgIndexP = - (IndexTupleForm *)palloc(n_indices * sizeof(IndexTupleForm)); - indexNatts = (int *) palloc(n_indices * sizeof(int)); - finfo = (FuncIndexInfo *) palloc(n_indices * sizeof(FuncIndexInfo)); - finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *)); - indexPred = (Node **) palloc(n_indices * sizeof(Node*)); - econtext = NULL; - for (i = 0; i < n_indices; i++) { - itupdescArr[i] = RelationGetTupleDescriptor(index_rels[i]); - pgIndexTup = - SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(index_rels[i]->rd_id), - 0,0,0); - Assert(pgIndexTup); - pgIndexP[i] = (IndexTupleForm)GETSTRUCT(pgIndexTup); - for (attnumP = &(pgIndexP[i]->indkey[0]), natts = 0; - *attnumP != InvalidAttrNumber; - attnumP++, natts++); - if (pgIndexP[i]->indproc != InvalidOid) { - FIgetnArgs(&finfo[i]) = natts; - natts = 1; - FIgetProcOid(&finfo[i]) = pgIndexP[i]->indproc; - *(FIgetname(&finfo[i])) = '\0'; - finfoP[i] = &finfo[i]; - } else - finfoP[i] = (FuncIndexInfo *) NULL; - indexNatts[i] = natts; - if (VARSIZE(&pgIndexP[i]->indpred) != 0) { - predString = fmgr(F_TEXTOUT, &pgIndexP[i]->indpred); - indexPred[i] = stringToNode(predString); - pfree(predString); - /* make dummy ExprContext for use by ExecQual */ - if (econtext == NULL) { + int natts; + AttrNumber *attnumP; + Datum *idatum; + int n_indices; + InsertIndexResult indexRes; + TupleDesc tupDesc; + Oid loaded_oid; + bool skip_tuple = false; + + tupDesc = RelationGetTupleDescriptor(rel); + attr = tupDesc->attrs; + attr_count = tupDesc->natts; + + has_index = false; + + /* + * This may be a scalar or a functional index. We initialize all + * kinds of arrays here to avoid doing extra work at every tuple copy. + */ + + if (rel->rd_rel->relhasindex) + { + GetIndexRelations(rel->rd_id, &n_indices, &index_rels); + if (n_indices > 0) + { + has_index = true; + itupdescArr = + (TupleDesc *) palloc(n_indices * sizeof(TupleDesc)); + pgIndexP = + (IndexTupleForm *) palloc(n_indices * sizeof(IndexTupleForm)); + indexNatts = (int *) palloc(n_indices * sizeof(int)); + finfo = (FuncIndexInfo *) palloc(n_indices * sizeof(FuncIndexInfo)); + finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *)); + indexPred = (Node **) palloc(n_indices * sizeof(Node *)); + econtext = NULL; + for (i = 0; i < n_indices; i++) + { + itupdescArr[i] = RelationGetTupleDescriptor(index_rels[i]); + pgIndexTup = + SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(index_rels[i]->rd_id), + 0, 0, 0); + Assert(pgIndexTup); + pgIndexP[i] = (IndexTupleForm) GETSTRUCT(pgIndexTup); + for (attnumP = &(pgIndexP[i]->indkey[0]), natts = 0; + *attnumP != InvalidAttrNumber; + attnumP++, natts++); + if (pgIndexP[i]->indproc != InvalidOid) + { + FIgetnArgs(&finfo[i]) = natts; + natts = 1; + FIgetProcOid(&finfo[i]) = pgIndexP[i]->indproc; + *(FIgetname(&finfo[i])) = '\0'; + finfoP[i] = &finfo[i]; + } + else + finfoP[i] = (FuncIndexInfo *) NULL; + indexNatts[i] = natts; + if (VARSIZE(&pgIndexP[i]->indpred) != 0) + { + predString = fmgr(F_TEXTOUT, &pgIndexP[i]->indpred); + indexPred[i] = stringToNode(predString); + pfree(predString); + /* make dummy ExprContext for use by ExecQual */ + if (econtext == NULL) + { #ifndef OMIT_PARTIAL_INDEX - tupleTable = ExecCreateTupleTable(1); - slot = ExecAllocTableSlot(tupleTable); - econtext = makeNode(ExprContext); - econtext->ecxt_scantuple = slot; - rtupdesc = RelationGetTupleDescriptor(rel); - slot->ttc_tupleDescriptor = rtupdesc; - /* - * There's no buffer associated with heap tuples here, - * so I set the slot's buffer to NULL. Currently, it - * appears that the only way a buffer could be needed - * would be if the partial index predicate referred to - * the "lock" system attribute. If it did, then - * heap_getattr would call HeapTupleGetRuleLock, which - * uses the buffer's descriptor to get the relation id. - * Rather than try to fix this, I'll just disallow - * partial indexes on "lock", which wouldn't be useful - * anyway. --Nels, Nov '92 - */ - /* SetSlotBuffer(slot, (Buffer) NULL); */ - /* SetSlotShouldFree(slot, false); */ - slot->ttc_buffer = (Buffer)NULL; - slot->ttc_shouldFree = false; -#endif /* OMIT_PARTIAL_INDEX */ - } - } else { - indexPred[i] = NULL; - } - } - } - } - - if (!binary) - { - in_functions = (func_ptr *) palloc(attr_count * sizeof(func_ptr)); - elements = (Oid *) palloc(attr_count * sizeof(Oid)); - for (i = 0; i < attr_count; i++) - { - in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid); - fmgr_info(in_func_oid, &in_functions[i], &dummy); - elements[i] = GetTypeElement(attr[i]->atttypid); - } - } - else - { - in_functions = NULL; - elements = NULL; - fread(&ntuples, sizeof(int32), 1, fp); - if (ntuples != 0) reading_to_eof = false; - } - - values = (Datum *) palloc(sizeof(Datum) * attr_count); - nulls = (char *) palloc(attr_count); - index_nulls = (char *) palloc(attr_count); - idatum = (Datum *) palloc(sizeof(Datum) * attr_count); - byval = (bool *) palloc(attr_count * sizeof(bool)); - - for (i = 0; i < attr_count; i++) { - nulls[i] = ' '; - index_nulls[i] = ' '; - byval[i] = (bool) IsTypeByVal(attr[i]->atttypid); - } - + tupleTable = ExecCreateTupleTable(1); + slot = ExecAllocTableSlot(tupleTable); + econtext = makeNode(ExprContext); + econtext->ecxt_scantuple = slot; + rtupdesc = RelationGetTupleDescriptor(rel); + slot->ttc_tupleDescriptor = rtupdesc; + + /* + * There's no buffer associated with heap tuples + * here, so I set the slot's buffer to NULL. + * Currently, it appears that the only way a + * buffer could be needed would be if the partial + * index predicate referred to the "lock" system + * attribute. If it did, then heap_getattr would + * call HeapTupleGetRuleLock, which uses the + * buffer's descriptor to get the relation id. + * Rather than try to fix this, I'll just disallow + * partial indexes on "lock", which wouldn't be + * useful anyway. --Nels, Nov '92 + */ + /* SetSlotBuffer(slot, (Buffer) NULL); */ + /* SetSlotShouldFree(slot, false); */ + slot->ttc_buffer = (Buffer) NULL; + slot->ttc_shouldFree = false; +#endif /* OMIT_PARTIAL_INDEX */ + } + } + else + { + indexPred[i] = NULL; + } + } + } + } + + if (!binary) + { + in_functions = (func_ptr *) palloc(attr_count * sizeof(func_ptr)); + elements = (Oid *) palloc(attr_count * sizeof(Oid)); + for (i = 0; i < attr_count; i++) + { + in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid); + fmgr_info(in_func_oid, &in_functions[i], &dummy); + elements[i] = GetTypeElement(attr[i]->atttypid); + } + } + else + { + in_functions = NULL; + elements = NULL; + fread(&ntuples, sizeof(int32), 1, fp); + if (ntuples != 0) + reading_to_eof = false; + } + + values = (Datum *) palloc(sizeof(Datum) * attr_count); + nulls = (char *) palloc(attr_count); + index_nulls = (char *) palloc(attr_count); + idatum = (Datum *) palloc(sizeof(Datum) * attr_count); + byval = (bool *) palloc(attr_count * sizeof(bool)); + + for (i = 0; i < attr_count; i++) + { + nulls[i] = ' '; + index_nulls[i] = ' '; + byval[i] = (bool) IsTypeByVal(attr[i]->atttypid); + } + #ifdef COPY_DEBUG - lineno = 0; + lineno = 0; #endif - while (!done) { - if (!binary) { + while (!done) + { + if (!binary) + { #ifdef COPY_PATCH - int newline = 0; + int newline = 0; + #endif #ifdef COPY_DEBUG - lineno++; - elog(DEBUG, "line %d", lineno); + lineno++; + elog(DEBUG, "line %d", lineno); #endif - if (oids) { + if (oids) + { #ifdef COPY_PATCH - string = CopyReadAttribute(fp, &isnull, delim, &newline); + string = CopyReadAttribute(fp, &isnull, delim, &newline); #else - string = CopyReadAttribute(fp, &isnull, delim); + string = CopyReadAttribute(fp, &isnull, delim); #endif - if (string == NULL) - done = 1; - else { - loaded_oid = oidin(string); - if (loaded_oid < BootstrapObjectIdData) - elog(WARN, "COPY TEXT: Invalid Oid"); - } - } - for (i = 0; i < attr_count && !done; i++) { + if (string == NULL) + done = 1; + else + { + loaded_oid = oidin(string); + if (loaded_oid < BootstrapObjectIdData) + elog(WARN, "COPY TEXT: Invalid Oid"); + } + } + for (i = 0; i < attr_count && !done; i++) + { #ifdef COPY_PATCH - string = CopyReadAttribute(fp, &isnull, delim, &newline); + string = CopyReadAttribute(fp, &isnull, delim, &newline); #else - string = CopyReadAttribute(fp, &isnull, delim); + string = CopyReadAttribute(fp, &isnull, delim); #endif - if (isnull) { - values[i] = PointerGetDatum(NULL); - nulls[i] = 'n'; - }else if (string == NULL) { - done = 1; - }else { - values[i] = - (Datum)(in_functions[i])(string, - elements[i], - attr[i]->attlen); - /* - * Sanity check - by reference attributes cannot return - * NULL - */ - if (!PointerIsValid(values[i]) && - !(rel->rd_att->attrs[i]->attbyval)) { + if (isnull) + { + values[i] = PointerGetDatum(NULL); + nulls[i] = 'n'; + } + else if (string == NULL) + { + done = 1; + } + else + { + values[i] = + (Datum) (in_functions[i]) (string, + elements[i], + attr[i]->attlen); + + /* + * Sanity check - by reference attributes cannot + * return NULL + */ + if (!PointerIsValid(values[i]) && + !(rel->rd_att->attrs[i]->attbyval)) + { #ifdef COPY_DEBUG - elog(WARN, - "copy from: line %d - Bad file format", lineno); + elog(WARN, + "copy from: line %d - Bad file format", lineno); #else - elog(WARN, "copy from: Bad file format"); + elog(WARN, "copy from: Bad file format"); #endif - } - } - } + } + } + } #ifdef COPY_PATCH - if (!done) { - CopyReadNewline(fp, &newline); - } + if (!done) + { + CopyReadNewline(fp, &newline); + } #endif - }else { /* binary */ - fread(&len, sizeof(int32), 1, fp); - if (feof(fp)) { - done = 1; - }else { - if (oids) { - fread(&loaded_oid, sizeof(int32), 1, fp); - if (loaded_oid < BootstrapObjectIdData) - elog(WARN, "COPY BINARY: Invalid Oid"); - } - fread(&null_ct, sizeof(int32), 1, fp); - if (null_ct > 0) { - for (i = 0; i < null_ct; i++) { - fread(&null_id, sizeof(int32), 1, fp); - nulls[null_id] = 'n'; - } - } - - string = (char *) palloc(len); - fread(string, len, 1, fp); - - ptr = string; - - for (i = 0; i < attr_count; i++) { - if (byval[i] && nulls[i] != 'n') { - - switch(attr[i]->attlen) { - case sizeof(char): - values[i] = (Datum) *(unsigned char *) ptr; - ptr += sizeof(char); - break; - case sizeof(short): - ptr = (char *) SHORTALIGN(ptr); - values[i] = (Datum) *(unsigned short *) ptr; - ptr += sizeof(short); - break; - case sizeof(int32): - ptr = (char *) INTALIGN(ptr); - values[i] = (Datum) *(uint32 *) ptr; - ptr += sizeof(int32); - break; - default: - elog(WARN, "COPY BINARY: impossible size!"); - break; - } - }else if (nulls[i] != 'n') { - switch (attr[i]->attlen) { - case -1: - if (attr[i]->attalign == 'd') - ptr = (char *)DOUBLEALIGN(ptr); - else - ptr = (char *)INTALIGN(ptr); - values[i] = (Datum) ptr; - ptr += * (uint32 *) ptr; - break; - case sizeof(char): - values[i] = (Datum)ptr; - ptr += attr[i]->attlen; - break; - case sizeof(short): - ptr = (char*)SHORTALIGN(ptr); - values[i] = (Datum)ptr; - ptr += attr[i]->attlen; - break; - case sizeof(int32): - ptr = (char*)INTALIGN(ptr); - values[i] = (Datum)ptr; - ptr += attr[i]->attlen; - break; - default: - if (attr[i]->attalign == 'd') - ptr = (char *)DOUBLEALIGN(ptr); - else - ptr = (char *)LONGALIGN(ptr); - values[i] = (Datum) ptr; - ptr += attr[i]->attlen; - } - } - } - } - } - if (done) continue; + } + else + { /* binary */ + fread(&len, sizeof(int32), 1, fp); + if (feof(fp)) + { + done = 1; + } + else + { + if (oids) + { + fread(&loaded_oid, sizeof(int32), 1, fp); + if (loaded_oid < BootstrapObjectIdData) + elog(WARN, "COPY BINARY: Invalid Oid"); + } + fread(&null_ct, sizeof(int32), 1, fp); + if (null_ct > 0) + { + for (i = 0; i < null_ct; i++) + { + fread(&null_id, sizeof(int32), 1, fp); + nulls[null_id] = 'n'; + } + } - /* - * Does it have any sence ? - vadim 12/14/96 - * - tupDesc = CreateTupleDesc(attr_count, attr); - */ - tuple = heap_formtuple(tupDesc, values, nulls); - if (oids) - tuple->t_oid = loaded_oid; - - skip_tuple = false; - /* BEFORE ROW INSERT Triggers */ - if ( rel->trigdesc && - rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0 ) - { - HeapTuple newtuple; - - newtuple = ExecBRInsertTriggers (rel, tuple); - - if ( newtuple == NULL ) /* "do nothing" */ - skip_tuple = true; - else if ( newtuple != tuple ) /* modified by Trigger(s) */ - { - pfree (tuple); - tuple = newtuple; - } - } - - if ( !skip_tuple ) - { - /* ---------------- - * Check the constraints of a tuple - * ---------------- - */ - - if ( rel->rd_att->constr ) - { - HeapTuple newtuple; - - newtuple = ExecConstraints ("CopyFrom", rel, tuple); - - if ( newtuple != tuple ) - { - pfree (tuple); - tuple = newtuple; - } - } - - heap_insert(rel, tuple); - - if (has_index) - { - for (i = 0; i < n_indices; i++) - { - if (indexPred[i] != NULL) - { + string = (char *) palloc(len); + fread(string, len, 1, fp); + + ptr = string; + + for (i = 0; i < attr_count; i++) + { + if (byval[i] && nulls[i] != 'n') + { + + switch (attr[i]->attlen) + { + case sizeof(char): + values[i] = (Datum) * (unsigned char *) ptr; + ptr += sizeof(char); + break; + case sizeof(short): + ptr = (char *) SHORTALIGN(ptr); + values[i] = (Datum) * (unsigned short *) ptr; + ptr += sizeof(short); + break; + case sizeof(int32): + ptr = (char *) INTALIGN(ptr); + values[i] = (Datum) * (uint32 *) ptr; + ptr += sizeof(int32); + break; + default: + elog(WARN, "COPY BINARY: impossible size!"); + break; + } + } + else if (nulls[i] != 'n') + { + switch (attr[i]->attlen) + { + case -1: + if (attr[i]->attalign == 'd') + ptr = (char *) DOUBLEALIGN(ptr); + else + ptr = (char *) INTALIGN(ptr); + values[i] = (Datum) ptr; + ptr += *(uint32 *) ptr; + break; + case sizeof(char): + values[i] = (Datum) ptr; + ptr += attr[i]->attlen; + break; + case sizeof(short): + ptr = (char *) SHORTALIGN(ptr); + values[i] = (Datum) ptr; + ptr += attr[i]->attlen; + break; + case sizeof(int32): + ptr = (char *) INTALIGN(ptr); + values[i] = (Datum) ptr; + ptr += attr[i]->attlen; + break; + default: + if (attr[i]->attalign == 'd') + ptr = (char *) DOUBLEALIGN(ptr); + else + ptr = (char *) LONGALIGN(ptr); + values[i] = (Datum) ptr; + ptr += attr[i]->attlen; + } + } + } + } + } + if (done) + continue; + + /* + * Does it have any sence ? - vadim 12/14/96 + * + * tupDesc = CreateTupleDesc(attr_count, attr); + */ + tuple = heap_formtuple(tupDesc, values, nulls); + if (oids) + tuple->t_oid = loaded_oid; + + skip_tuple = false; + /* BEFORE ROW INSERT Triggers */ + if (rel->trigdesc && + rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) + { + HeapTuple newtuple; + + newtuple = ExecBRInsertTriggers(rel, tuple); + + if (newtuple == NULL) /* "do nothing" */ + skip_tuple = true; + else if (newtuple != tuple) /* modified by Trigger(s) */ + { + pfree(tuple); + tuple = newtuple; + } + } + + if (!skip_tuple) + { + /* ---------------- + * Check the constraints of a tuple + * ---------------- + */ + + if (rel->rd_att->constr) + { + HeapTuple newtuple; + + newtuple = ExecConstraints("CopyFrom", rel, tuple); + + if (newtuple != tuple) + { + pfree(tuple); + tuple = newtuple; + } + } + + heap_insert(rel, tuple); + + if (has_index) + { + for (i = 0; i < n_indices; i++) + { + if (indexPred[i] != NULL) + { #ifndef OMIT_PARTIAL_INDEX - /* - * if tuple doesn't satisfy predicate, - * don't update index - */ - slot->val = tuple; - /*SetSlotContents(slot, tuple); */ - if (ExecQual((List*)indexPred[i], econtext) == false) - continue; -#endif /* OMIT_PARTIAL_INDEX */ - } - FormIndexDatum(indexNatts[i], - (AttrNumber *)&(pgIndexP[i]->indkey[0]), - tuple, - tupDesc, - InvalidBuffer, - idatum, - index_nulls, - finfoP[i]); - indexRes = index_insert(index_rels[i], idatum, index_nulls, - &(tuple->t_ctid), rel); - if (indexRes) pfree(indexRes); - } - } - /* AFTER ROW INSERT Triggers */ - if ( rel->trigdesc && - rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 ) - ExecARInsertTriggers (rel, tuple); - } - - if (binary) pfree(string); - - for (i = 0; i < attr_count; i++) { - if (!byval[i] && nulls[i] != 'n') { - if (!binary) pfree((void*)values[i]); - }else if (nulls[i] == 'n') { - nulls[i] = ' '; - } - } - - pfree(tuple); - tuples_read++; - - if (!reading_to_eof && ntuples == tuples_read) done = true; - } - pfree(values); - if (!binary) pfree(in_functions); - pfree(nulls); - pfree(byval); - heap_close(rel); + + /* + * if tuple doesn't satisfy predicate, don't + * update index + */ + slot->val = tuple; + /* SetSlotContents(slot, tuple); */ + if (ExecQual((List *) indexPred[i], econtext) == false) + continue; +#endif /* OMIT_PARTIAL_INDEX */ + } + FormIndexDatum(indexNatts[i], + (AttrNumber *) & (pgIndexP[i]->indkey[0]), + tuple, + tupDesc, + InvalidBuffer, + idatum, + index_nulls, + finfoP[i]); + indexRes = index_insert(index_rels[i], idatum, index_nulls, + &(tuple->t_ctid), rel); + if (indexRes) + pfree(indexRes); + } + } + /* AFTER ROW INSERT Triggers */ + if (rel->trigdesc && + rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0) + ExecARInsertTriggers(rel, tuple); + } + + if (binary) + pfree(string); + + for (i = 0; i < attr_count; i++) + { + if (!byval[i] && nulls[i] != 'n') + { + if (!binary) + pfree((void *) values[i]); + } + else if (nulls[i] == 'n') + { + nulls[i] = ' '; + } + } + + pfree(tuple); + tuples_read++; + + if (!reading_to_eof && ntuples == tuples_read) + done = true; + } + pfree(values); + if (!binary) + pfree(in_functions); + pfree(nulls); + pfree(byval); + heap_close(rel); } -static Oid +static Oid GetOutputFunction(Oid type) { - HeapTuple typeTuple; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type), - 0,0,0); - - if (HeapTupleIsValid(typeTuple)) - return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput); - - elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type); - return(InvalidOid); + HeapTuple typeTuple; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type), + 0, 0, 0); + + if (HeapTupleIsValid(typeTuple)) + return ((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput); + + elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type); + return (InvalidOid); } -static Oid +static Oid GetTypeElement(Oid type) { - HeapTuple typeTuple; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type), - 0,0,0); - - - if (HeapTupleIsValid(typeTuple)) - return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem); - - elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type); - return(InvalidOid); + HeapTuple typeTuple; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type), + 0, 0, 0); + + + if (HeapTupleIsValid(typeTuple)) + return ((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem); + + elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type); + return (InvalidOid); } -static Oid +static Oid GetInputFunction(Oid type) { - HeapTuple typeTuple; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type), - 0,0,0); - - if (HeapTupleIsValid(typeTuple)) - return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typinput); - - elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type); - return(InvalidOid); + HeapTuple typeTuple; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type), + 0, 0, 0); + + if (HeapTupleIsValid(typeTuple)) + return ((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typinput); + + elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type); + return (InvalidOid); } -static Oid +static Oid IsTypeByVal(Oid type) { - HeapTuple typeTuple; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type), - 0,0,0); - - if (HeapTupleIsValid(typeTuple)) - return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typbyval); - - elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type); - - return(InvalidOid); + HeapTuple typeTuple; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type), + 0, 0, 0); + + if (HeapTupleIsValid(typeTuple)) + return ((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typbyval); + + elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type); + + return (InvalidOid); } -/* +/* * Given the OID of a relation, return an array of index relation descriptors * and the number of index relations. These relation descriptors are open * using heap_open(). @@ -779,71 +900,77 @@ IsTypeByVal(Oid type) * Space for the array itself is palloc'ed. */ -typedef struct rel_list { - Oid index_rel_oid; - struct rel_list *next; -} RelationList; +typedef struct rel_list +{ + Oid index_rel_oid; + struct rel_list *next; +} RelationList; static void GetIndexRelations(Oid main_relation_oid, - int *n_indices, - Relation **index_rels) + int *n_indices, + Relation ** index_rels) { - RelationList *head, *scan; - Relation pg_index_rel; - HeapScanDesc scandesc; - Oid index_relation_oid; - HeapTuple tuple; - TupleDesc tupDesc; - int i; - bool isnull; - - pg_index_rel = heap_openr(IndexRelationName); - scandesc = heap_beginscan(pg_index_rel, 0, NULL, 0, NULL); - tupDesc = RelationGetTupleDescriptor(pg_index_rel); - - *n_indices = 0; - - head = (RelationList *) palloc(sizeof(RelationList)); - scan = head; - head->next = NULL; - - for (tuple = heap_getnext(scandesc, 0, NULL); - tuple != NULL; - tuple = heap_getnext(scandesc, 0, NULL)) { - - index_relation_oid = - (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer, 2, - tupDesc, &isnull)); - if (index_relation_oid == main_relation_oid) { - scan->index_rel_oid = - (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer, - Anum_pg_index_indexrelid, - tupDesc, &isnull)); - (*n_indices)++; - scan->next = (RelationList *) palloc(sizeof(RelationList)); - scan = scan->next; - } - } - - heap_endscan(scandesc); - heap_close(pg_index_rel); - - /* We cannot trust to relhasindex of the main_relation now, so... */ - if ( *n_indices == 0 ) - return; - - *index_rels = (Relation *) palloc(*n_indices * sizeof(Relation)); - - for (i = 0, scan = head; i < *n_indices; i++, scan = scan->next) { - (*index_rels)[i] = index_open(scan->index_rel_oid); - } - - for (i = 0, scan = head; i < *n_indices + 1; i++) { - scan = head->next; - pfree(head); - head = scan; - } + RelationList *head, + *scan; + Relation pg_index_rel; + HeapScanDesc scandesc; + Oid index_relation_oid; + HeapTuple tuple; + TupleDesc tupDesc; + int i; + bool isnull; + + pg_index_rel = heap_openr(IndexRelationName); + scandesc = heap_beginscan(pg_index_rel, 0, NULL, 0, NULL); + tupDesc = RelationGetTupleDescriptor(pg_index_rel); + + *n_indices = 0; + + head = (RelationList *) palloc(sizeof(RelationList)); + scan = head; + head->next = NULL; + + for (tuple = heap_getnext(scandesc, 0, NULL); + tuple != NULL; + tuple = heap_getnext(scandesc, 0, NULL)) + { + + index_relation_oid = + (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer, 2, + tupDesc, &isnull)); + if (index_relation_oid == main_relation_oid) + { + scan->index_rel_oid = + (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer, + Anum_pg_index_indexrelid, + tupDesc, &isnull)); + (*n_indices)++; + scan->next = (RelationList *) palloc(sizeof(RelationList)); + scan = scan->next; + } + } + + heap_endscan(scandesc); + heap_close(pg_index_rel); + + /* We cannot trust to relhasindex of the main_relation now, so... */ + if (*n_indices == 0) + return; + + *index_rels = (Relation *) palloc(*n_indices * sizeof(Relation)); + + for (i = 0, scan = head; i < *n_indices; i++, scan = scan->next) + { + (*index_rels)[i] = index_open(scan->index_rel_oid); + } + + for (i = 0, scan = head; i < *n_indices + 1; i++) + { + scan = head->next; + pfree(head); + head = scan; + } } #define EXT_ATTLEN 5*8192 @@ -851,20 +978,22 @@ GetIndexRelations(Oid main_relation_oid, /* returns 1 is c is in s */ -static bool -inString(char c, char* s) +static bool +inString(char c, char *s) { - int i; - - if (s) { - i = 0; - while (s[i] != '\0') { - if (s[i] == c) - return 1; - i++; - } - } - return 0; + int i; + + if (s) + { + i = 0; + while (s[i] != '\0') + { + if (s[i] == c) + return 1; + i++; + } + } + return 0; } #ifdef COPY_PATCH @@ -873,19 +1002,21 @@ inString(char c, char* s) */ void -CopyReadNewline(FILE *fp, int *newline) +CopyReadNewline(FILE * fp, int *newline) { - if (!*newline) { + if (!*newline) + { #ifdef COPY_DEBUG - elog(NOTICE, "CopyReadNewline: line %d - extra fields ignored", - lineno); + elog(NOTICE, "CopyReadNewline: line %d - extra fields ignored", + lineno); #else - elog(NOTICE, "CopyReadNewline: line - extra fields ignored"); + elog(NOTICE, "CopyReadNewline: line - extra fields ignored"); #endif - while (!feof(fp) && (getc(fp) != '\n')); - } - *newline = 0; + while (!feof(fp) && (getc(fp) != '\n')); + } + *newline = 0; } + #endif /* @@ -895,148 +1026,167 @@ CopyReadNewline(FILE *fp, int *newline) * can be used as standard input. */ -static char * +static char * #ifdef COPY_PATCH -CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline) +CopyReadAttribute(FILE * fp, bool * isnull, char *delim, int *newline) #else -CopyReadAttribute(FILE *fp, bool *isnull, char *delim) +CopyReadAttribute(FILE * fp, bool * isnull, char *delim) #endif { - static char attribute[EXT_ATTLEN]; - char c; - int done = 0; - int i = 0; - + static char attribute[EXT_ATTLEN]; + char c; + int done = 0; + int i = 0; + #ifdef COPY_PATCH - /* if last delimiter was a newline return a NULL attribute */ - if (*newline) { - *isnull = (bool) true; - return(NULL); - } + /* if last delimiter was a newline return a NULL attribute */ + if (*newline) + { + *isnull = (bool) true; + return (NULL); + } #endif - *isnull = (bool) false; /* set default */ - if (feof(fp)) - return(NULL); - - while (!done) { - c = getc(fp); - - if (feof(fp)) - return(NULL); - else if (c == '\\') { - c = getc(fp); - if (feof(fp)) - return(NULL); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': { - int val; - val = VALUE(c); - c = getc(fp); - if (ISOCTAL(c)) { - val = (val<<3) + VALUE(c); - c = getc(fp); - if (ISOCTAL(c)) { - val = (val<<3) + VALUE(c); - } else { - if (feof(fp)) - return(NULL); - ungetc(c, fp); - } - } else { - if (feof(fp)) - return(NULL); - ungetc(c, fp); - } - c = val & 0377; - } - break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case 'N': - attribute[0] = '\0'; /* just to be safe */ - *isnull = (bool) true; - break; - case '.': - c = getc(fp); - if (c != '\n') - elog(WARN, "CopyReadAttribute - end of record marker corrupted"); - return(NULL); - break; - } - }else if (inString(c,delim) || c == '\n') { + *isnull = (bool) false; /* set default */ + if (feof(fp)) + return (NULL); + + while (!done) + { + c = getc(fp); + + if (feof(fp)) + return (NULL); + else if (c == '\\') + { + c = getc(fp); + if (feof(fp)) + return (NULL); + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int val; + + val = VALUE(c); + c = getc(fp); + if (ISOCTAL(c)) + { + val = (val << 3) + VALUE(c); + c = getc(fp); + if (ISOCTAL(c)) + { + val = (val << 3) + VALUE(c); + } + else + { + if (feof(fp)) + return (NULL); + ungetc(c, fp); + } + } + else + { + if (feof(fp)) + return (NULL); + ungetc(c, fp); + } + c = val & 0377; + } + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'N': + attribute[0] = '\0'; /* just to be safe */ + *isnull = (bool) true; + break; + case '.': + c = getc(fp); + if (c != '\n') + elog(WARN, "CopyReadAttribute - end of record marker corrupted"); + return (NULL); + break; + } + } + else if (inString(c, delim) || c == '\n') + { #ifdef COPY_PATCH - if (c == '\n') { - *newline = 1; - } + if (c == '\n') + { + *newline = 1; + } #endif - done = 1; - } - if (!done) attribute[i++] = c; - if (i == EXT_ATTLEN - 1) - elog(WARN, "CopyReadAttribute - attribute length too long"); - } - attribute[i] = '\0'; - return(&attribute[0]); + done = 1; + } + if (!done) + attribute[i++] = c; + if (i == EXT_ATTLEN - 1) + elog(WARN, "CopyReadAttribute - attribute length too long"); + } + attribute[i] = '\0'; + return (&attribute[0]); } static void -CopyAttributeOut(FILE *fp, char *string, char *delim) +CopyAttributeOut(FILE * fp, char *string, char *delim) { - char c; - int is_array = false; - int len = strlen(string); - - /* XXX - This is a kludge, we should check the data type */ - if (len && (string[0] == '{') && (string[len-1] == '}')) - is_array = true; - - for ( ; (c = *string) != '\0'; string++) { - if (c == delim[0] || c == '\n' || - (c == '\\' && !is_array)) - fputc('\\', fp); - else - if (c == '\\' && is_array) - if (*(string+1) == '\\') { - /* translate \\ to \\\\ */ - fputc('\\', fp); - fputc('\\', fp); - fputc('\\', fp); - string++; - } else if (*(string+1) == '"') { - /* translate \" to \\\" */ - fputc('\\', fp); - fputc('\\', fp); - } - fputc(*string, fp); - } + char c; + int is_array = false; + int len = strlen(string); + + /* XXX - This is a kludge, we should check the data type */ + if (len && (string[0] == '{') && (string[len - 1] == '}')) + is_array = true; + + for (; (c = *string) != '\0'; string++) + { + if (c == delim[0] || c == '\n' || + (c == '\\' && !is_array)) + fputc('\\', fp); + else if (c == '\\' && is_array) + if (*(string + 1) == '\\') + { + /* translate \\ to \\\\ */ + fputc('\\', fp); + fputc('\\', fp); + fputc('\\', fp); + string++; + } + else if (*(string + 1) == '"') + { + /* translate \" to \\\" */ + fputc('\\', fp); + fputc('\\', fp); + } + fputc(*string, fp); + } } /* - * Returns the number of tuples in a relation. Unfortunately, currently + * Returns the number of tuples in a relation. Unfortunately, currently * must do a scan of the entire relation to determine this. * * relation is expected to be an open relation descriptor. @@ -1044,17 +1194,17 @@ CopyAttributeOut(FILE *fp, char *string, char *delim) static int CountTuples(Relation relation) { - HeapScanDesc scandesc; - HeapTuple tuple; - - int i; - - scandesc = heap_beginscan(relation, 0, NULL, 0, NULL); - - for (tuple = heap_getnext(scandesc, 0, NULL), i = 0; - tuple != NULL; - tuple = heap_getnext(scandesc, 0, NULL), i++) - ; - heap_endscan(scandesc); - return(i); + HeapScanDesc scandesc; + HeapTuple tuple; + + int i; + + scandesc = heap_beginscan(relation, 0, NULL, 0, NULL); + + for (tuple = heap_getnext(scandesc, 0, NULL), i = 0; + tuple != NULL; + tuple = heap_getnext(scandesc, 0, NULL), i++) + ; + heap_endscan(scandesc); + return (i); } diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c index 248aaa3e768..92641ca70d6 100644 --- a/src/backend/commands/creatinh.c +++ b/src/backend/commands/creatinh.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * creatinh.c-- - * POSTGRES create/destroy relation with inheritance utility code. + * POSTGRES create/destroy relation with inheritance utility code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.14 1997/08/22 03:03:56 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.15 1997/09/07 04:40:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -29,623 +29,661 @@ #include <catalog/pg_ipl.h> /* ---------------- - * local stuff + * local stuff * ---------------- */ -static int checkAttrExists(char *attributeName, - char *attributeType, List *schema); -static List *MergeAttributes(List *schema, List *supers, List **supconstr); -static void StoreCatalogInheritance(Oid relationId, List *supers); +static int +checkAttrExists(char *attributeName, + char *attributeType, List * schema); +static List *MergeAttributes(List * schema, List * supers, List ** supconstr); +static void StoreCatalogInheritance(Oid relationId, List * supers); /* ---------------------------------------------------------------- - * DefineRelation -- - * Creates a new relation. + * DefineRelation -- + * Creates a new relation. * ---------------------------------------------------------------- */ void -DefineRelation(CreateStmt *stmt) +DefineRelation(CreateStmt * stmt) { - char *relname = palloc(NAMEDATALEN); - List *schema = stmt->tableElts; - int numberOfAttributes; - Oid relationId; - char archChar; - List *inheritList = NULL; - char *archiveName = NULL; - TupleDesc descriptor; - List *constraints; - int heaploc, archloc; - - char* typename = NULL; /* the typename of this relation. not useod for now */ - - if ( strlen(stmt->relname) >= NAMEDATALEN) - elog(WARN, "the relation name %s is >= %d characters long", stmt->relname, - NAMEDATALEN); - strNcpy(relname,stmt->relname,NAMEDATALEN-1); /* make full length for copy */ - - /* ---------------- - * Handle parameters - * XXX parameter handling missing below. - * ---------------- - */ - inheritList = stmt->inhRelnames; - - /* ---------------- - * determine archive mode - * XXX use symbolic constants... - * ---------------- - */ - archChar = 'n'; - - switch (stmt->archiveType) { - case ARCH_NONE: + char *relname = palloc(NAMEDATALEN); + List *schema = stmt->tableElts; + int numberOfAttributes; + Oid relationId; + char archChar; + List *inheritList = NULL; + char *archiveName = NULL; + TupleDesc descriptor; + List *constraints; + int heaploc, + archloc; + + char *typename = NULL; /* the typename of this relation. + * not useod for now */ + + if (strlen(stmt->relname) >= NAMEDATALEN) + elog(WARN, "the relation name %s is >= %d characters long", stmt->relname, + NAMEDATALEN); + strNcpy(relname, stmt->relname, NAMEDATALEN - 1); /* make full length for + * copy */ + + /* ---------------- + * Handle parameters + * XXX parameter handling missing below. + * ---------------- + */ + inheritList = stmt->inhRelnames; + + /* ---------------- + * determine archive mode + * XXX use symbolic constants... + * ---------------- + */ archChar = 'n'; - break; - case ARCH_LIGHT: - archChar = 'l'; - break; - case ARCH_HEAVY: - archChar = 'h'; - break; - default: - elog(WARN, "Botched archive mode %d, ignoring", - stmt->archiveType); - break; - } - - if (stmt->location == -1) - heaploc = 0; - else - heaploc = stmt->location; - - /* - * For now, any user-defined relation defaults to the magnetic - * disk storgage manager. --mao 2 july 91 - */ - if (stmt->archiveLoc == -1) { - archloc = 0; - } else { - if (archChar == 'n') { - elog(WARN, "Set archive location, but not mode, for %s", - relname); + + switch (stmt->archiveType) + { + case ARCH_NONE: + archChar = 'n'; + break; + case ARCH_LIGHT: + archChar = 'l'; + break; + case ARCH_HEAVY: + archChar = 'h'; + break; + default: + elog(WARN, "Botched archive mode %d, ignoring", + stmt->archiveType); + break; + } + + if (stmt->location == -1) + heaploc = 0; + else + heaploc = stmt->location; + + /* + * For now, any user-defined relation defaults to the magnetic disk + * storgage manager. --mao 2 july 91 + */ + if (stmt->archiveLoc == -1) + { + archloc = 0; + } + else + { + if (archChar == 'n') + { + elog(WARN, "Set archive location, but not mode, for %s", + relname); + } + archloc = stmt->archiveLoc; + } + + /* ---------------- + * generate relation schema, including inherited attributes. + * ---------------- + */ + schema = MergeAttributes(schema, inheritList, &constraints); + constraints = nconc(constraints, stmt->constraints); + + numberOfAttributes = length(schema); + if (numberOfAttributes <= 0) + { + elog(WARN, "DefineRelation: %s", + "please inherit from a relation or define an attribute"); } - archloc = stmt->archiveLoc; - } - - /* ---------------- - * generate relation schema, including inherited attributes. - * ---------------- - */ - schema = MergeAttributes(schema, inheritList, &constraints); - constraints = nconc (constraints, stmt->constraints); - - numberOfAttributes = length(schema); - if (numberOfAttributes <= 0) { - elog(WARN, "DefineRelation: %s", - "please inherit from a relation or define an attribute"); - } - - /* ---------------- - * create a relation descriptor from the relation schema - * and create the relation. - * ---------------- - */ - descriptor = BuildDescForRelation(schema, relname); - - if ( constraints != NIL ) - { - List *entry; - int nconstr = length (constraints); - ConstrCheck *check = (ConstrCheck *) palloc (nconstr * sizeof (ConstrCheck)); - int ncheck = 0; - int i; - - foreach (entry, constraints) - { - ConstraintDef *cdef = (ConstraintDef *) lfirst (entry); - - if ( cdef->type == CONSTR_CHECK ) - { - if ( cdef->name != NULL ) - { - for (i = 0; i < ncheck; i++) - { - if ( strcmp (check[i].ccname, cdef->name) == 0 ) - elog (WARN, "DefineRelation: name (%s) of CHECK constraint duplicated", cdef->name); - } - check[ncheck].ccname = cdef->name; - } - else - { - check[ncheck].ccname = (char*) palloc (NAMEDATALEN); - sprintf (check[ncheck].ccname, "$%d", ncheck + 1); - } - check[ncheck].ccbin = NULL; - check[ncheck].ccsrc = (char*) cdef->def; - ncheck++; - } - } - if ( ncheck > 0 ) - { - if ( ncheck < nconstr ) - check = (ConstrCheck *) repalloc (check, ncheck * sizeof (ConstrCheck)); - if ( descriptor->constr == NULL ) - { - descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); - descriptor->constr->num_defval = 0; - descriptor->constr->has_not_null = false; - } - descriptor->constr->num_check = ncheck; - descriptor->constr->check = check; - } - } - - relationId = heap_create(relname, - typename, - archChar, - heaploc, - descriptor); - - StoreCatalogInheritance(relationId, inheritList); - - /* - * create an archive relation if necessary - */ - if (archChar != 'n') - { - TupleDesc tupdesc; + + /* ---------------- + * create a relation descriptor from the relation schema + * and create the relation. + * ---------------- + */ + descriptor = BuildDescForRelation(schema, relname); + + if (constraints != NIL) + { + List *entry; + int nconstr = length(constraints); + ConstrCheck *check = (ConstrCheck *) palloc(nconstr * sizeof(ConstrCheck)); + int ncheck = 0; + int i; + + foreach(entry, constraints) + { + ConstraintDef *cdef = (ConstraintDef *) lfirst(entry); + + if (cdef->type == CONSTR_CHECK) + { + if (cdef->name != NULL) + { + for (i = 0; i < ncheck; i++) + { + if (strcmp(check[i].ccname, cdef->name) == 0) + elog(WARN, "DefineRelation: name (%s) of CHECK constraint duplicated", cdef->name); + } + check[ncheck].ccname = cdef->name; + } + else + { + check[ncheck].ccname = (char *) palloc(NAMEDATALEN); + sprintf(check[ncheck].ccname, "$%d", ncheck + 1); + } + check[ncheck].ccbin = NULL; + check[ncheck].ccsrc = (char *) cdef->def; + ncheck++; + } + } + if (ncheck > 0) + { + if (ncheck < nconstr) + check = (ConstrCheck *) repalloc(check, ncheck * sizeof(ConstrCheck)); + if (descriptor->constr == NULL) + { + descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); + descriptor->constr->num_defval = 0; + descriptor->constr->has_not_null = false; + } + descriptor->constr->num_check = ncheck; + descriptor->constr->check = check; + } + } + + relationId = heap_create(relname, + typename, + archChar, + heaploc, + descriptor); + + StoreCatalogInheritance(relationId, inheritList); + /* - * Need to create an archive relation for this heap relation. - * We cobble up the command by hand, and increment the command - * counter ourselves. + * create an archive relation if necessary */ - - CommandCounterIncrement(); - archiveName = MakeArchiveName(relationId); - - tupdesc = CreateTupleDescCopy (descriptor); /* get rid of constraints */ - (void) heap_create(archiveName, - typename, - 'n', /* archive isn't archived */ - archloc, - tupdesc); - - FreeTupleDesc (tupdesc); - FreeTupleDesc (descriptor); - pfree(archiveName); - } + if (archChar != 'n') + { + TupleDesc tupdesc; + + /* + * Need to create an archive relation for this heap relation. We + * cobble up the command by hand, and increment the command + * counter ourselves. + */ + + CommandCounterIncrement(); + archiveName = MakeArchiveName(relationId); + + tupdesc = CreateTupleDescCopy(descriptor); /* get rid of + * constraints */ + (void) heap_create(archiveName, + typename, + 'n', /* archive isn't archived */ + archloc, + tupdesc); + + FreeTupleDesc(tupdesc); + FreeTupleDesc(descriptor); + pfree(archiveName); + } } /* * RemoveRelation -- - * Deletes a new relation. + * Deletes a new relation. * * Exceptions: - * BadArg if name is invalid. + * BadArg if name is invalid. * * Note: - * If the relation has indices defined on it, then the index relations + * If the relation has indices defined on it, then the index relations * themselves will be destroyed, too. */ void RemoveRelation(char *name) { - AssertArg(name); - heap_destroy(name); + AssertArg(name); + heap_destroy(name); } /* * MergeAttributes -- - * Returns new schema given initial schema and supers. + * Returns new schema given initial schema and supers. * * * 'schema' is the column/attribute definition for the table. (It's a list - * of ColumnDef's.) It is destructively changed. + * of ColumnDef's.) It is destructively changed. * 'inheritList' is the list of inherited relations (a list of Value(str)'s). * * Notes: - * The order in which the attributes are inherited is very important. - * Intuitively, the inherited attributes should come first. If a table - * inherits from multiple parents, the order of those attributes are - * according to the order of the parents specified in CREATE TABLE. + * The order in which the attributes are inherited is very important. + * Intuitively, the inherited attributes should come first. If a table + * inherits from multiple parents, the order of those attributes are + * according to the order of the parents specified in CREATE TABLE. * - * Here's an example: + * Here's an example: * - * create table person (name text, age int4, location point); - * create table emp (salary int4, manager char16) inherits(person); - * create table student (gpa float8) inherits (person); - * create table stud_emp (percent int4) inherits (emp, student); + * create table person (name text, age int4, location point); + * create table emp (salary int4, manager char16) inherits(person); + * create table student (gpa float8) inherits (person); + * create table stud_emp (percent int4) inherits (emp, student); * - * the order of the attributes of stud_emp is as follow: + * the order of the attributes of stud_emp is as follow: * * - * person {1:name, 2:age, 3:location} - * / \ - * {6:gpa} student emp {4:salary, 5:manager} - * \ / - * stud_emp {7:percent} + * person {1:name, 2:age, 3:location} + * / \ + * {6:gpa} student emp {4:salary, 5:manager} + * \ / + * stud_emp {7:percent} */ -static List * -MergeAttributes(List *schema, List *supers, List **supconstr) +static List * +MergeAttributes(List * schema, List * supers, List ** supconstr) { - List *entry; - List *inhSchema = NIL; - List *constraints = NIL; - - /* - * Validates that there are no duplications. - * Validity checking of types occurs later. - */ - foreach (entry, schema) { - List *rest; - ColumnDef *coldef = lfirst(entry); - - foreach (rest, lnext(entry)) { - /* - * check for duplicated relation names - */ - ColumnDef *restdef = lfirst(rest); - - if (!strcmp(coldef->colname, restdef->colname)) { - elog(WARN, "attribute \"%s\" duplicated", - coldef->colname); - } - } - } - foreach (entry, supers) { - List *rest; - - foreach (rest, lnext(entry)) { - if (!strcmp(strVal(lfirst(entry)), strVal(lfirst(rest)))) { - elog(WARN, "relation \"%s\" duplicated", - strVal(lfirst(entry))); - } - } - } - - /* - * merge the inherited attributes into the schema - */ - foreach (entry, supers) { - char *name = strVal(lfirst(entry)); - Relation relation; - List *partialResult = NIL; - AttrNumber attrno; - TupleDesc tupleDesc; - TupleConstr *constr; - - relation = heap_openr(name); - if (relation==NULL) { - elog(WARN, - "MergeAttr: Can't inherit from non-existent superclass '%s'", - name); - } - if ( relation->rd_rel->relkind == 'S' ) + List *entry; + List *inhSchema = NIL; + List *constraints = NIL; + + /* + * Validates that there are no duplications. Validity checking of + * types occurs later. + */ + foreach(entry, schema) { - elog(WARN, "MergeAttr: Can't inherit from sequence superclass '%s'", - name); - } - tupleDesc = RelationGetTupleDescriptor(relation); - constr = tupleDesc->constr; - - for (attrno = relation->rd_rel->relnatts - 1; attrno >= 0; attrno--) { - AttributeTupleForm attribute = tupleDesc->attrs[attrno]; - char *attributeName; - char *attributeType; - HeapTuple tuple; - ColumnDef *def; - TypeName *typename; - - /* - * form name, type and constraints - */ - attributeName = (attribute->attname).data; - tuple = - SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(attribute->atttypid), - 0,0,0); - AssertState(HeapTupleIsValid(tuple)); - attributeType = - (((TypeTupleForm)GETSTRUCT(tuple))->typname).data; - /* - * check validity - * - */ - if (checkAttrExists(attributeName, attributeType, inhSchema) || - checkAttrExists(attributeName, attributeType, schema)) { - /* - * this entry already exists - */ - continue; - } - - /* - * add an entry to the schema - */ - def = makeNode(ColumnDef); - typename = makeNode(TypeName); - def->colname = pstrdup(attributeName); - typename->name = pstrdup(attributeType); - def->typename = typename; - def->is_not_null = attribute->attnotnull; - def->defval = NULL; - if ( attribute->atthasdef ) - { - AttrDefault *attrdef = constr->defval; - int i; - - Assert ( constr != NULL && constr->num_defval > 0 ); - - for (i = 0; i < constr->num_defval; i++) - { - if ( attrdef[i].adnum != attrno + 1 ) - continue; - def->defval = pstrdup (attrdef[i].adsrc); - break; - } - Assert ( def->defval != NULL ); - } - partialResult = lcons(def, partialResult); + List *rest; + ColumnDef *coldef = lfirst(entry); + + foreach(rest, lnext(entry)) + { + + /* + * check for duplicated relation names + */ + ColumnDef *restdef = lfirst(rest); + + if (!strcmp(coldef->colname, restdef->colname)) + { + elog(WARN, "attribute \"%s\" duplicated", + coldef->colname); + } + } } - - if ( constr && constr->num_check > 0 ) + foreach(entry, supers) { - ConstrCheck *check = constr->check; - int i; - - for (i = 0; i < constr->num_check; i++) - { - ConstraintDef *cdef = (ConstraintDef *) palloc (sizeof (ConstraintDef)); - - cdef->type = CONSTR_CHECK; - if ( check[i].ccname[0] == '$' ) - cdef->name = NULL; - else - cdef->name = pstrdup (check[i].ccname); - cdef->def = (void*) pstrdup (check[i].ccsrc); - constraints = lappend (constraints, cdef); - } + List *rest; + + foreach(rest, lnext(entry)) + { + if (!strcmp(strVal(lfirst(entry)), strVal(lfirst(rest)))) + { + elog(WARN, "relation \"%s\" duplicated", + strVal(lfirst(entry))); + } + } } - + /* - * iteration cleanup and result collection + * merge the inherited attributes into the schema */ - heap_close(relation); + foreach(entry, supers) + { + char *name = strVal(lfirst(entry)); + Relation relation; + List *partialResult = NIL; + AttrNumber attrno; + TupleDesc tupleDesc; + TupleConstr *constr; + + relation = heap_openr(name); + if (relation == NULL) + { + elog(WARN, + "MergeAttr: Can't inherit from non-existent superclass '%s'", + name); + } + if (relation->rd_rel->relkind == 'S') + { + elog(WARN, "MergeAttr: Can't inherit from sequence superclass '%s'", + name); + } + tupleDesc = RelationGetTupleDescriptor(relation); + constr = tupleDesc->constr; + + for (attrno = relation->rd_rel->relnatts - 1; attrno >= 0; attrno--) + { + AttributeTupleForm attribute = tupleDesc->attrs[attrno]; + char *attributeName; + char *attributeType; + HeapTuple tuple; + ColumnDef *def; + TypeName *typename; + + /* + * form name, type and constraints + */ + attributeName = (attribute->attname).data; + tuple = + SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(attribute->atttypid), + 0, 0, 0); + AssertState(HeapTupleIsValid(tuple)); + attributeType = + (((TypeTupleForm) GETSTRUCT(tuple))->typname).data; + + /* + * check validity + * + */ + if (checkAttrExists(attributeName, attributeType, inhSchema) || + checkAttrExists(attributeName, attributeType, schema)) + { + + /* + * this entry already exists + */ + continue; + } + + /* + * add an entry to the schema + */ + def = makeNode(ColumnDef); + typename = makeNode(TypeName); + def->colname = pstrdup(attributeName); + typename->name = pstrdup(attributeType); + def->typename = typename; + def->is_not_null = attribute->attnotnull; + def->defval = NULL; + if (attribute->atthasdef) + { + AttrDefault *attrdef = constr->defval; + int i; + + Assert(constr != NULL && constr->num_defval > 0); + + for (i = 0; i < constr->num_defval; i++) + { + if (attrdef[i].adnum != attrno + 1) + continue; + def->defval = pstrdup(attrdef[i].adsrc); + break; + } + Assert(def->defval != NULL); + } + partialResult = lcons(def, partialResult); + } + + if (constr && constr->num_check > 0) + { + ConstrCheck *check = constr->check; + int i; + + for (i = 0; i < constr->num_check; i++) + { + ConstraintDef *cdef = (ConstraintDef *) palloc(sizeof(ConstraintDef)); + + cdef->type = CONSTR_CHECK; + if (check[i].ccname[0] == '$') + cdef->name = NULL; + else + cdef->name = pstrdup(check[i].ccname); + cdef->def = (void *) pstrdup(check[i].ccsrc); + constraints = lappend(constraints, cdef); + } + } + + /* + * iteration cleanup and result collection + */ + heap_close(relation); + + /* + * wants the inherited schema to appear in the order they are + * specified in CREATE TABLE + */ + inhSchema = nconc(inhSchema, partialResult); + } /* - * wants the inherited schema to appear in the order they are - * specified in CREATE TABLE + * put the inherited schema before our the schema for this table */ - inhSchema = nconc(inhSchema, partialResult); - } - - /* - * put the inherited schema before our the schema for this table - */ - schema = nconc(inhSchema, schema); - *supconstr = constraints; - return (schema); + schema = nconc(inhSchema, schema); + *supconstr = constraints; + return (schema); } /* * StoreCatalogInheritance -- - * Updates the system catalogs with proper inheritance information. + * Updates the system catalogs with proper inheritance information. */ static void -StoreCatalogInheritance(Oid relationId, List *supers) +StoreCatalogInheritance(Oid relationId, List * supers) { - Relation relation; - TupleDesc desc; - int16 seqNumber; - List *entry; - List *idList; - HeapTuple tuple; - - /* ---------------- - * sanity checks - * ---------------- - */ - AssertArg(OidIsValid(relationId)); - - if (supers==NIL) - return; - - /* ---------------- - * Catalog INHERITS information. - * ---------------- - */ - relation = heap_openr( InheritsRelationName ); - desc = RelationGetTupleDescriptor(relation); - - seqNumber = 1; - idList = NIL; - foreach (entry, supers) { - Datum datum[ Natts_pg_inherits ]; - char nullarr[ Natts_pg_inherits ]; - - tuple = SearchSysCacheTuple(RELNAME, - PointerGetDatum(strVal(lfirst(entry))), - 0,0,0); - AssertArg(HeapTupleIsValid(tuple)); - - /* - * build idList for use below - */ - idList = lappendi(idList, tuple->t_oid); - - datum[0] = ObjectIdGetDatum(relationId); /* inhrel */ - datum[1] = ObjectIdGetDatum(tuple->t_oid); /* inhparent */ - datum[2] = Int16GetDatum(seqNumber); /* inhseqno */ - - nullarr[0] = ' '; - nullarr[1] = ' '; - nullarr[2] = ' '; - - tuple = heap_formtuple(desc,datum, nullarr); - - heap_insert(relation, tuple); - pfree(tuple); - - seqNumber += 1; - } - - heap_close(relation); - - /* ---------------- - * Catalog IPL information. - * - * Algorithm: - * 0. list superclasses (by Oid) in order given (see idList). - * 1. append after each relationId, its superclasses, recursively. - * 3. remove all but last of duplicates. - * 4. store result. - * ---------------- - */ - - /* ---------------- - * 1. - * ---------------- - */ - foreach (entry, idList) { + Relation relation; + TupleDesc desc; + int16 seqNumber; + List *entry; + List *idList; HeapTuple tuple; - Oid id; - int16 number; - List *next; - List *current; - - id = (Oid)lfirsti(entry); - current = entry; - next = lnext(entry); - - for (number = 1; ; number += 1) { - tuple = SearchSysCacheTuple(INHRELID, - ObjectIdGetDatum(id), - Int16GetDatum(number), - 0,0); - - if (! HeapTupleIsValid(tuple)) - break; - - lnext(current) = - lconsi(((InheritsTupleForm) - GETSTRUCT(tuple))->inhparent, - NIL); - - current = lnext(current); + + /* ---------------- + * sanity checks + * ---------------- + */ + AssertArg(OidIsValid(relationId)); + + if (supers == NIL) + return; + + /* ---------------- + * Catalog INHERITS information. + * ---------------- + */ + relation = heap_openr(InheritsRelationName); + desc = RelationGetTupleDescriptor(relation); + + seqNumber = 1; + idList = NIL; + foreach(entry, supers) + { + Datum datum[Natts_pg_inherits]; + char nullarr[Natts_pg_inherits]; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(strVal(lfirst(entry))), + 0, 0, 0); + AssertArg(HeapTupleIsValid(tuple)); + + /* + * build idList for use below + */ + idList = lappendi(idList, tuple->t_oid); + + datum[0] = ObjectIdGetDatum(relationId); /* inhrel */ + datum[1] = ObjectIdGetDatum(tuple->t_oid); /* inhparent */ + datum[2] = Int16GetDatum(seqNumber); /* inhseqno */ + + nullarr[0] = ' '; + nullarr[1] = ' '; + nullarr[2] = ' '; + + tuple = heap_formtuple(desc, datum, nullarr); + + heap_insert(relation, tuple); + pfree(tuple); + + seqNumber += 1; } - lnext(current) = next; - } - - /* ---------------- - * 2. - * ---------------- - */ - foreach (entry, idList) { - Oid name; - List *rest; - bool found = false; - - again: - name = lfirsti(entry); - foreach (rest, lnext(entry)) { - if (name == lfirsti(rest)) { - found = true; - break; - } + + heap_close(relation); + + /* ---------------- + * Catalog IPL information. + * + * Algorithm: + * 0. list superclasses (by Oid) in order given (see idList). + * 1. append after each relationId, its superclasses, recursively. + * 3. remove all but last of duplicates. + * 4. store result. + * ---------------- + */ + + /* ---------------- + * 1. + * ---------------- + */ + foreach(entry, idList) + { + HeapTuple tuple; + Oid id; + int16 number; + List *next; + List *current; + + id = (Oid) lfirsti(entry); + current = entry; + next = lnext(entry); + + for (number = 1;; number += 1) + { + tuple = SearchSysCacheTuple(INHRELID, + ObjectIdGetDatum(id), + Int16GetDatum(number), + 0, 0); + + if (!HeapTupleIsValid(tuple)) + break; + + lnext(current) = + lconsi(((InheritsTupleForm) + GETSTRUCT(tuple))->inhparent, + NIL); + + current = lnext(current); + } + lnext(current) = next; } - if (found) { - /* - * entry list must be of length >= 2 or else no match - * - * so, remove this entry. - */ - lfirst(entry) = lfirst(lnext(entry)); - lnext(entry) = lnext(lnext(entry)); - - found = false; - goto again; + + /* ---------------- + * 2. + * ---------------- + */ + foreach(entry, idList) + { + Oid name; + List *rest; + bool found = false; + +again: + name = lfirsti(entry); + foreach(rest, lnext(entry)) + { + if (name == lfirsti(rest)) + { + found = true; + break; + } + } + if (found) + { + + /* + * entry list must be of length >= 2 or else no match + * + * so, remove this entry. + */ + lfirst(entry) = lfirst(lnext(entry)); + lnext(entry) = lnext(lnext(entry)); + + found = false; + goto again; + } } - } - - /* ---------------- - * 3. - * ---------------- - */ - relation = heap_openr( InheritancePrecidenceListRelationName ); - desc = RelationGetTupleDescriptor(relation); - - seqNumber = 1; - - foreach (entry, idList) { - Datum datum[ Natts_pg_ipl ]; - char nullarr[ Natts_pg_ipl ]; - - datum[0] = ObjectIdGetDatum(relationId); /* iplrel */ - datum[1] = ObjectIdGetDatum(lfirsti(entry)); - /*iplinherits*/ - datum[2] = Int16GetDatum(seqNumber); /* iplseqno */ - - nullarr[0] = ' '; - nullarr[1] = ' '; - nullarr[2] = ' '; - - tuple = heap_formtuple( desc, datum, nullarr); - - heap_insert(relation, tuple); - pfree(tuple); - - seqNumber += 1; - } - - heap_close(relation); + + /* ---------------- + * 3. + * ---------------- + */ + relation = heap_openr(InheritancePrecidenceListRelationName); + desc = RelationGetTupleDescriptor(relation); + + seqNumber = 1; + + foreach(entry, idList) + { + Datum datum[Natts_pg_ipl]; + char nullarr[Natts_pg_ipl]; + + datum[0] = ObjectIdGetDatum(relationId); /* iplrel */ + datum[1] = ObjectIdGetDatum(lfirsti(entry)); + /* iplinherits */ + datum[2] = Int16GetDatum(seqNumber); /* iplseqno */ + + nullarr[0] = ' '; + nullarr[1] = ' '; + nullarr[2] = ' '; + + tuple = heap_formtuple(desc, datum, nullarr); + + heap_insert(relation, tuple); + pfree(tuple); + + seqNumber += 1; + } + + heap_close(relation); } /* * returns 1 if attribute already exists in schema, 0 otherwise. */ static int -checkAttrExists(char *attributeName, char *attributeType, List *schema) +checkAttrExists(char *attributeName, char *attributeType, List * schema) { - List *s; - - foreach (s, schema) { - ColumnDef *def = lfirst(s); - - if (!strcmp(attributeName, def->colname)) { - /* - * attribute exists. Make sure the types are the same. - */ - if (strcmp(attributeType, def->typename->name) != 0) { - elog(WARN, "%s and %s conflict for %s", - attributeType, def->typename->name, attributeName); - } - return 1; + List *s; + + foreach(s, schema) + { + ColumnDef *def = lfirst(s); + + if (!strcmp(attributeName, def->colname)) + { + + /* + * attribute exists. Make sure the types are the same. + */ + if (strcmp(attributeType, def->typename->name) != 0) + { + elog(WARN, "%s and %s conflict for %s", + attributeType, def->typename->name, attributeName); + } + return 1; + } } - } - return 0; + return 0; } /* * MakeArchiveName - * make an archive rel name out of a regular rel name + * make an archive rel name out of a regular rel name * * the CALLER is responsible for freeing the memory allocated */ -char* +char * MakeArchiveName(Oid relationId) { - char *arch; + char *arch; - /* - * Archive relations are named a,XXXXX where XXXXX == the OID - * of the relation they archive. Create a string containing - * this name and find the reldesc for the archive relation. - */ - arch = palloc(NAMEDATALEN); - sprintf(arch, "a,%d",relationId); + /* + * Archive relations are named a,XXXXX where XXXXX == the OID of the + * relation they archive. Create a string containing this name and + * find the reldesc for the archive relation. + */ + arch = palloc(NAMEDATALEN); + sprintf(arch, "a,%d", relationId); - return arch; + return arch; } diff --git a/src/backend/commands/defind.c b/src/backend/commands/defind.c index c6b293fec68..9b8c5a0218a 100644 --- a/src/backend/commands/defind.c +++ b/src/backend/commands/defind.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * defind.c-- - * POSTGRES define, extend and remove index code. + * POSTGRES define, extend and remove index code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.12 1997/03/26 03:05:28 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.13 1997/09/07 04:40:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,7 +30,7 @@ #include <utils/relcache.h> #include <utils/lsyscache.h> #include <commands/defrem.h> -#include <parser/parsetree.h> /* for getrelid() */ +#include <parser/parsetree.h> /* for getrelid() */ #include <optimizer/prep.h> #include <optimizer/clauses.h> #include <storage/lmgr.h> @@ -39,508 +39,543 @@ #define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL) /* non-export function prototypes */ -static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid); -static void CheckPredExpr(Node *predicate, List *rangeTable, +static void CheckPredicate(List * predList, List * rangeTable, Oid baseRelOid); +static void +CheckPredExpr(Node * predicate, List * rangeTable, Oid baseRelOid); static void -CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid); -static void FuncIndexArgs(IndexElem *funcIndex, AttrNumber *attNumP, - Oid *argTypes, Oid *opOidP, Oid relId); -static void NormIndexAttrs(List *attList, AttrNumber *attNumP, - Oid *opOidP, Oid relId); -static char *GetDefaultOpClass(Oid atttypid); + CheckPredClause(Expr * predicate, List * rangeTable, Oid baseRelOid); +static void +FuncIndexArgs(IndexElem * funcIndex, AttrNumber * attNumP, + Oid * argTypes, Oid * opOidP, Oid relId); +static void +NormIndexAttrs(List * attList, AttrNumber * attNumP, + Oid * opOidP, Oid relId); +static char *GetDefaultOpClass(Oid atttypid); /* * DefineIndex -- - * Creates a new index. + * Creates a new index. * * 'attributeList' is a list of IndexElem specifying either a functional - * index or a list of attributes to index on. + * index or a list of attributes to index on. * 'parameterList' is a list of ParamString specified in the with clause. * 'predicate' is the qual specified in the where clause. * 'rangetable' is for the predicate * * Exceptions: - * XXX + * XXX */ void DefineIndex(char *heapRelationName, - char *indexRelationName, - char *accessMethodName, - List *attributeList, - List *parameterList, - bool unique, - Expr *predicate, - List *rangetable) + char *indexRelationName, + char *accessMethodName, + List * attributeList, + List * parameterList, + bool unique, + Expr * predicate, + List * rangetable) { - Oid *classObjectId; - Oid accessMethodId; - Oid relationId; - int numberOfAttributes; - AttrNumber *attributeNumberA; - HeapTuple tuple; - uint16 parameterCount = 0; - Datum *parameterA = NULL; - FuncIndexInfo fInfo; - List *cnfPred = NULL; - bool lossy = FALSE; - List *pl; - - /* - * Handle attributes - */ - numberOfAttributes = length(attributeList); - if (numberOfAttributes <= 0) { - elog(WARN, "DefineIndex: must specify at least one attribute"); - } - - /* - * compute heap relation id - */ - tuple = SearchSysCacheTuple(RELNAME, - PointerGetDatum(heapRelationName), - 0,0,0); - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "DefineIndex: %s relation not found", - heapRelationName); - } - relationId = tuple->t_oid; - - if (unique && strcmp(accessMethodName,"btree") != 0) - elog(WARN, "DefineIndex: unique indices are only available with the btree access method"); - - if (numberOfAttributes > 1 && strcmp(accessMethodName,"btree") != 0) - elog(WARN, "DefineIndex: multi-column indices are only available with the btree access method"); - - /* - * compute access method id - */ - tuple = SearchSysCacheTuple(AMNAME, PointerGetDatum(accessMethodName), - 0,0,0); - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "DefineIndex: %s access method not found", - accessMethodName); - } - accessMethodId = tuple->t_oid; - - - /* - * Handle parameters - * [param list is now different (NOT USED, really) - ay 10/94] - * - * WITH clause reinstated to handle lossy indices. - * -- JMH, 7/22/96 - */ - foreach(pl, parameterList) { - ParamString *param = (ParamString*)lfirst(pl); - - if (!strcasecmp(param->name, "islossy")) - lossy = TRUE; - } - - - /* - * Convert the partial-index predicate from parsetree form to plan - * form, so it can be readily evaluated during index creation. - * Note: "predicate" comes in as a list containing (1) the predicate - * itself (a where_clause), and (2) a corresponding range table. - * - * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94] - */ - if (predicate != NULL && rangetable != NIL) { - cnfPred = cnfify((Expr*)copyObject(predicate), true); - fix_opids(cnfPred); - CheckPredicate(cnfPred, rangetable, relationId); - } - - if (IsFuncIndex(attributeList)) { - IndexElem *funcIndex= lfirst(attributeList); - int nargs; - - nargs = length(funcIndex->args); - if (nargs > INDEX_MAX_KEYS) { - elog(WARN, - "Too many args to function, limit of %d", - INDEX_MAX_KEYS); + Oid *classObjectId; + Oid accessMethodId; + Oid relationId; + int numberOfAttributes; + AttrNumber *attributeNumberA; + HeapTuple tuple; + uint16 parameterCount = 0; + Datum *parameterA = NULL; + FuncIndexInfo fInfo; + List *cnfPred = NULL; + bool lossy = FALSE; + List *pl; + + /* + * Handle attributes + */ + numberOfAttributes = length(attributeList); + if (numberOfAttributes <= 0) + { + elog(WARN, "DefineIndex: must specify at least one attribute"); + } + + /* + * compute heap relation id + */ + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(heapRelationName), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "DefineIndex: %s relation not found", + heapRelationName); } - - FIsetnArgs(&fInfo,nargs); + relationId = tuple->t_oid; - strcpy(FIgetname(&fInfo), funcIndex->name); + if (unique && strcmp(accessMethodName, "btree") != 0) + elog(WARN, "DefineIndex: unique indices are only available with the btree access method"); - attributeNumberA = - (AttrNumber *)palloc(nargs * sizeof attributeNumberA[0]); - - classObjectId = (Oid *)palloc(sizeof classObjectId[0]); - - - FuncIndexArgs(funcIndex, attributeNumberA, - &(FIgetArg(&fInfo, 0)), - classObjectId, relationId); - - index_create(heapRelationName, - indexRelationName, - &fInfo, NULL, accessMethodId, - numberOfAttributes, attributeNumberA, - classObjectId, parameterCount, parameterA, (Node*)cnfPred, - lossy, unique); - }else { - attributeNumberA = - (AttrNumber *)palloc(numberOfAttributes * - sizeof attributeNumberA[0]); - - classObjectId = - (Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]); - - NormIndexAttrs(attributeList, attributeNumberA, - classObjectId, relationId); - - index_create(heapRelationName, indexRelationName, NULL, - attributeList, - accessMethodId, numberOfAttributes, attributeNumberA, - classObjectId, parameterCount, parameterA, (Node*)cnfPred, - lossy, unique); - } + if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0) + elog(WARN, "DefineIndex: multi-column indices are only available with the btree access method"); + + /* + * compute access method id + */ + tuple = SearchSysCacheTuple(AMNAME, PointerGetDatum(accessMethodName), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "DefineIndex: %s access method not found", + accessMethodName); + } + accessMethodId = tuple->t_oid; + + + /* + * Handle parameters [param list is now different (NOT USED, really) - + * ay 10/94] + * + * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96 + */ + foreach(pl, parameterList) + { + ParamString *param = (ParamString *) lfirst(pl); + + if (!strcasecmp(param->name, "islossy")) + lossy = TRUE; + } + + + /* + * Convert the partial-index predicate from parsetree form to plan + * form, so it can be readily evaluated during index creation. Note: + * "predicate" comes in as a list containing (1) the predicate itself + * (a where_clause), and (2) a corresponding range table. + * + * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94] + */ + if (predicate != NULL && rangetable != NIL) + { + cnfPred = cnfify((Expr *) copyObject(predicate), true); + fix_opids(cnfPred); + CheckPredicate(cnfPred, rangetable, relationId); + } + + if (IsFuncIndex(attributeList)) + { + IndexElem *funcIndex = lfirst(attributeList); + int nargs; + + nargs = length(funcIndex->args); + if (nargs > INDEX_MAX_KEYS) + { + elog(WARN, + "Too many args to function, limit of %d", + INDEX_MAX_KEYS); + } + + FIsetnArgs(&fInfo, nargs); + + strcpy(FIgetname(&fInfo), funcIndex->name); + + attributeNumberA = + (AttrNumber *) palloc(nargs * sizeof attributeNumberA[0]); + + classObjectId = (Oid *) palloc(sizeof classObjectId[0]); + + + FuncIndexArgs(funcIndex, attributeNumberA, + &(FIgetArg(&fInfo, 0)), + classObjectId, relationId); + + index_create(heapRelationName, + indexRelationName, + &fInfo, NULL, accessMethodId, + numberOfAttributes, attributeNumberA, + classObjectId, parameterCount, parameterA, (Node *) cnfPred, + lossy, unique); + } + else + { + attributeNumberA = + (AttrNumber *) palloc(numberOfAttributes * + sizeof attributeNumberA[0]); + + classObjectId = + (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]); + + NormIndexAttrs(attributeList, attributeNumberA, + classObjectId, relationId); + + index_create(heapRelationName, indexRelationName, NULL, + attributeList, + accessMethodId, numberOfAttributes, attributeNumberA, + classObjectId, parameterCount, parameterA, (Node *) cnfPred, + lossy, unique); + } } /* * ExtendIndex -- - * Extends a partial index. + * Extends a partial index. * * Exceptions: - * XXX + * XXX */ void -ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable) +ExtendIndex(char *indexRelationName, Expr * predicate, List * rangetable) { - Oid *classObjectId; - Oid accessMethodId; - Oid indexId, relationId; - Oid indproc; - int numberOfAttributes; - AttrNumber *attributeNumberA; - HeapTuple tuple; - FuncIndexInfo fInfo; - FuncIndexInfo *funcInfo = NULL; - IndexTupleForm index; - Node *oldPred = NULL; - List *cnfPred = NULL; - PredInfo *predInfo; - Relation heapRelation; - Relation indexRelation; - int i; - - /* - * compute index relation id and access method id - */ - tuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(indexRelationName), - 0,0,0); - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "ExtendIndex: %s index not found", - indexRelationName); - } - indexId = tuple->t_oid; - accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam; - - /* - * find pg_index tuple - */ - tuple = SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(indexId), - 0,0,0); - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "ExtendIndex: %s is not an index", - indexRelationName); - } - - /* - * Extract info from the pg_index tuple - */ - index = (IndexTupleForm)GETSTRUCT(tuple); - Assert(index->indexrelid == indexId); - relationId = index->indrelid; - indproc = index->indproc; - - for (i=0; i<INDEX_MAX_KEYS; i++) - if (index->indkey[i] == 0) break; - numberOfAttributes = i; - - if (VARSIZE(&index->indpred) != 0) { - char *predString; - - predString = fmgr(F_TEXTOUT, &index->indpred); - oldPred = stringToNode(predString); - pfree(predString); - } - if (oldPred == NULL) - elog(WARN, "ExtendIndex: %s is not a partial index", - indexRelationName); - - /* - * Convert the extension predicate from parsetree form to plan - * form, so it can be readily evaluated during index creation. - * Note: "predicate" comes in as a list containing (1) the predicate - * itself (a where_clause), and (2) a corresponding range table. - */ - if (rangetable != NIL) { - cnfPred = cnfify((Expr*)copyObject(predicate), true); - fix_opids(cnfPred); - CheckPredicate(cnfPred, rangetable, relationId); - } - - /* make predInfo list to pass to index_build */ - predInfo = (PredInfo*)palloc(sizeof(PredInfo)); - predInfo->pred = (Node*)cnfPred; - predInfo->oldPred = oldPred; - - attributeNumberA = - (AttrNumber *)palloc(numberOfAttributes* - sizeof attributeNumberA[0]); - classObjectId = - (Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]); - - - for (i=0; i<numberOfAttributes; i++) { - attributeNumberA[i] = index->indkey[i]; - classObjectId[i] = index->indclass[i]; - } - - if (indproc != InvalidOid) { - funcInfo = &fInfo; -/* FIgetnArgs(funcInfo) = numberOfAttributes; */ - FIsetnArgs(funcInfo,numberOfAttributes); - - tuple = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(indproc), - 0,0,0); + Oid *classObjectId; + Oid accessMethodId; + Oid indexId, + relationId; + Oid indproc; + int numberOfAttributes; + AttrNumber *attributeNumberA; + HeapTuple tuple; + FuncIndexInfo fInfo; + FuncIndexInfo *funcInfo = NULL; + IndexTupleForm index; + Node *oldPred = NULL; + List *cnfPred = NULL; + PredInfo *predInfo; + Relation heapRelation; + Relation indexRelation; + int i; + + /* + * compute index relation id and access method id + */ + tuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(indexRelationName), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "ExtendIndex: %s index not found", + indexRelationName); + } + indexId = tuple->t_oid; + accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam; + + /* + * find pg_index tuple + */ + tuple = SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(indexId), + 0, 0, 0); if (!HeapTupleIsValid(tuple)) - elog(WARN, "ExtendIndex: index procedure not found"); - - namecpy(&(funcInfo->funcName), - &(((Form_pg_proc) GETSTRUCT(tuple))->proname)); - - FIsetProcOid(funcInfo,tuple->t_oid); - } - - heapRelation = heap_open(relationId); - indexRelation = index_open(indexId); - - RelationSetLockForWrite(heapRelation); - - InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId); - - index_build(heapRelation, indexRelation, numberOfAttributes, - attributeNumberA, 0, NULL, funcInfo, predInfo); + { + elog(WARN, "ExtendIndex: %s is not an index", + indexRelationName); + } + + /* + * Extract info from the pg_index tuple + */ + index = (IndexTupleForm) GETSTRUCT(tuple); + Assert(index->indexrelid == indexId); + relationId = index->indrelid; + indproc = index->indproc; + + for (i = 0; i < INDEX_MAX_KEYS; i++) + if (index->indkey[i] == 0) + break; + numberOfAttributes = i; + + if (VARSIZE(&index->indpred) != 0) + { + char *predString; + + predString = fmgr(F_TEXTOUT, &index->indpred); + oldPred = stringToNode(predString); + pfree(predString); + } + if (oldPred == NULL) + elog(WARN, "ExtendIndex: %s is not a partial index", + indexRelationName); + + /* + * Convert the extension predicate from parsetree form to plan form, + * so it can be readily evaluated during index creation. Note: + * "predicate" comes in as a list containing (1) the predicate itself + * (a where_clause), and (2) a corresponding range table. + */ + if (rangetable != NIL) + { + cnfPred = cnfify((Expr *) copyObject(predicate), true); + fix_opids(cnfPred); + CheckPredicate(cnfPred, rangetable, relationId); + } + + /* make predInfo list to pass to index_build */ + predInfo = (PredInfo *) palloc(sizeof(PredInfo)); + predInfo->pred = (Node *) cnfPred; + predInfo->oldPred = oldPred; + + attributeNumberA = + (AttrNumber *) palloc(numberOfAttributes * + sizeof attributeNumberA[0]); + classObjectId = + (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]); + + + for (i = 0; i < numberOfAttributes; i++) + { + attributeNumberA[i] = index->indkey[i]; + classObjectId[i] = index->indclass[i]; + } + + if (indproc != InvalidOid) + { + funcInfo = &fInfo; +/* FIgetnArgs(funcInfo) = numberOfAttributes; */ + FIsetnArgs(funcInfo, numberOfAttributes); + + tuple = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(indproc), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(WARN, "ExtendIndex: index procedure not found"); + + namecpy(&(funcInfo->funcName), + &(((Form_pg_proc) GETSTRUCT(tuple))->proname)); + + FIsetProcOid(funcInfo, tuple->t_oid); + } + + heapRelation = heap_open(relationId); + indexRelation = index_open(indexId); + + RelationSetLockForWrite(heapRelation); + + InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId); + + index_build(heapRelation, indexRelation, numberOfAttributes, + attributeNumberA, 0, NULL, funcInfo, predInfo); } /* * CheckPredicate - * Checks that the given list of partial-index predicates refer - * (via the given range table) only to the given base relation oid, - * and that they're in a form the planner can handle, i.e., - * boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR - * has to be on the left). + * Checks that the given list of partial-index predicates refer + * (via the given range table) only to the given base relation oid, + * and that they're in a form the planner can handle, i.e., + * boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR + * has to be on the left). */ static void -CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid) +CheckPredicate(List * predList, List * rangeTable, Oid baseRelOid) { - List *item; - - foreach (item, predList) { - CheckPredExpr(lfirst(item), rangeTable, baseRelOid); - } + List *item; + + foreach(item, predList) + { + CheckPredExpr(lfirst(item), rangeTable, baseRelOid); + } } static void -CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid) +CheckPredExpr(Node * predicate, List * rangeTable, Oid baseRelOid) { - List *clauses = NIL, *clause; - - if (is_opclause(predicate)) { - CheckPredClause((Expr*)predicate, rangeTable, baseRelOid); - return; - } else if (or_clause(predicate)) - clauses = ((Expr*)predicate)->args; - else if (and_clause(predicate)) - clauses = ((Expr*)predicate)->args; - else - elog(WARN, "Unsupported partial-index predicate expression type"); - - foreach (clause, clauses) { - CheckPredExpr(lfirst(clause), rangeTable, baseRelOid); - } + List *clauses = NIL, + *clause; + + if (is_opclause(predicate)) + { + CheckPredClause((Expr *) predicate, rangeTable, baseRelOid); + return; + } + else if (or_clause(predicate)) + clauses = ((Expr *) predicate)->args; + else if (and_clause(predicate)) + clauses = ((Expr *) predicate)->args; + else + elog(WARN, "Unsupported partial-index predicate expression type"); + + foreach(clause, clauses) + { + CheckPredExpr(lfirst(clause), rangeTable, baseRelOid); + } } static void -CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid) +CheckPredClause(Expr * predicate, List * rangeTable, Oid baseRelOid) { - Var *pred_var; - Const *pred_const; - - pred_var = (Var *)get_leftop(predicate); - pred_const = (Const *)get_rightop(predicate); - - if (!IsA(predicate->oper,Oper) || - !IsA(pred_var,Var) || - !IsA(pred_const,Const)) { - elog(WARN, "Unsupported partial-index predicate clause type"); - } - - if (getrelid(pred_var->varno, rangeTable) != baseRelOid) - elog(WARN, - "Partial-index predicates may refer only to the base relation"); + Var *pred_var; + Const *pred_const; + + pred_var = (Var *) get_leftop(predicate); + pred_const = (Const *) get_rightop(predicate); + + if (!IsA(predicate->oper, Oper) || + !IsA(pred_var, Var) || + !IsA(pred_const, Const)) + { + elog(WARN, "Unsupported partial-index predicate clause type"); + } + + if (getrelid(pred_var->varno, rangeTable) != baseRelOid) + elog(WARN, + "Partial-index predicates may refer only to the base relation"); } -static void -FuncIndexArgs(IndexElem *funcIndex, - AttrNumber *attNumP, - Oid *argTypes, - Oid *opOidP, - Oid relId) +static void +FuncIndexArgs(IndexElem * funcIndex, + AttrNumber * attNumP, + Oid * argTypes, + Oid * opOidP, + Oid relId) { - List *rest; - HeapTuple tuple; - AttributeTupleForm att; - - tuple = SearchSysCacheTuple(CLANAME, - PointerGetDatum(funcIndex->class), - 0,0,0); - - if (!HeapTupleIsValid(tuple)) + List *rest; + HeapTuple tuple; + AttributeTupleForm att; + + tuple = SearchSysCacheTuple(CLANAME, + PointerGetDatum(funcIndex->class), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) { - elog(WARN, "DefineIndex: %s class not found", - funcIndex->class); + elog(WARN, "DefineIndex: %s class not found", + funcIndex->class); } - *opOidP = tuple->t_oid; - - memset(argTypes, 0, 8 * sizeof(Oid)); - - /* - * process the function arguments - */ - for (rest=funcIndex->args; rest != NIL; rest = lnext(rest)) { - char *arg; - - arg = strVal(lfirst(rest)); - - tuple = SearchSysCacheTuple(ATTNAME, - ObjectIdGetDatum(relId), - PointerGetDatum(arg),0,0); - - if (!HeapTupleIsValid(tuple)) { - elog(WARN, - "DefineIndex: attribute \"%s\" not found", - arg); + *opOidP = tuple->t_oid; + + memset(argTypes, 0, 8 * sizeof(Oid)); + + /* + * process the function arguments + */ + for (rest = funcIndex->args; rest != NIL; rest = lnext(rest)) + { + char *arg; + + arg = strVal(lfirst(rest)); + + tuple = SearchSysCacheTuple(ATTNAME, + ObjectIdGetDatum(relId), + PointerGetDatum(arg), 0, 0); + + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, + "DefineIndex: attribute \"%s\" not found", + arg); + } + att = (AttributeTupleForm) GETSTRUCT(tuple); + *attNumP++ = att->attnum; + *argTypes++ = att->atttypid; } - att = (AttributeTupleForm)GETSTRUCT(tuple); - *attNumP++ = att->attnum; - *argTypes++ = att->atttypid; - } } -static void -NormIndexAttrs(List *attList, /* list of IndexElem's */ - AttrNumber *attNumP, - Oid *opOidP, - Oid relId) +static void +NormIndexAttrs(List * attList, /* list of IndexElem's */ + AttrNumber * attNumP, + Oid * opOidP, + Oid relId) { - List *rest; - HeapTuple tuple; - - /* - * process attributeList - */ - - for (rest=attList; rest != NIL; rest = lnext(rest)) { - IndexElem *attribute; - AttributeTupleForm attform; - - attribute = lfirst(rest); - - if (attribute->name == NULL) - elog(WARN, "missing attribute for define index"); - - tuple = SearchSysCacheTuple(ATTNAME, - ObjectIdGetDatum(relId), - PointerGetDatum(attribute->name), - 0,0); - if (!HeapTupleIsValid(tuple)) { - elog(WARN, - "DefineIndex: attribute \"%s\" not found", - attribute->name); - } + List *rest; + HeapTuple tuple; - attform = (AttributeTupleForm)GETSTRUCT(tuple); - *attNumP++ = attform->attnum; - - if (attribute->class == NULL) { - /* no operator class specified, so find the default */ - attribute->class = GetDefaultOpClass(attform->atttypid); - if(attribute->class == NULL) { - elog(WARN, - "Can't find a default operator class for type %d.", - attform->atttypid); - } - } - - tuple = SearchSysCacheTuple(CLANAME, - PointerGetDatum(attribute->class), - 0,0,0); - - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "DefineIndex: %s class not found", - attribute->class); + /* + * process attributeList + */ + + for (rest = attList; rest != NIL; rest = lnext(rest)) + { + IndexElem *attribute; + AttributeTupleForm attform; + + attribute = lfirst(rest); + + if (attribute->name == NULL) + elog(WARN, "missing attribute for define index"); + + tuple = SearchSysCacheTuple(ATTNAME, + ObjectIdGetDatum(relId), + PointerGetDatum(attribute->name), + 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, + "DefineIndex: attribute \"%s\" not found", + attribute->name); + } + + attform = (AttributeTupleForm) GETSTRUCT(tuple); + *attNumP++ = attform->attnum; + + if (attribute->class == NULL) + { + /* no operator class specified, so find the default */ + attribute->class = GetDefaultOpClass(attform->atttypid); + if (attribute->class == NULL) + { + elog(WARN, + "Can't find a default operator class for type %d.", + attform->atttypid); + } + } + + tuple = SearchSysCacheTuple(CLANAME, + PointerGetDatum(attribute->class), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "DefineIndex: %s class not found", + attribute->class); + } + *opOidP++ = tuple->t_oid; } - *opOidP++ = tuple->t_oid; - } } -static char * +static char * GetDefaultOpClass(Oid atttypid) { - HeapTuple tuple; + HeapTuple tuple; - tuple = SearchSysCacheTuple(CLADEFTYPE, - ObjectIdGetDatum(atttypid), - 0, 0, 0); - if(!HeapTupleIsValid(tuple)) { - return 0; - } + tuple = SearchSysCacheTuple(CLADEFTYPE, + ObjectIdGetDatum(atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + return 0; + } - return nameout(&(((Form_pg_opclass)GETSTRUCT(tuple))->opcname)); + return nameout(&(((Form_pg_opclass) GETSTRUCT(tuple))->opcname)); } /* * RemoveIndex -- - * Deletes an index. + * Deletes an index. * * Exceptions: - * BadArg if name is invalid. - * "WARN" if index nonexistent. - * ... + * BadArg if name is invalid. + * "WARN" if index nonexistent. + * ... */ void RemoveIndex(char *name) { - HeapTuple tuple; - - tuple = SearchSysCacheTuple(RELNAME, - PointerGetDatum(name), - 0,0,0); - - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "index \"%s\" nonexistent", name); - } - - if (((Form_pg_class)GETSTRUCT(tuple))->relkind != RELKIND_INDEX) { - elog(WARN, "relation \"%s\" is of type \"%c\"", - name, - ((Form_pg_class)GETSTRUCT(tuple))->relkind); - } - - index_destroy(tuple->t_oid); + HeapTuple tuple; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(name), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "index \"%s\" nonexistent", name); + } + + if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX) + { + elog(WARN, "relation \"%s\" is of type \"%c\"", + name, + ((Form_pg_class) GETSTRUCT(tuple))->relkind); + } + + index_destroy(tuple->t_oid); } diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 800d85a48b8..fb1df213cec 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -2,33 +2,33 @@ * * define.c-- * - * These routines execute some of the CREATE statements. In an earlier - * version of Postgres, these were "define" statements. + * These routines execute some of the CREATE statements. In an earlier + * version of Postgres, these were "define" statements. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.13 1997/08/12 22:52:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.14 1997/09/07 04:40:46 momjian Exp $ * * DESCRIPTION - * The "DefineFoo" routines take the parse tree and pick out the - * appropriate arguments/flags, passing the results to the - * corresponding "FooDefine" routines (in src/catalog) that do - * the actual catalog-munging. These routines also verify permission - * of the user to execute the command. + * The "DefineFoo" routines take the parse tree and pick out the + * appropriate arguments/flags, passing the results to the + * corresponding "FooDefine" routines (in src/catalog) that do + * the actual catalog-munging. These routines also verify permission + * of the user to execute the command. * * NOTES - * These things must be defined and committed in the following order: - * "create function": - * input/output, recv/send procedures - * "create type": - * type - * "create operator": - * operators + * These things must be defined and committed in the following order: + * "create function": + * input/output, recv/send procedures + * "create type": + * type + * "create operator": + * operators * - * Most of the parse-tree manipulation routines are defined in - * commands/manip.c. + * Most of the parse-tree manipulation routines are defined in + * commands/manip.c. * *------------------------------------------------------------------------- */ @@ -46,209 +46,259 @@ #include <catalog/pg_proc.h> #include <catalog/pg_type.h> #include <utils/syscache.h> -#include <fmgr.h> /* for fmgr */ -#include <utils/builtins.h> /* prototype for textin() */ +#include <fmgr.h> /* for fmgr */ +#include <utils/builtins.h> /* prototype for textin() */ #include <commands/defrem.h> #include <optimizer/xfunc.h> #include <tcop/dest.h> #include <catalog/pg_user.h> -static char *defGetString(DefElem *def); -static int defGetTypeLength(DefElem *def); +static char *defGetString(DefElem * def); +static int defGetTypeLength(DefElem * def); -#define DEFAULT_TYPDELIM ',' +#define DEFAULT_TYPDELIM ',' static void -case_translate_language_name(const char *input, char *output) { +case_translate_language_name(const char *input, char *output) +{ /*------------------------------------------------------------------------- Translate the input language name to lower case, except if it's C, - translate to upper case. + translate to upper case. --------------------------------------------------------------------------*/ - int i; + int i; - for (i = 0; i < NAMEDATALEN && input[i] != '\0'; ++i) - output[i] = tolower(input[i]); + for (i = 0; i < NAMEDATALEN && input[i] != '\0'; ++i) + output[i] = tolower(input[i]); - output[i] = '\0'; + output[i] = '\0'; - if (strcmp(output, "c") == 0) output[0] = 'C'; -} + if (strcmp(output, "c") == 0) + output[0] = 'C'; +} static void -compute_return_type(const Node *returnType, - char **prorettype_p, bool *returnsSet_p) { +compute_return_type(const Node * returnType, + char **prorettype_p, bool * returnsSet_p) +{ /*--------------------------------------------------------------------------- - Examine the "returns" clause returnType of the CREATE FUNCTION statement + Examine the "returns" clause returnType of the CREATE FUNCTION statement and return information about it as **prorettype_p and **returnsSet. ----------------------------------------------------------------------------*/ - if (nodeTag(returnType) == T_TypeName) { - /* a set of values */ - TypeName *setType = (TypeName *)returnType; - *prorettype_p = setType->name; - *returnsSet_p = true; - }else { - /* singleton */ - *prorettype_p = strVal(returnType); - *returnsSet_p = false; - } + if (nodeTag(returnType) == T_TypeName) + { + /* a set of values */ + TypeName *setType = (TypeName *) returnType; + + *prorettype_p = setType->name; + *returnsSet_p = true; + } + else + { + /* singleton */ + *prorettype_p = strVal(returnType); + *returnsSet_p = false; + } } - -static void -compute_full_attributes(const List *parameters, int32 *byte_pct_p, - int32 *perbyte_cpu_p, int32 *percall_cpu_p, - int32 *outin_ratio_p, bool *canCache_p) { + +static void +compute_full_attributes(const List * parameters, int32 * byte_pct_p, + int32 * perbyte_cpu_p, int32 * percall_cpu_p, + int32 * outin_ratio_p, bool * canCache_p) +{ /*-------------------------------------------------------------------------- Interpret the parameters *parameters and return their contents as *byte_pct_p, etc. These are the full parameters of a C or internal function. ---------------------------------------------------------------------------*/ - List *pl; - - /* the defaults */ - *byte_pct_p = BYTE_PCT; - *perbyte_cpu_p = PERBYTE_CPU; - *percall_cpu_p = PERCALL_CPU; - *outin_ratio_p = OUTIN_RATIO; - - foreach(pl, (List *)parameters) { - ParamString *param = (ParamString*)lfirst(pl); - - if (strcasecmp(param->name, "iscachable") == 0) { - *canCache_p = true; - } else if (strcasecmp(param->name, "trusted") == 0) { - /* - * we don't have untrusted functions any more. The 4.2 - * implementation is lousy anyway so I took it out. - * -ay 10/94 - */ - elog(WARN, "untrusted function has been decommissioned."); - } else if (strcasecmp(param->name, "byte_pct") == 0) { - /* - ** handle expensive function parameters - */ - *byte_pct_p = atoi(param->val); - } else if (strcasecmp(param->name, "perbyte_cpu") == 0) { - if (sscanf(param->val, "%d", perbyte_cpu_p) == 0) { - int count; - char *ptr; - for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) - if (*ptr == '!') count++; - *perbyte_cpu_p = (int) pow(10.0, (double) count); - } - } else if (strcasecmp(param->name, "percall_cpu") == 0) { - if (sscanf(param->val, "%d", percall_cpu_p) == 0) { - int count; - char *ptr; - for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) - if (*ptr == '!') count++; - *percall_cpu_p = (int) pow(10.0, (double) count); - } - } else if (strcasecmp(param->name, "outin_ratio") == 0) { - *outin_ratio_p = atoi(param->val); - } - } + List *pl; + + /* the defaults */ + *byte_pct_p = BYTE_PCT; + *perbyte_cpu_p = PERBYTE_CPU; + *percall_cpu_p = PERCALL_CPU; + *outin_ratio_p = OUTIN_RATIO; + + foreach(pl, (List *) parameters) + { + ParamString *param = (ParamString *) lfirst(pl); + + if (strcasecmp(param->name, "iscachable") == 0) + { + *canCache_p = true; + } + else if (strcasecmp(param->name, "trusted") == 0) + { + + /* + * we don't have untrusted functions any more. The 4.2 + * implementation is lousy anyway so I took it out. -ay 10/94 + */ + elog(WARN, "untrusted function has been decommissioned."); + } + else if (strcasecmp(param->name, "byte_pct") == 0) + { + + /* + * * handle expensive function parameters + */ + *byte_pct_p = atoi(param->val); + } + else if (strcasecmp(param->name, "perbyte_cpu") == 0) + { + if (sscanf(param->val, "%d", perbyte_cpu_p) == 0) + { + int count; + char *ptr; + + for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) + if (*ptr == '!') + count++; + *perbyte_cpu_p = (int) pow(10.0, (double) count); + } + } + else if (strcasecmp(param->name, "percall_cpu") == 0) + { + if (sscanf(param->val, "%d", percall_cpu_p) == 0) + { + int count; + char *ptr; + + for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) + if (*ptr == '!') + count++; + *percall_cpu_p = (int) pow(10.0, (double) count); + } + } + else if (strcasecmp(param->name, "outin_ratio") == 0) + { + *outin_ratio_p = atoi(param->val); + } + } } static void interpret_AS_clause(const char languageName[], const char as[], - char **prosrc_str_p, char **probin_str_p) { - - if ( strcmp(languageName, "C") == 0 || - strcmp(languageName, "internal") == 0 ) { - *prosrc_str_p = "-"; - *probin_str_p = (char *)as; - } else { - *prosrc_str_p = (char *)as; - *probin_str_p = "-"; - } + char **prosrc_str_p, char **probin_str_p) +{ + + if (strcmp(languageName, "C") == 0 || + strcmp(languageName, "internal") == 0) + { + *prosrc_str_p = "-"; + *probin_str_p = (char *) as; + } + else + { + *prosrc_str_p = (char *) as; + *probin_str_p = "-"; + } } /* * CreateFunction -- - * Execute a CREATE FUNCTION utility statement. + * Execute a CREATE FUNCTION utility statement. * */ void -CreateFunction(ProcedureStmt *stmt, CommandDest dest) +CreateFunction(ProcedureStmt * stmt, CommandDest dest) { - char *probin_str; - /* pathname of executable file that executes this function, if any */ - char *prosrc_str; - /* SQL that executes this function, if any */ - char *prorettype; - /* Type of return value (or member of set of values) from function */ - char languageName[NAMEDATALEN]; - /* name of language of function, with case adjusted: - "C", "internal", or "SQL" - */ - /* The following are attributes of the function, as expressed in the - CREATE FUNCTION statement, where applicable. - */ - int32 byte_pct, perbyte_cpu, percall_cpu, outin_ratio; - bool canCache; - bool returnsSet; - /* The function returns a set of values, as opposed to a singleton. */ - - - case_translate_language_name(stmt->language, languageName); - - compute_return_type(stmt->returnType, &prorettype, &returnsSet); - - if ( strcmp(languageName, "C") == 0 || - strcmp(languageName, "internal") == 0 ) { - compute_full_attributes(stmt->withClause, - &byte_pct, &perbyte_cpu, &percall_cpu, - &outin_ratio, &canCache); - } else if (strcmp(languageName, "sql") == 0) { - /* query optimizer groks sql, these are meaningless */ - perbyte_cpu = percall_cpu = 0; - byte_pct = outin_ratio = 100; - canCache = false; - } else { - elog(WARN, - "Unrecognized language specified in a CREATE FUNCTION: " - "'%s'. Recognized languages are sql, C, and internal.", - languageName); - } - - interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str); - - if (strcmp(languageName, "sql") != 0 && !superuser()) - elog(WARN, - "Only users with Postgres superuser privilege are permitted " - "to create a function " - "in the '%s' language. Others may use the 'sql' language.", - languageName); - /* Above does not return. */ - else { - /* And now that we have all the parameters, and know we're permitted - to do so, go ahead and create the function. - */ - ProcedureCreate(stmt->funcname, - returnsSet, - prorettype, - languageName, - prosrc_str, /* converted to text later */ - probin_str, /* converted to text later */ - canCache, - true, /* (obsolete "trusted") */ - byte_pct, - perbyte_cpu, - percall_cpu, - outin_ratio, - stmt->defArgs, - dest); - } + char *probin_str; + + /* pathname of executable file that executes this function, if any */ + char *prosrc_str; + + /* SQL that executes this function, if any */ + char *prorettype; + + /* Type of return value (or member of set of values) from function */ + char languageName[NAMEDATALEN]; + + /* + * name of language of function, with case adjusted: "C", "internal", + * or "SQL" + */ + + /* + * The following are attributes of the function, as expressed in the + * CREATE FUNCTION statement, where applicable. + */ + int32 byte_pct, + perbyte_cpu, + percall_cpu, + outin_ratio; + bool canCache; + bool returnsSet; + + /* The function returns a set of values, as opposed to a singleton. */ + + + case_translate_language_name(stmt->language, languageName); + + compute_return_type(stmt->returnType, &prorettype, &returnsSet); + + if (strcmp(languageName, "C") == 0 || + strcmp(languageName, "internal") == 0) + { + compute_full_attributes(stmt->withClause, + &byte_pct, &perbyte_cpu, &percall_cpu, + &outin_ratio, &canCache); + } + else if (strcmp(languageName, "sql") == 0) + { + /* query optimizer groks sql, these are meaningless */ + perbyte_cpu = percall_cpu = 0; + byte_pct = outin_ratio = 100; + canCache = false; + } + else + { + elog(WARN, + "Unrecognized language specified in a CREATE FUNCTION: " + "'%s'. Recognized languages are sql, C, and internal.", + languageName); + } + + interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str); + + if (strcmp(languageName, "sql") != 0 && !superuser()) + elog(WARN, + "Only users with Postgres superuser privilege are permitted " + "to create a function " + "in the '%s' language. Others may use the 'sql' language.", + languageName); + /* Above does not return. */ + else + { + + /* + * And now that we have all the parameters, and know we're + * permitted to do so, go ahead and create the function. + */ + ProcedureCreate(stmt->funcname, + returnsSet, + prorettype, + languageName, + prosrc_str, /* converted to text later */ + probin_str, /* converted to text later */ + canCache, + true, /* (obsolete "trusted") */ + byte_pct, + perbyte_cpu, + percall_cpu, + outin_ratio, + stmt->defArgs, + dest); + } } @@ -256,344 +306,437 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest) /* -------------------------------- * DefineOperator-- * - * this function extracts all the information from the - * parameter list generated by the parser and then has - * OperatorCreate() do all the actual work. + * this function extracts all the information from the + * parameter list generated by the parser and then has + * OperatorCreate() do all the actual work. * * 'parameters' is a list of DefElem * -------------------------------- */ void -DefineOperator(char *oprName, - List *parameters) +DefineOperator(char *oprName, + List * parameters) { - uint16 precedence=0; /* operator precedence */ - bool canHash=false; /* operator hashes */ - bool isLeftAssociative=true; /* operator is left associative */ - char *functionName=NULL; /* function for operator */ - char *typeName1=NULL; /* first type name */ - char *typeName2=NULL; /* second type name */ - char *commutatorName=NULL; /* optional commutator operator name */ - char *negatorName=NULL; /* optional negator operator name */ - char *restrictionName=NULL; /* optional restrict. sel. procedure */ - char *joinName=NULL; /* optional join sel. procedure name */ - char *sortName1=NULL; /* optional first sort operator */ - char *sortName2=NULL; /* optional second sort operator */ - List *pl; - - /* - * loop over the definition list and extract the information we need. - */ - foreach (pl, parameters) { - DefElem *defel = (DefElem *)lfirst(pl); - - if (!strcasecmp(defel->defname, "leftarg")) { - /* see gram.y, must be setof */ - if (nodeTag(defel->arg)==T_TypeName) - elog(WARN, "setof type not implemented for leftarg"); - - if (nodeTag(defel->arg)==T_String) { - typeName1 = defGetString(defel); - }else { - elog(WARN, "type for leftarg is malformed."); - } - } else if (!strcasecmp(defel->defname, "rightarg")) { - /* see gram.y, must be setof */ - if (nodeTag(defel->arg)==T_TypeName) - elog(WARN, "setof type not implemented for rightarg"); - - if (nodeTag(defel->arg)==T_String) { - typeName2 = defGetString(defel); - }else { - elog(WARN, "type for rightarg is malformed."); - } - } else if (!strcasecmp(defel->defname, "procedure")) { - functionName = defGetString(defel); - } else if (!strcasecmp(defel->defname, "precedence")) { - /* NOT IMPLEMENTED (never worked in v4.2) */ - elog(NOTICE, "CREATE OPERATOR: precedence not implemented"); - } else if (!strcasecmp(defel->defname, "associativity")) { - /* NOT IMPLEMENTED (never worked in v4.2) */ - elog(NOTICE, "CREATE OPERATOR: associativity not implemented"); - } else if (!strcasecmp(defel->defname, "commutator")) { - commutatorName = defGetString(defel); - } else if (!strcasecmp(defel->defname, "negator")) { - negatorName = defGetString(defel); - } else if (!strcasecmp(defel->defname, "restrict")) { - restrictionName = defGetString(defel); - } else if (!strcasecmp(defel->defname, "join")) { - joinName = defGetString(defel); - } else if (!strcasecmp(defel->defname, "hashes")) { - canHash = TRUE; - } else if (!strcasecmp(defel->defname, "sort1")) { - /* ---------------- - * XXX ( ... [ , sort1 = oprname ] [ , sort2 = oprname ] ... ) - * XXX is undocumented in the reference manual source as of - * 89/8/22. - * ---------------- - */ - sortName1 = defGetString(defel); - } else if (!strcasecmp(defel->defname, "sort2")) { - sortName2 = defGetString(defel); - } else { - elog(NOTICE, "DefineOperator: attribute \"%s\" not recognized", - defel->defname); - } - } - - /* - * make sure we have our required definitions - */ - if (functionName==NULL) { - elog(WARN, "Define: \"procedure\" unspecified"); - } - - /* ---------------- - * now have OperatorCreate do all the work.. - * ---------------- - */ - OperatorCreate(oprName, /* operator name */ - typeName1, /* first type name */ - typeName2, /* second type name */ - functionName, /* function for operator */ - precedence, /* operator precedence */ - isLeftAssociative, /* operator is left associative */ - commutatorName, /* optional commutator operator name */ - negatorName, /* optional negator operator name */ - restrictionName, /* optional restrict. sel. procedure */ - joinName, /* optional join sel. procedure name */ - canHash, /* operator hashes */ - sortName1, /* optional first sort operator */ - sortName2); /* optional second sort operator */ - + uint16 precedence = 0; /* operator precedence */ + bool canHash = false; /* operator hashes */ + bool isLeftAssociative = true; /* operator is left + * associative */ + char *functionName = NULL; /* function for operator */ + char *typeName1 = NULL; /* first type name */ + char *typeName2 = NULL; /* second type name */ + char *commutatorName = NULL; /* optional commutator + * operator name */ + char *negatorName = NULL; /* optional negator operator name */ + char *restrictionName = NULL; /* optional restrict. sel. + * procedure */ + char *joinName = NULL; /* optional join sel. procedure + * name */ + char *sortName1 = NULL; /* optional first sort operator */ + char *sortName2 = NULL; /* optional second sort operator */ + List *pl; + + /* + * loop over the definition list and extract the information we need. + */ + foreach(pl, parameters) + { + DefElem *defel = (DefElem *) lfirst(pl); + + if (!strcasecmp(defel->defname, "leftarg")) + { + /* see gram.y, must be setof */ + if (nodeTag(defel->arg) == T_TypeName) + elog(WARN, "setof type not implemented for leftarg"); + + if (nodeTag(defel->arg) == T_String) + { + typeName1 = defGetString(defel); + } + else + { + elog(WARN, "type for leftarg is malformed."); + } + } + else if (!strcasecmp(defel->defname, "rightarg")) + { + /* see gram.y, must be setof */ + if (nodeTag(defel->arg) == T_TypeName) + elog(WARN, "setof type not implemented for rightarg"); + + if (nodeTag(defel->arg) == T_String) + { + typeName2 = defGetString(defel); + } + else + { + elog(WARN, "type for rightarg is malformed."); + } + } + else if (!strcasecmp(defel->defname, "procedure")) + { + functionName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "precedence")) + { + /* NOT IMPLEMENTED (never worked in v4.2) */ + elog(NOTICE, "CREATE OPERATOR: precedence not implemented"); + } + else if (!strcasecmp(defel->defname, "associativity")) + { + /* NOT IMPLEMENTED (never worked in v4.2) */ + elog(NOTICE, "CREATE OPERATOR: associativity not implemented"); + } + else if (!strcasecmp(defel->defname, "commutator")) + { + commutatorName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "negator")) + { + negatorName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "restrict")) + { + restrictionName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "join")) + { + joinName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "hashes")) + { + canHash = TRUE; + } + else if (!strcasecmp(defel->defname, "sort1")) + { + /* ---------------- + * XXX ( ... [ , sort1 = oprname ] [ , sort2 = oprname ] ... ) + * XXX is undocumented in the reference manual source as of + * 89/8/22. + * ---------------- + */ + sortName1 = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "sort2")) + { + sortName2 = defGetString(defel); + } + else + { + elog(NOTICE, "DefineOperator: attribute \"%s\" not recognized", + defel->defname); + } + } + + /* + * make sure we have our required definitions + */ + if (functionName == NULL) + { + elog(WARN, "Define: \"procedure\" unspecified"); + } + + /* ---------------- + * now have OperatorCreate do all the work.. + * ---------------- + */ + OperatorCreate(oprName, /* operator name */ + typeName1, /* first type name */ + typeName2, /* second type name */ + functionName,/* function for operator */ + precedence, /* operator precedence */ + isLeftAssociative, /* operator is left associative */ + commutatorName, /* optional commutator operator + * name */ + negatorName, /* optional negator operator name */ + restrictionName, /* optional restrict. sel. + * procedure */ + joinName, /* optional join sel. procedure name */ + canHash, /* operator hashes */ + sortName1, /* optional first sort operator */ + sortName2); /* optional second sort operator */ + } /* ------------------- - * DefineAggregate + * DefineAggregate * ------------------ */ void -DefineAggregate(char *aggName, List *parameters) +DefineAggregate(char *aggName, List * parameters) { - char *stepfunc1Name = NULL; - char *stepfunc2Name = NULL; - char *finalfuncName = NULL; - char *baseType = NULL; - char *stepfunc1Type = NULL; - char *stepfunc2Type = NULL; - char *init1 = NULL; - char *init2 = NULL; - List *pl; - - foreach (pl, parameters) { - DefElem *defel = (DefElem *)lfirst(pl); - - /* - * sfunc1 - */ - if (!strcasecmp(defel->defname, "sfunc1")) { - stepfunc1Name = defGetString(defel); - } else if (!strcasecmp(defel->defname, "basetype")) { - baseType = defGetString(defel); - } else if (!strcasecmp(defel->defname, "stype1")) { - stepfunc1Type = defGetString(defel); - - /* - * sfunc2 - */ - } else if (!strcasecmp(defel->defname, "sfunc2")) { - stepfunc2Name = defGetString(defel); - } else if (!strcasecmp(defel->defname, "stype2")) { - stepfunc2Type = defGetString(defel); - /* - * final - */ - } else if (!strcasecmp(defel->defname, "finalfunc")) { - finalfuncName = defGetString(defel); - /* - * initial conditions - */ - } else if (!strcasecmp(defel->defname, "initcond1")) { - init1 = defGetString(defel); - } else if (!strcasecmp(defel->defname, "initcond2")) { - init2 = defGetString(defel); - } else { - elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized", - defel->defname); - } - } - - /* - * make sure we have our required definitions - */ - if (baseType==NULL) - elog(WARN, "Define: \"basetype\" unspecified"); - if (stepfunc1Name!=NULL) { - if (stepfunc1Type==NULL) - elog(WARN, "Define: \"stype1\" unspecified"); - } - if (stepfunc2Name!=NULL) { - if (stepfunc2Type==NULL) - elog(WARN, "Define: \"stype2\" unspecified"); - } - - /* - * Most of the argument-checking is done inside of AggregateCreate - */ - AggregateCreate(aggName, /* aggregate name */ - stepfunc1Name, /* first step function name */ - stepfunc2Name, /* second step function name */ - finalfuncName, /* final function name */ - baseType, /* type of object being aggregated */ - stepfunc1Type, /* return type of first function */ - stepfunc2Type, /* return type of second function */ - init1, /* first initial condition */ - init2); /* second initial condition */ - - /* XXX free palloc'd memory */ + char *stepfunc1Name = NULL; + char *stepfunc2Name = NULL; + char *finalfuncName = NULL; + char *baseType = NULL; + char *stepfunc1Type = NULL; + char *stepfunc2Type = NULL; + char *init1 = NULL; + char *init2 = NULL; + List *pl; + + foreach(pl, parameters) + { + DefElem *defel = (DefElem *) lfirst(pl); + + /* + * sfunc1 + */ + if (!strcasecmp(defel->defname, "sfunc1")) + { + stepfunc1Name = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "basetype")) + { + baseType = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "stype1")) + { + stepfunc1Type = defGetString(defel); + + /* + * sfunc2 + */ + } + else if (!strcasecmp(defel->defname, "sfunc2")) + { + stepfunc2Name = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "stype2")) + { + stepfunc2Type = defGetString(defel); + + /* + * final + */ + } + else if (!strcasecmp(defel->defname, "finalfunc")) + { + finalfuncName = defGetString(defel); + + /* + * initial conditions + */ + } + else if (!strcasecmp(defel->defname, "initcond1")) + { + init1 = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "initcond2")) + { + init2 = defGetString(defel); + } + else + { + elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized", + defel->defname); + } + } + + /* + * make sure we have our required definitions + */ + if (baseType == NULL) + elog(WARN, "Define: \"basetype\" unspecified"); + if (stepfunc1Name != NULL) + { + if (stepfunc1Type == NULL) + elog(WARN, "Define: \"stype1\" unspecified"); + } + if (stepfunc2Name != NULL) + { + if (stepfunc2Type == NULL) + elog(WARN, "Define: \"stype2\" unspecified"); + } + + /* + * Most of the argument-checking is done inside of AggregateCreate + */ + AggregateCreate(aggName, /* aggregate name */ + stepfunc1Name, /* first step function name */ + stepfunc2Name, /* second step function name */ + finalfuncName, /* final function name */ + baseType, /* type of object being aggregated */ + stepfunc1Type, /* return type of first function */ + stepfunc2Type, /* return type of second function */ + init1, /* first initial condition */ + init2); /* second initial condition */ + + /* XXX free palloc'd memory */ } /* * DefineType -- - * Registers a new type. + * Registers a new type. * */ void -DefineType(char *typeName, List *parameters) +DefineType(char *typeName, List * parameters) { - int16 internalLength= 0; /* int2 */ - int16 externalLength= 0; /* int2 */ - char *elemName = NULL; - char *inputName = NULL; - char *outputName = NULL; - char *sendName = NULL; - char *receiveName = NULL; - char *defaultValue = NULL; /* Datum */ - bool byValue = false; - char delimiter = DEFAULT_TYPDELIM; - char *shadow_type; - List *pl; - char alignment = 'i'; /* default alignment */ - - /* - * Type names can only be 15 characters long, so that the shadow type - * can be created using the 16th character as necessary. - */ - if (strlen(typeName) >= (NAMEDATALEN - 1)) { - elog(WARN, "DefineType: type names must be %d characters or less", - NAMEDATALEN - 1); - } - - foreach(pl, parameters) { - DefElem *defel = (DefElem*)lfirst(pl); - - if (!strcasecmp(defel->defname, "internallength")) { - internalLength = defGetTypeLength(defel); - }else if (!strcasecmp(defel->defname, "externallength")) { - externalLength = defGetTypeLength(defel); - }else if (!strcasecmp(defel->defname, "input")) { - inputName = defGetString(defel); - }else if (!strcasecmp(defel->defname, "output")) { - outputName = defGetString(defel); - }else if (!strcasecmp(defel->defname, "send")) { - sendName = defGetString(defel); - }else if (!strcasecmp(defel->defname, "delimiter")) { - char *p = defGetString(defel); - delimiter = p[0]; - }else if (!strcasecmp(defel->defname, "receive")) { - receiveName = defGetString(defel); - }else if (!strcasecmp(defel->defname, "element")) { - elemName = defGetString(defel); - }else if (!strcasecmp(defel->defname, "default")) { - defaultValue = defGetString(defel); - }else if (!strcasecmp(defel->defname, "passedbyvalue")) { - byValue = true; - }else if (!strcasecmp(defel->defname, "alignment")) { - char *a = defGetString(defel); - if (!strcasecmp(a, "double")) { - alignment = 'd'; - } else if (!strcasecmp(a, "int")) { - alignment = 'i'; - } else { - elog(WARN, "DefineType: \"%s\" alignment not recognized", - a); - } - }else { - elog(NOTICE, "DefineType: attribute \"%s\" not recognized", - defel->defname); - } - } - - /* - * make sure we have our required definitions - */ - if (inputName==NULL) - elog(WARN, "Define: \"input\" unspecified"); - if (outputName==NULL) - elog(WARN, "Define: \"output\" unspecified"); - - /* ---------------- - * now have TypeCreate do all the real work. - * ---------------- - */ - TypeCreate(typeName, /* type name */ - InvalidOid, /* relation oid (n/a here) */ - internalLength, /* internal size */ - externalLength, /* external size */ - 'b', /* type-type (base type) */ - delimiter, /* array element delimiter */ - inputName, /* input procedure */ - outputName, /* output procedure */ - sendName, /* send procedure */ - receiveName, /* receive procedure */ - elemName, /* element type name */ - defaultValue, /* default type value */ - byValue, /* passed by value */ - alignment); - - /* ---------------- - * When we create a true type (as opposed to a complex type) - * we need to have an shadow array entry for it in pg_type as well. - * ---------------- - */ - shadow_type = makeArrayTypeName(typeName); - - TypeCreate(shadow_type, /* type name */ - InvalidOid, /* relation oid (n/a here) */ - -1, /* internal size */ - -1, /* external size */ - 'b', /* type-type (base type) */ - DEFAULT_TYPDELIM, /* array element delimiter */ - "array_in", /* input procedure */ - "array_out", /* output procedure */ - "array_out", /* send procedure */ - "array_in", /* receive procedure */ - typeName, /* element type name */ - defaultValue, /* default type value */ - false, /* never passed by value */ - alignment); - - pfree(shadow_type); + int16 internalLength = 0; /* int2 */ + int16 externalLength = 0; /* int2 */ + char *elemName = NULL; + char *inputName = NULL; + char *outputName = NULL; + char *sendName = NULL; + char *receiveName = NULL; + char *defaultValue = NULL; /* Datum */ + bool byValue = false; + char delimiter = DEFAULT_TYPDELIM; + char *shadow_type; + List *pl; + char alignment = 'i'; /* default alignment */ + + /* + * Type names can only be 15 characters long, so that the shadow type + * can be created using the 16th character as necessary. + */ + if (strlen(typeName) >= (NAMEDATALEN - 1)) + { + elog(WARN, "DefineType: type names must be %d characters or less", + NAMEDATALEN - 1); + } + + foreach(pl, parameters) + { + DefElem *defel = (DefElem *) lfirst(pl); + + if (!strcasecmp(defel->defname, "internallength")) + { + internalLength = defGetTypeLength(defel); + } + else if (!strcasecmp(defel->defname, "externallength")) + { + externalLength = defGetTypeLength(defel); + } + else if (!strcasecmp(defel->defname, "input")) + { + inputName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "output")) + { + outputName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "send")) + { + sendName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "delimiter")) + { + char *p = defGetString(defel); + + delimiter = p[0]; + } + else if (!strcasecmp(defel->defname, "receive")) + { + receiveName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "element")) + { + elemName = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "default")) + { + defaultValue = defGetString(defel); + } + else if (!strcasecmp(defel->defname, "passedbyvalue")) + { + byValue = true; + } + else if (!strcasecmp(defel->defname, "alignment")) + { + char *a = defGetString(defel); + + if (!strcasecmp(a, "double")) + { + alignment = 'd'; + } + else if (!strcasecmp(a, "int")) + { + alignment = 'i'; + } + else + { + elog(WARN, "DefineType: \"%s\" alignment not recognized", + a); + } + } + else + { + elog(NOTICE, "DefineType: attribute \"%s\" not recognized", + defel->defname); + } + } + + /* + * make sure we have our required definitions + */ + if (inputName == NULL) + elog(WARN, "Define: \"input\" unspecified"); + if (outputName == NULL) + elog(WARN, "Define: \"output\" unspecified"); + + /* ---------------- + * now have TypeCreate do all the real work. + * ---------------- + */ + TypeCreate(typeName, /* type name */ + InvalidOid, /* relation oid (n/a here) */ + internalLength, /* internal size */ + externalLength, /* external size */ + 'b', /* type-type (base type) */ + delimiter, /* array element delimiter */ + inputName, /* input procedure */ + outputName, /* output procedure */ + sendName, /* send procedure */ + receiveName, /* receive procedure */ + elemName, /* element type name */ + defaultValue, /* default type value */ + byValue, /* passed by value */ + alignment); + + /* ---------------- + * When we create a true type (as opposed to a complex type) + * we need to have an shadow array entry for it in pg_type as well. + * ---------------- + */ + shadow_type = makeArrayTypeName(typeName); + + TypeCreate(shadow_type, /* type name */ + InvalidOid, /* relation oid (n/a here) */ + -1, /* internal size */ + -1, /* external size */ + 'b', /* type-type (base type) */ + DEFAULT_TYPDELIM,/* array element delimiter */ + "array_in", /* input procedure */ + "array_out", /* output procedure */ + "array_out", /* send procedure */ + "array_in", /* receive procedure */ + typeName, /* element type name */ + defaultValue, /* default type value */ + false, /* never passed by value */ + alignment); + + pfree(shadow_type); } -static char * -defGetString(DefElem *def) +static char * +defGetString(DefElem * def) { - if (nodeTag(def->arg)!=T_String) - elog(WARN, "Define: \"%s\" = what?", def->defname); - return (strVal(def->arg)); + if (nodeTag(def->arg) != T_String) + elog(WARN, "Define: \"%s\" = what?", def->defname); + return (strVal(def->arg)); } -static int -defGetTypeLength(DefElem *def) +static int +defGetTypeLength(DefElem * def) { - if (nodeTag(def->arg)==T_Integer) - return (intVal(def->arg)); - else if (nodeTag(def->arg)==T_String && - !strcasecmp(strVal(def->arg),"variable")) - return -1; /* variable length */ - - elog(WARN, "Define: \"%s\" = what?", def->defname); - return -1; + if (nodeTag(def->arg) == T_Integer) + return (intVal(def->arg)); + else if (nodeTag(def->arg) == T_String && + !strcasecmp(strVal(def->arg), "variable")) + return -1; /* variable length */ + + elog(WARN, "Define: \"%s\" = what?", def->defname); + return -1; } diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 7d1f34d0327..192076e3911 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * explain.c-- - * Explain the query execution plan + * Explain the query execution plan * * Copyright (c) 1994-5, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.10 1997/08/18 20:52:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.11 1997/09/07 04:40:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ #include <postgres.h> #include <parser/catalog_utils.h> -#include <parser/parse_query.h> /* for MakeTimeRange() */ +#include <parser/parse_query.h> /* for MakeTimeRange() */ #include <nodes/plannodes.h> #include <tcop/tcopprot.h> #include <lib/stringinfo.h> @@ -25,79 +25,86 @@ #include <optimizer/planner.h> #include <access/xact.h> -typedef struct ExplainState { - /* options */ - bool printCost; /* print cost */ - bool printNodes; /* do nodeToString() instead */ - /* other states */ - List *rtable; /* range table */ -} ExplainState; +typedef struct ExplainState +{ + /* options */ + bool printCost; /* print cost */ + bool printNodes; /* do nodeToString() instead */ + /* other states */ + List *rtable; /* range table */ +} ExplainState; -static char *Explain_PlanToString(Plan *plan, ExplainState *es); +static char *Explain_PlanToString(Plan * plan, ExplainState * es); /* * ExplainQuery - - * print out the execution plan for a given query + * print out the execution plan for a given query * */ void -ExplainQuery(Query *query, bool verbose, CommandDest dest) +ExplainQuery(Query * query, bool verbose, CommandDest dest) { - char *s = NULL, *s2; - Plan *plan; - ExplainState *es; - int len; - - if (IsAbortedTransactionBlockState()) { - char *tag = "*ABORT STATE*"; - EndCommand(tag, dest); - - elog(NOTICE, "(transaction aborted): %s", - "queries ignored until END"); - - return; - } + char *s = NULL, + *s2; + Plan *plan; + ExplainState *es; + int len; - /* plan the queries (XXX we've ignored rewrite!!) */ - plan = planner(query); + if (IsAbortedTransactionBlockState()) + { + char *tag = "*ABORT STATE*"; - /* pg_plan could have failed */ - if (plan == NULL) - return; + EndCommand(tag, dest); + + elog(NOTICE, "(transaction aborted): %s", + "queries ignored until END"); + + return; + } - es = (ExplainState*)malloc(sizeof(ExplainState)); - memset(es, 0, sizeof(ExplainState)); + /* plan the queries (XXX we've ignored rewrite!!) */ + plan = planner(query); - es->printCost = true; /* default */ + /* pg_plan could have failed */ + if (plan == NULL) + return; - if (verbose) - es->printNodes = true; + es = (ExplainState *) malloc(sizeof(ExplainState)); + memset(es, 0, sizeof(ExplainState)); - es->rtable = query->rtable; + es->printCost = true; /* default */ - if (es->printNodes) - s = nodeToString(plan); + if (verbose) + es->printNodes = true; - if (es->printCost) { - s2 = Explain_PlanToString(plan, es); - if (s == NULL) - s = s2; - else { - strcat(s, "\n\n"); - strcat(s, s2); + es->rtable = query->rtable; + + if (es->printNodes) + s = nodeToString(plan); + + if (es->printCost) + { + s2 = Explain_PlanToString(plan, es); + if (s == NULL) + s = s2; + else + { + strcat(s, "\n\n"); + strcat(s, s2); + } } - } - - /* output the plan */ - len = strlen(s); - elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN-64, s); - len -= ELOG_MAXLEN-64; - while (len > 0) { - s += ELOG_MAXLEN-64; - elog(NOTICE, "%.*s", ELOG_MAXLEN-64, s); - len -= ELOG_MAXLEN-64; - } - free(es); + + /* output the plan */ + len = strlen(s); + elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN - 64, s); + len -= ELOG_MAXLEN - 64; + while (len > 0) + { + s += ELOG_MAXLEN - 64; + elog(NOTICE, "%.*s", ELOG_MAXLEN - 64, s); + len -= ELOG_MAXLEN - 64; + } + free(es); } /***************************************************************************** @@ -106,122 +113,130 @@ ExplainQuery(Query *query, bool verbose, CommandDest dest) /* * explain_outNode - - * converts a Node into ascii string and append it to 'str' + * converts a Node into ascii string and append it to 'str' */ static void -explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) +explain_outNode(StringInfo str, Plan * plan, int indent, ExplainState * es) { - char *pname; - char buf[1000]; - int i; - - if (plan==NULL) { + char *pname; + char buf[1000]; + int i; + + if (plan == NULL) + { + appendStringInfo(str, "\n"); + return; + } + + switch (nodeTag(plan)) + { + case T_Result: + pname = "Result"; + break; + case T_Append: + pname = "Append"; + break; + case T_NestLoop: + pname = "Nested Loop"; + break; + case T_MergeJoin: + pname = "Merge Join"; + break; + case T_HashJoin: + pname = "Hash Join"; + break; + case T_SeqScan: + pname = "Seq Scan"; + break; + case T_IndexScan: + pname = "Index Scan"; + break; + case T_Temp: + pname = "Temp Scan"; + break; + case T_Sort: + pname = "Sort"; + break; + case T_Group: + pname = "Group"; + break; + case T_Agg: + pname = "Aggregate"; + break; + case T_Unique: + pname = "Unique"; + break; + case T_Hash: + pname = "Hash"; + break; + case T_Tee: + pname = "Tee"; + break; + default: + pname = ""; + break; + } + + for (i = 0; i < indent; i++) + appendStringInfo(str, " "); + + appendStringInfo(str, pname); + switch (nodeTag(plan)) + { + case T_SeqScan: + case T_IndexScan: + if (((Scan *) plan)->scanrelid > 0) + { + RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable); + + sprintf(buf, " on %s", rte->refname); + appendStringInfo(str, buf); + } + break; + default: + break; + } + if (es->printCost) + { + sprintf(buf, " (cost=%.2f size=%d width=%d)", + plan->cost, plan->plan_size, plan->plan_width); + appendStringInfo(str, buf); + } appendStringInfo(str, "\n"); - return; - } - - switch(nodeTag(plan)) { - case T_Result: - pname = "Result"; - break; - case T_Append: - pname = "Append"; - break; - case T_NestLoop: - pname = "Nested Loop"; - break; - case T_MergeJoin: - pname = "Merge Join"; - break; - case T_HashJoin: - pname = "Hash Join"; - break; - case T_SeqScan: - pname = "Seq Scan"; - break; - case T_IndexScan: - pname = "Index Scan"; - break; - case T_Temp: - pname = "Temp Scan"; - break; - case T_Sort: - pname = "Sort"; - break; - case T_Group: - pname = "Group"; - break; - case T_Agg: - pname = "Aggregate"; - break; - case T_Unique: - pname = "Unique"; - break; - case T_Hash: - pname = "Hash"; - break; - case T_Tee: - pname = "Tee"; - break; - default: - pname = ""; - break; - } - - for(i=0; i < indent; i++) - appendStringInfo(str, " "); - - appendStringInfo(str, pname); - switch(nodeTag(plan)) { - case T_SeqScan: - case T_IndexScan: - if (((Scan*)plan)->scanrelid > 0) { - RangeTblEntry *rte = nth(((Scan*)plan)->scanrelid-1, es->rtable); - sprintf(buf, " on %s", rte->refname); - appendStringInfo(str, buf); + + /* lefttree */ + if (outerPlan(plan)) + { + for (i = 0; i < indent; i++) + appendStringInfo(str, " "); + appendStringInfo(str, " -> "); + explain_outNode(str, outerPlan(plan), indent + 1, es); } - break; - default: - break; - } - if (es->printCost) { - sprintf(buf, " (cost=%.2f size=%d width=%d)", - plan->cost, plan->plan_size, plan->plan_width); - appendStringInfo(str, buf); - } - appendStringInfo(str, "\n"); - - /* lefttree */ - if (outerPlan(plan)) { - for(i=0; i < indent; i++) - appendStringInfo(str, " "); - appendStringInfo(str, " -> "); - explain_outNode(str, outerPlan(plan), indent+1, es); - } - - /* righttree */ - if (innerPlan(plan)) { - for(i=0; i < indent; i++) - appendStringInfo(str, " "); - appendStringInfo(str, " -> "); - explain_outNode(str, innerPlan(plan), indent+1, es); - } - return; + + /* righttree */ + if (innerPlan(plan)) + { + for (i = 0; i < indent; i++) + appendStringInfo(str, " "); + appendStringInfo(str, " -> "); + explain_outNode(str, innerPlan(plan), indent + 1, es); + } + return; } -static char * -Explain_PlanToString(Plan *plan, ExplainState *es) +static char * +Explain_PlanToString(Plan * plan, ExplainState * es) { - StringInfo str; - char *s; - - if (plan==NULL) - return ""; - Assert(plan!=NULL); - str = makeStringInfo(); - explain_outNode(str, plan, 0, es); - s = str->data; - pfree(str); - - return s; + StringInfo str; + char *s; + + if (plan == NULL) + return ""; + Assert(plan != NULL); + str = makeStringInfo(); + explain_outNode(str, plan, 0, es); + s = str->data; + pfree(str); + + return s; } diff --git a/src/backend/commands/purge.c b/src/backend/commands/purge.c index 5c514fc8675..8000bbc7352 100644 --- a/src/backend/commands/purge.c +++ b/src/backend/commands/purge.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * purge.c-- - * the POSTGRES purge command. + * the POSTGRES purge command. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/purge.c,v 1.6 1997/08/12 22:52:25 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/purge.c,v 1.7 1997/09/07 04:40:51 momjian Exp $ * * Note: - * XXX There are many instances of int32 instead of ...Time. These - * should be changed once it is decided the signed'ness will be. + * XXX There are many instances of int32 instead of ...Time. These + * should be changed once it is decided the signed'ness will be. * *------------------------------------------------------------------------- */ @@ -21,145 +21,156 @@ #include <access/heapam.h> #include <access/xact.h> -#include <utils/tqual.h> /* for NowTimeQual */ +#include <utils/tqual.h> /* for NowTimeQual */ #include <catalog/catname.h> #include <catalog/indexing.h> #include <fmgr.h> #include <commands/purge.h> -#include <utils/builtins.h> /* for isreltime() */ +#include <utils/builtins.h> /* for isreltime() */ -static char cmdname[] = "RelationPurge"; +static char cmdname[] = "RelationPurge"; -#define RELATIVE 01 -#define ABSOLUTE 02 +#define RELATIVE 01 +#define ABSOLUTE 02 int32 RelationPurge(char *relationName, - char *absoluteTimeString, - char *relativeTimeString) + char *absoluteTimeString, + char *relativeTimeString) { - register i; - AbsoluteTime absoluteTime = INVALID_ABSTIME; - RelativeTime relativeTime = INVALID_RELTIME; - bits8 dateTag; - Relation relation; - HeapScanDesc scan; - static ScanKeyData key[1] = { - { 0, Anum_pg_class_relname, F_NAMEEQ } - }; - Buffer buffer; - HeapTuple newTuple, oldTuple; - AbsoluteTime currentTime; - char *values[Natts_pg_class]; - char nulls[Natts_pg_class]; - char replace[Natts_pg_class]; - Relation idescs[Num_pg_class_indices]; - - /* - * XXX for some reason getmyrelids (in inval.c) barfs when - * you heap_replace tuples from these classes. i thought - * setheapoverride would fix it but it didn't. for now, - * just disallow purge on these classes. - */ - if (strcmp(RelationRelationName, relationName) == 0 || - strcmp(AttributeRelationName, relationName) == 0 || - strcmp(AccessMethodRelationName, relationName) == 0 || - strcmp(AccessMethodOperatorRelationName, relationName) == 0) { - elog(WARN, "%s: cannot purge catalog \"%s\"", - cmdname, relationName); - } - - if (PointerIsValid(absoluteTimeString)) { - absoluteTime = (int32) nabstimein(absoluteTimeString); - absoluteTimeString[0] = '\0'; - if (absoluteTime == INVALID_ABSTIME) { - elog(NOTICE, "%s: bad absolute time string \"%s\"", - cmdname, absoluteTimeString); - elog(WARN, "purge not executed"); + register i; + AbsoluteTime absoluteTime = INVALID_ABSTIME; + RelativeTime relativeTime = INVALID_RELTIME; + bits8 dateTag; + Relation relation; + HeapScanDesc scan; + static ScanKeyData key[1] = { + {0, Anum_pg_class_relname, F_NAMEEQ} + }; + Buffer buffer; + HeapTuple newTuple, + oldTuple; + AbsoluteTime currentTime; + char *values[Natts_pg_class]; + char nulls[Natts_pg_class]; + char replace[Natts_pg_class]; + Relation idescs[Num_pg_class_indices]; + + /* + * XXX for some reason getmyrelids (in inval.c) barfs when you + * heap_replace tuples from these classes. i thought setheapoverride + * would fix it but it didn't. for now, just disallow purge on these + * classes. + */ + if (strcmp(RelationRelationName, relationName) == 0 || + strcmp(AttributeRelationName, relationName) == 0 || + strcmp(AccessMethodRelationName, relationName) == 0 || + strcmp(AccessMethodOperatorRelationName, relationName) == 0) + { + elog(WARN, "%s: cannot purge catalog \"%s\"", + cmdname, relationName); } - } - -#ifdef PURGEDEBUG - elog(DEBUG, "%s: absolute time `%s' is %d.", - cmdname, absoluteTimeString, absoluteTime); -#endif /* defined(PURGEDEBUG) */ - - if (PointerIsValid(relativeTimeString)) { - if (isreltime(relativeTimeString) != 1) { - elog(WARN, "%s: bad relative time string \"%s\"", - cmdname, relativeTimeString); + + if (PointerIsValid(absoluteTimeString)) + { + absoluteTime = (int32) nabstimein(absoluteTimeString); + absoluteTimeString[0] = '\0'; + if (absoluteTime == INVALID_ABSTIME) + { + elog(NOTICE, "%s: bad absolute time string \"%s\"", + cmdname, absoluteTimeString); + elog(WARN, "purge not executed"); + } } - relativeTime = reltimein(relativeTimeString); - + +#ifdef PURGEDEBUG + elog(DEBUG, "%s: absolute time `%s' is %d.", + cmdname, absoluteTimeString, absoluteTime); +#endif /* defined(PURGEDEBUG) */ + + if (PointerIsValid(relativeTimeString)) + { + if (isreltime(relativeTimeString) != 1) + { + elog(WARN, "%s: bad relative time string \"%s\"", + cmdname, relativeTimeString); + } + relativeTime = reltimein(relativeTimeString); + #ifdef PURGEDEBUG - elog(DEBUG, "%s: relative time `%s' is %d.", - cmdname, relativeTimeString, relativeTime); -#endif /* defined(PURGEDEBUG) */ - } - - /* - * Find the RELATION relation tuple for the given relation. - */ - relation = heap_openr(RelationRelationName); - key[0].sk_argument = PointerGetDatum(relationName); - fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs); - - scan = heap_beginscan(relation, 0, NowTimeQual, 1, key); - oldTuple = heap_getnext(scan, 0, &buffer); - if (!HeapTupleIsValid(oldTuple)) { + elog(DEBUG, "%s: relative time `%s' is %d.", + cmdname, relativeTimeString, relativeTime); +#endif /* defined(PURGEDEBUG) */ + } + + /* + * Find the RELATION relation tuple for the given relation. + */ + relation = heap_openr(RelationRelationName); + key[0].sk_argument = PointerGetDatum(relationName); + fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs); + + scan = heap_beginscan(relation, 0, NowTimeQual, 1, key); + oldTuple = heap_getnext(scan, 0, &buffer); + if (!HeapTupleIsValid(oldTuple)) + { + heap_endscan(scan); + heap_close(relation); + elog(WARN, "%s: no such relation: %s", cmdname, relationName); + return (0); + } + + /* + * Dig around in the tuple. + */ + currentTime = GetCurrentTransactionStartTime(); + if (!RelativeTimeIsValid(relativeTime)) + { + dateTag = ABSOLUTE; + if (!AbsoluteTimeIsValid(absoluteTime)) + absoluteTime = currentTime; + } + else if (!AbsoluteTimeIsValid(absoluteTime)) + dateTag = RELATIVE; + else + dateTag = ABSOLUTE | RELATIVE; + + for (i = 0; i < Natts_pg_class; ++i) + { + nulls[i] = heap_attisnull(oldTuple, i + 1) ? 'n' : ' '; + values[i] = NULL; + replace[i] = ' '; + } + if (dateTag & ABSOLUTE) + { + values[Anum_pg_class_relexpires - 1] = + (char *) UInt32GetDatum(absoluteTime); + replace[Anum_pg_class_relexpires - 1] = 'r'; + } + if (dateTag & RELATIVE) + { + values[Anum_pg_class_relpreserved - 1] = + (char *) UInt32GetDatum(relativeTime); + replace[Anum_pg_class_relpreserved - 1] = 'r'; + } + + /* + * Change the RELATION relation tuple for the given relation. + */ + newTuple = heap_modifytuple(oldTuple, buffer, relation, (Datum *) values, + nulls, replace); + + /* XXX How do you detect an insertion error?? */ + heap_replace(relation, &newTuple->t_ctid, newTuple); + + /* keep the system catalog indices current */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newTuple); + CatalogCloseIndices(Num_pg_class_indices, idescs); + + pfree(newTuple); + heap_endscan(scan); heap_close(relation); - elog(WARN, "%s: no such relation: %s", cmdname, relationName); - return(0); - } - - /* - * Dig around in the tuple. - */ - currentTime = GetCurrentTransactionStartTime(); - if (!RelativeTimeIsValid(relativeTime)) { - dateTag = ABSOLUTE; - if (!AbsoluteTimeIsValid(absoluteTime)) - absoluteTime = currentTime; - } else if (!AbsoluteTimeIsValid(absoluteTime)) - dateTag = RELATIVE; - else - dateTag = ABSOLUTE | RELATIVE; - - for (i = 0; i < Natts_pg_class; ++i) { - nulls[i] = heap_attisnull(oldTuple, i+1) ? 'n' : ' '; - values[i] = NULL; - replace[i] = ' '; - } - if (dateTag & ABSOLUTE) { - values[Anum_pg_class_relexpires-1] = - (char *) UInt32GetDatum(absoluteTime); - replace[Anum_pg_class_relexpires-1] = 'r'; - } - if (dateTag & RELATIVE) { - values[Anum_pg_class_relpreserved-1] = - (char *) UInt32GetDatum(relativeTime); - replace[Anum_pg_class_relpreserved-1] = 'r'; - } - - /* - * Change the RELATION relation tuple for the given relation. - */ - newTuple = heap_modifytuple(oldTuple, buffer, relation, (Datum*)values, - nulls, replace); - - /* XXX How do you detect an insertion error?? */ - heap_replace(relation, &newTuple->t_ctid, newTuple); - - /* keep the system catalog indices current */ - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newTuple); - CatalogCloseIndices(Num_pg_class_indices, idescs); - - pfree(newTuple); - - heap_endscan(scan); - heap_close(relation); - return(1); + return (1); } - diff --git a/src/backend/commands/recipe.c b/src/backend/commands/recipe.c index e6aa009bd33..bf05c293d13 100644 --- a/src/backend/commands/recipe.c +++ b/src/backend/commands/recipe.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * recipe.c-- - * routines for handling execution of Tioga recipes + * routines for handling execution of Tioga recipes * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.6 1997/08/12 20:15:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.7 1997/09/07 04:40:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,7 +21,7 @@ #include <commands/recipe.h> #include <libpq/libpq-be.h> #include <utils/builtins.h> -#include <utils/relcache.h> /* for RelationNameGetRelation*/ +#include <utils/relcache.h> /* for RelationNameGetRelation */ #include <parser/parse_query.h> #include <rewrite/rewriteHandler.h> #include <rewrite/rewriteManip.h> @@ -35,9 +35,12 @@ extern CommandDest whereToSendOutput; #ifndef TIOGA -void beginRecipe(RecipeStmt *stmt) { - elog(NOTICE,"You must compile with TIOGA defined in order to use recipes\n"); +void +beginRecipe(RecipeStmt * stmt) +{ + elog(NOTICE, "You must compile with TIOGA defined in order to use recipes\n"); } + #else #include <tioga/tgRecipe.h> @@ -45,49 +48,59 @@ void beginRecipe(RecipeStmt *stmt) { #define DEBUG_RECIPE 1 /* structure to keep track of the tee node plans */ -typedef struct _teePlanInfo { - char* tpi_relName; - Query* tpi_parsetree; - Plan* tpi_plan; -} TeePlanInfo; - -typedef struct _teeInfo { - int num; - TeePlanInfo *val; -} TeeInfo; - -QueryTreeList *appendQlist(QueryTreeList *q1, QueryTreeList *q2); -void OffsetVarAttno(Node* node, int varno, int offset); - -static void appendTeeQuery(TeeInfo *teeInfo, - QueryTreeList *q, - char* teeNodeName); - -static Plan* replaceTeeScans(Plan* plan, - Query* parsetree, - TeeInfo *teeInfo); -static void replaceSeqScan(Plan* plan, - Plan* parent, - int rt_ind, - Plan* tplan); - -static void tg_rewriteQuery(TgRecipe* r, TgNode *n, - QueryTreeList *q, - QueryTreeList *inputQlist); -static Node *tg_replaceNumberedParam(Node* expression, - int pnum, - int rt_ind, - char *teeRelName); -static Node *tg_rewriteParamsInExpr(Node *expression, - QueryTreeList *inputQlist); -static QueryTreeList *tg_parseSubQuery(TgRecipe* r, - TgNode* n, - TeeInfo* teeInfo); -static QueryTreeList* tg_parseTeeNode(TgRecipe *r, - TgNode *n, - int i, - QueryTreeList *qList, - TeeInfo* teeInfo); +typedef struct _teePlanInfo +{ + char *tpi_relName; + Query *tpi_parsetree; + Plan *tpi_plan; +} TeePlanInfo; + +typedef struct _teeInfo +{ + int num; + TeePlanInfo *val; +} TeeInfo; + +QueryTreeList *appendQlist(QueryTreeList * q1, QueryTreeList * q2); +void OffsetVarAttno(Node * node, int varno, int offset); + +static void +appendTeeQuery(TeeInfo * teeInfo, + QueryTreeList * q, + char *teeNodeName); + +static Plan * +replaceTeeScans(Plan * plan, + Query * parsetree, + TeeInfo * teeInfo); +static void +replaceSeqScan(Plan * plan, + Plan * parent, + int rt_ind, + Plan * tplan); + +static void +tg_rewriteQuery(TgRecipe * r, TgNode * n, + QueryTreeList * q, + QueryTreeList * inputQlist); +static Node * +tg_replaceNumberedParam(Node * expression, + int pnum, + int rt_ind, + char *teeRelName); +static Node * +tg_rewriteParamsInExpr(Node * expression, + QueryTreeList * inputQlist); +static QueryTreeList * +tg_parseSubQuery(TgRecipe * r, + TgNode * n, + TeeInfo * teeInfo); +static QueryTreeList * +tg_parseTeeNode(TgRecipe * r, + TgNode * n, + int i, + QueryTreeList * qList, + TeeInfo * teeInfo); /* @@ -96,172 +109,192 @@ static QueryTreeList* tg_parseTeeNode(TgRecipe *r, To parse a Tioga recipe, we start from an eye node and go backwards through its input nodes. To rewrite a Tioga node, we do the following: - 1) parse the node we're at in the standard way (calling parser() ) - 2) rewrite its input nodes recursively using Tioga rewrite - 3) now, with the rewritten input parse trees and the original parse tree - of the node, we rewrite the the node. - To do the rewrite, we use the target lists, range tables, and - qualifications of the input parse trees + 1) parse the node we're at in the standard way (calling parser() ) + 2) rewrite its input nodes recursively using Tioga rewrite + 3) now, with the rewritten input parse trees and the original parse tree + of the node, we rewrite the the node. + To do the rewrite, we use the target lists, range tables, and + qualifications of the input parse trees */ /* * beginRecipe: - * this is the main function to recipe execution - * this function is invoked for EXECUTE RECIPE ... statements - * - * takes in a RecipeStmt structure from the parser + * this is the main function to recipe execution + * this function is invoked for EXECUTE RECIPE ... statements + * + * takes in a RecipeStmt structure from the parser * and returns a list of cursor names */ void -beginRecipe(RecipeStmt* stmt) +beginRecipe(RecipeStmt * stmt) { - TgRecipe* r; - int i; - QueryTreeList *qList; - char portalName[1024]; - - Plan *plan; - TupleDesc attinfo; - QueryDesc *queryDesc; - Query *parsetree; - - int numTees; - TeeInfo* teeInfo; - - /* retrieveRecipe() reads the recipe from the database - and returns a TgRecipe* structure we can work with */ - - r = retrieveRecipe(stmt->recipeName); - - if (r == NULL) return; - - /* find the number of tees in the recipe */ - numTees = r->tees->num; - - if (numTees > 0) { - /* allocate a teePlan structure */ - teeInfo = (TeeInfo*)malloc(sizeof(TeeInfo)); - teeInfo->num = numTees; - teeInfo->val = (TeePlanInfo*)malloc(numTees * sizeof(TeePlanInfo)); - for (i=0;i<numTees;i++) { - teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName; - teeInfo->val[i].tpi_parsetree = NULL; - teeInfo->val[i].tpi_plan = NULL; - } - } else - teeInfo = NULL; - - /* - * for each viewer in the recipe, go backwards from each viewer input - * and generate a plan. Attach the plan to cursors. - **/ - for (i=0;i<r->eyes->num;i++) { - TgNodePtr e; - - e = r->eyes->val[i]; - if (e->inNodes->num > 1) { - elog(NOTICE, - "beginRecipe: Currently eyes cannot have more than one input"); - } - if (e->inNodes->num == 0) { - /* no input to this eye, skip it */ - continue; - } + TgRecipe *r; + int i; + QueryTreeList *qList; + char portalName[1024]; + + Plan *plan; + TupleDesc attinfo; + QueryDesc *queryDesc; + Query *parsetree; + + int numTees; + TeeInfo *teeInfo; + + /* + * retrieveRecipe() reads the recipe from the database and returns a + * TgRecipe* structure we can work with + */ + + r = retrieveRecipe(stmt->recipeName); + + if (r == NULL) + return; + + /* find the number of tees in the recipe */ + numTees = r->tees->num; + + if (numTees > 0) + { + /* allocate a teePlan structure */ + teeInfo = (TeeInfo *) malloc(sizeof(TeeInfo)); + teeInfo->num = numTees; + teeInfo->val = (TeePlanInfo *) malloc(numTees * sizeof(TeePlanInfo)); + for (i = 0; i < numTees; i++) + { + teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName; + teeInfo->val[i].tpi_parsetree = NULL; + teeInfo->val[i].tpi_plan = NULL; + } + } + else + teeInfo = NULL; + + /* + * for each viewer in the recipe, go backwards from each viewer input + * and generate a plan. Attach the plan to cursors. + */ + for (i = 0; i < r->eyes->num; i++) + { + TgNodePtr e; + + e = r->eyes->val[i]; + if (e->inNodes->num > 1) + { + elog(NOTICE, + "beginRecipe: Currently eyes cannot have more than one input"); + } + if (e->inNodes->num == 0) + { + /* no input to this eye, skip it */ + continue; + } #ifdef DEBUG_RECIPE - elog(NOTICE,"beginRecipe: eyes[%d] = %s\n", i, e->nodeName); -#endif /* DEBUG_RECIPE */ - - qList = tg_parseSubQuery(r,e->inNodes->val[0], teeInfo); - - if (qList == NULL) { - /* eye is directly connected to a tee node */ - /* XXX TODO: handle this case */ - } - - /* now, plan the queries */ - /* should really do everything pg_plan() does, but for now, - we skip the rule rewrite and time qual stuff */ - - /* ---------------------------------------------------------- - * 1) plan the main query, everything from an eye node back to - a Tee - * ---------------------------------------------------------- */ - parsetree = qList->qtrees[0]; - - /* before we plan, we want to see all the changes - we did, during the rewrite phase, such as - creating the tee tables, - setheapoverride() allows us to see the changes */ - setheapoverride(true); - plan = planner(parsetree); - - /* ---------------------------------------------------------- - * 2) plan the tee queries, (subgraphs rooted from a Tee) - by the time the eye is processed, all tees that contribute - to that eye will have been included in the teeInfo list - * ---------------------------------------------------------- */ - if (teeInfo) { - int t; - Plan* tplan; - Tee* newplan; - - for (t=0; t<teeInfo->num;t++) { - if (teeInfo->val[t].tpi_plan == NULL) { - /* plan it in the usual fashion */ - tplan = planner(teeInfo->val[t].tpi_parsetree); - - /* now add a tee node to the root of the plan */ -elog(NOTICE, "adding tee plan node to the root of the %s\n", - teeInfo->val[t].tpi_relName); - newplan = (Tee*)makeNode(Tee); - newplan->plan.targetlist = tplan->targetlist; - newplan->plan.qual = NULL; /* tplan->qual; */ - newplan->plan.lefttree = tplan; - newplan->plan.righttree = NULL; - newplan->leftParent = NULL; - newplan->rightParent = NULL; - /* the range table of the tee is the range table - of the tplan */ - newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable; - strcpy(newplan->teeTableName, - teeInfo->val[t].tpi_relName); - teeInfo->val[t].tpi_plan = (Plan*)newplan; - } - } - - /* ---------------------------------------------------------- - * 3) replace the tee table scans in the main plan with - actual tee plannodes - * ---------------------------------------------------------- */ - - plan = replaceTeeScans(plan, parsetree, teeInfo); - - } /* if (teeInfo) */ - - setheapoverride(false); - - /* define a portal for this viewer input */ - /* for now, eyes can only have one input */ - sprintf(portalName, "%s%d",e->nodeName,0); - - queryDesc = CreateQueryDesc(parsetree, - plan, - whereToSendOutput); - /* ---------------- - * call ExecStart to prepare the plan for execution - * ---------------- - */ - attinfo = ExecutorStart(queryDesc,NULL); - - ProcessPortal(portalName, - parsetree, - plan, - attinfo, - whereToSendOutput); -elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName); - } + elog(NOTICE, "beginRecipe: eyes[%d] = %s\n", i, e->nodeName); +#endif /* DEBUG_RECIPE */ + + qList = tg_parseSubQuery(r, e->inNodes->val[0], teeInfo); + + if (qList == NULL) + { + /* eye is directly connected to a tee node */ + /* XXX TODO: handle this case */ + } + + /* now, plan the queries */ + + /* + * should really do everything pg_plan() does, but for now, we + * skip the rule rewrite and time qual stuff + */ + + /* ---------------------------------------------------------- + * 1) plan the main query, everything from an eye node back to + a Tee + * ---------------------------------------------------------- */ + parsetree = qList->qtrees[0]; + + /* + * before we plan, we want to see all the changes we did, during + * the rewrite phase, such as creating the tee tables, + * setheapoverride() allows us to see the changes + */ + setheapoverride(true); + plan = planner(parsetree); + + /* ---------------------------------------------------------- + * 2) plan the tee queries, (subgraphs rooted from a Tee) + by the time the eye is processed, all tees that contribute + to that eye will have been included in the teeInfo list + * ---------------------------------------------------------- */ + if (teeInfo) + { + int t; + Plan *tplan; + Tee *newplan; + + for (t = 0; t < teeInfo->num; t++) + { + if (teeInfo->val[t].tpi_plan == NULL) + { + /* plan it in the usual fashion */ + tplan = planner(teeInfo->val[t].tpi_parsetree); + + /* now add a tee node to the root of the plan */ + elog(NOTICE, "adding tee plan node to the root of the %s\n", + teeInfo->val[t].tpi_relName); + newplan = (Tee *) makeNode(Tee); + newplan->plan.targetlist = tplan->targetlist; + newplan->plan.qual = NULL; /* tplan->qual; */ + newplan->plan.lefttree = tplan; + newplan->plan.righttree = NULL; + newplan->leftParent = NULL; + newplan->rightParent = NULL; + + /* + * the range table of the tee is the range table of + * the tplan + */ + newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable; + strcpy(newplan->teeTableName, + teeInfo->val[t].tpi_relName); + teeInfo->val[t].tpi_plan = (Plan *) newplan; + } + } + + /* ---------------------------------------------------------- + * 3) replace the tee table scans in the main plan with + actual tee plannodes + * ---------------------------------------------------------- */ + + plan = replaceTeeScans(plan, parsetree, teeInfo); + + } /* if (teeInfo) */ + + setheapoverride(false); + + /* define a portal for this viewer input */ + /* for now, eyes can only have one input */ + sprintf(portalName, "%s%d", e->nodeName, 0); + + queryDesc = CreateQueryDesc(parsetree, + plan, + whereToSendOutput); + /* ---------------- + * call ExecStart to prepare the plan for execution + * ---------------- + */ + attinfo = ExecutorStart(queryDesc, NULL); + + ProcessPortal(portalName, + parsetree, + plan, + attinfo, + whereToSendOutput); + elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName); + } } @@ -269,109 +302,122 @@ elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName); /* * tg_rewriteQuery - - * r - the recipe being rewritten - * n - the node that we're current at - * q - a QueryTree List containing the parse tree of the node - * inputQlist - the parsetrees of its input nodes, - * the size of inputQlist must be the same as the - * number of input nodes. Some elements in the inpuQlist - * may be null if the inputs to those nodes are unconnected + * r - the recipe being rewritten + * n - the node that we're current at + * q - a QueryTree List containing the parse tree of the node + * inputQlist - the parsetrees of its input nodes, + * the size of inputQlist must be the same as the + * number of input nodes. Some elements in the inpuQlist + * may be null if the inputs to those nodes are unconnected * - * this is the main routine for rewriting the recipe queries - * the original query tree 'q' is modified + * this is the main routine for rewriting the recipe queries + * the original query tree 'q' is modified */ -static void -tg_rewriteQuery(TgRecipe* r, - TgNode *n, - QueryTreeList *q, - QueryTreeList *inputQlist) +static void +tg_rewriteQuery(TgRecipe * r, + TgNode * n, + QueryTreeList * q, + QueryTreeList * inputQlist) { - Query* orig; - Query* inputQ; - int i; - List *rtable; - List *input_rtable; - int rt_length; - - /* orig is the original parse tree of the node */ - orig = q->qtrees[0]; - - - /*------------------------------------------------------------------- - step 1: - - form a combined range table from all the range tables in the original - query as well as the input nodes - - form a combined qualification from the qual in the original plus - the quals of the input nodes - ------------------------------------------------------------------- - */ - - /* start with the original range table */ - rtable = orig->rtable; - rt_length = length(rtable); - - for (i=0;i<n->inNodes->num;i++) { - if (n->inNodes->val[i] != NULL && - n->inNodes->val[i]->nodeType != TG_TEE_NODE) { - inputQ = inputQlist->qtrees[i]; - input_rtable = inputQ->rtable; - - /* need to offset the var nodes in the qual and targetlist - because they are indexed off the original rtable */ - OffsetVarNodes((Node*)inputQ->qual, rt_length); - OffsetVarNodes((Node*)inputQ->targetList, rt_length); - - /* append the range tables from the children nodes */ - rtable = nconc (rtable, input_rtable); - - /* append the qualifications of the child node into the - original qual list */ - AddQual(orig, inputQ->qual); + Query *orig; + Query *inputQ; + int i; + List *rtable; + List *input_rtable; + int rt_length; + + /* orig is the original parse tree of the node */ + orig = q->qtrees[0]; + + + /*------------------------------------------------------------------- + step 1: + + form a combined range table from all the range tables in the original + query as well as the input nodes + + form a combined qualification from the qual in the original plus + the quals of the input nodes + ------------------------------------------------------------------- + */ + + /* start with the original range table */ + rtable = orig->rtable; + rt_length = length(rtable); + + for (i = 0; i < n->inNodes->num; i++) + { + if (n->inNodes->val[i] != NULL && + n->inNodes->val[i]->nodeType != TG_TEE_NODE) + { + inputQ = inputQlist->qtrees[i]; + input_rtable = inputQ->rtable; + + /* + * need to offset the var nodes in the qual and targetlist + * because they are indexed off the original rtable + */ + OffsetVarNodes((Node *) inputQ->qual, rt_length); + OffsetVarNodes((Node *) inputQ->targetList, rt_length); + + /* append the range tables from the children nodes */ + rtable = nconc(rtable, input_rtable); + + /* + * append the qualifications of the child node into the + * original qual list + */ + AddQual(orig, inputQ->qual); + } } - } - orig->rtable = rtable; - - /* step 2: - rewrite the target list of the original parse tree - if there are any references to params, replace them with - the appropriate target list entry of the children node - */ - if (orig->targetList != NIL) { - List *tl; - TargetEntry *tle; - - foreach (tl, orig->targetList) { - tle = lfirst(tl); - if (tle->resdom != NULL) { - tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist); - } - } - } - - /* step 3: - rewrite the qual of the original parse tree - if there are any references to params, replace them with - the appropriate target list entry of the children node - */ - if (orig->qual) { - if (nodeTag(orig->qual) == T_List) { - elog(WARN, "tg_rewriteQuery: Whoa! why is my qual a List???"); + orig->rtable = rtable; + + /* + * step 2: rewrite the target list of the original parse tree if there + * are any references to params, replace them with the appropriate + * target list entry of the children node + */ + if (orig->targetList != NIL) + { + List *tl; + TargetEntry *tle; + + foreach(tl, orig->targetList) + { + tle = lfirst(tl); + if (tle->resdom != NULL) + { + tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist); + } + } + } + + /* + * step 3: rewrite the qual of the original parse tree if there are + * any references to params, replace them with the appropriate target + * list entry of the children node + */ + if (orig->qual) + { + if (nodeTag(orig->qual) == T_List) + { + elog(WARN, "tg_rewriteQuery: Whoa! why is my qual a List???"); + } + orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist); } - orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist); - } - /* at this point, we're done with the rewrite, the querytreelist q - has been modified */ + /* + * at this point, we're done with the rewrite, the querytreelist q has + * been modified + */ } /* tg_replaceNumberedParam: - this procedure replaces the specified numbered param with a + this procedure replaces the specified numbered param with a reference to a range table this procedure recursively calls itself @@ -379,104 +425,137 @@ tg_rewriteQuery(TgRecipe* r, it returns a (possibly modified) Node*. */ -static Node* -tg_replaceNumberedParam(Node *expression, - int pnum, /* the number of the parameter */ - int rt_ind, /* the range table index */ - char *teeRelName) /* the relname of the tee table */ +static Node * +tg_replaceNumberedParam(Node * expression, + int pnum, /* the number of the parameter */ + int rt_ind, /* the range table index */ + char *teeRelName) /* the relname of the tee + * table */ { - TargetEntry *param_tle; - Param* p; - Var *newVar,*oldVar; - - if (expression == NULL) return NULL; - - switch (nodeTag(expression)) { - case T_Param: - { - /* the node is a parameter, - substitute the entry from the target list of the child that - corresponds to the parameter number*/ - p = (Param*)expression; - - /* we only deal with the case of numbered parameters */ - if (p->paramkind == PARAM_NUM && p->paramid == pnum) { - - if (p->param_tlist) { - /* we have a parameter with an attribute like $N.foo - so replace it with a new var node */ - - /* param tlist can only have one entry in them! */ - param_tle = (TargetEntry*)(lfirst(p->param_tlist)); - oldVar = (Var*)param_tle->expr; - oldVar->varno = rt_ind; - oldVar->varnoold = rt_ind; - return (Node*)oldVar; - } else { - /* we have $N without the .foo */ - bool defined; - bool isRel; - /* TODO here, we need to check to see whether the type of the - tee is a complex type (relation) or a simple type */ - /* if it is a simple type, then we need to get the "result" - attribute from the tee relation */ - - isRel = (typeid_get_relid(p->paramtype) != 0); - if (isRel) { - newVar = makeVar(rt_ind, - 0, /* the whole tuple */ - TypeGet(teeRelName,&defined), - rt_ind, - 0); - return (Node*)newVar; - } else - newVar = makeVar(rt_ind, - 1, /* just the first field, which is 'result' */ - TypeGet(teeRelName,&defined), - rt_ind, - 0); - return (Node*)newVar; - - } - } - else { - elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind); - } - } - break; - case T_Expr: - { - /* the node is an expression, we need to recursively - call ourselves until we find parameter nodes */ - List *l; - Expr *expr = (Expr*)expression; - List *newArgs; - - /* we have to make a new args lists because Params - can be replaced by Var nodes in tg_replaceNumberedParam()*/ - newArgs = NIL; - - /* we only care about argument to expressions, - it doesn't matter when the opType is */ - /* recursively rewrite the arguments of this expression */ - foreach (l, expr->args) { - newArgs = lappend(newArgs, - tg_replaceNumberedParam(lfirst(l), - pnum, - rt_ind, - teeRelName)); - } - /* change the arguments of the expression */ - expr->args = newArgs; - } - break; - default: - { - /* ignore other expr types */ - } - } - - return expression; + TargetEntry *param_tle; + Param *p; + Var *newVar, + *oldVar; + + if (expression == NULL) + return NULL; + + switch (nodeTag(expression)) + { + case T_Param: + { + + /* + * the node is a parameter, substitute the entry from the + * target list of the child that corresponds to the parameter + * number + */ + p = (Param *) expression; + + /* we only deal with the case of numbered parameters */ + if (p->paramkind == PARAM_NUM && p->paramid == pnum) + { + + if (p->param_tlist) + { + + /* + * we have a parameter with an attribute like $N.foo + * so replace it with a new var node + */ + + /* param tlist can only have one entry in them! */ + param_tle = (TargetEntry *) (lfirst(p->param_tlist)); + oldVar = (Var *) param_tle->expr; + oldVar->varno = rt_ind; + oldVar->varnoold = rt_ind; + return (Node *) oldVar; + } + else + { + /* we have $N without the .foo */ + bool defined; + bool isRel; + + /* + * TODO here, we need to check to see whether the type + * of the tee is a complex type (relation) or a simple + * type + */ + + /* + * if it is a simple type, then we need to get the + * "result" attribute from the tee relation + */ + + isRel = (typeid_get_relid(p->paramtype) != 0); + if (isRel) + { + newVar = makeVar(rt_ind, + 0, /* the whole tuple */ + TypeGet(teeRelName, &defined), + rt_ind, + 0); + return (Node *) newVar; + } + else + newVar = makeVar(rt_ind, + 1, /* just the first field, + * which is 'result' */ + TypeGet(teeRelName, &defined), + rt_ind, + 0); + return (Node *) newVar; + + } + } + else + { + elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind); + } + } + break; + case T_Expr: + { + + /* + * the node is an expression, we need to recursively call + * ourselves until we find parameter nodes + */ + List *l; + Expr *expr = (Expr *) expression; + List *newArgs; + + /* + * we have to make a new args lists because Params can be + * replaced by Var nodes in tg_replaceNumberedParam() + */ + newArgs = NIL; + + /* + * we only care about argument to expressions, it doesn't + * matter when the opType is + */ + /* recursively rewrite the arguments of this expression */ + foreach(l, expr->args) + { + newArgs = lappend(newArgs, + tg_replaceNumberedParam(lfirst(l), + pnum, + rt_ind, + teeRelName)); + } + /* change the arguments of the expression */ + expr->args = newArgs; + } + break; + default: + { + /* ignore other expr types */ + } + } + + return expression; } @@ -485,694 +564,817 @@ tg_replaceNumberedParam(Node *expression, /* tg_rewriteParamsInExpr: - rewrite the params in expressions by using the targetlist entries - from the input parsetrees + rewrite the params in expressions by using the targetlist entries + from the input parsetrees this procedure recursively calls itself it returns a (possibly modified) Node*. */ -static Node* -tg_rewriteParamsInExpr(Node *expression, QueryTreeList *inputQlist) +static Node * +tg_rewriteParamsInExpr(Node * expression, QueryTreeList * inputQlist) { - List *tl; - TargetEntry *param_tle, *tle; - Param* p; - int childno; - char *resname; - - if (expression == NULL) return NULL; - - switch (nodeTag(expression)) { - case T_Param: - { - /* the node is a parameter, - substitute the entry from the target list of the child that - corresponds to the parameter number*/ - p = (Param*)expression; - - /* we only deal with the case of numbered parameters */ - if (p->paramkind == PARAM_NUM) { - /* paramid's start from 1*/ - childno = p->paramid - 1; - - if (p->param_tlist) { - /* we have a parameter with an attribute like $N.foo - so match the resname "foo" against the target list - of the (N-1)th inputQlist */ - - /* param tlist can only have one entry in them! */ - param_tle = (TargetEntry*)(lfirst(p->param_tlist)); - resname = param_tle->resdom->resname; - - if (inputQlist->qtrees[childno]) { - foreach (tl, inputQlist->qtrees[childno]->targetList) { - tle = lfirst(tl); - if (strcmp(resname, tle->resdom->resname) == 0) { - return tle->expr; - } - } - } - else { - elog(WARN,"tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid); - } - - } else { - /* we have $N without the .foo */ - /* use the first resdom in the targetlist of the */ - /* appropriate child query */ - tl = inputQlist->qtrees[childno]->targetList; - tle = lfirst(tl); - return tle->expr; - } - } - else { - elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind); - } - } - break; - case T_Expr: - { - /* the node is an expression, we need to recursively - call ourselves until we find parameter nodes */ - List *l; - Expr *expr = (Expr*)expression; - List *newArgs; - - /* we have to make a new args lists because Params - can be replaced by Var nodes in tg_rewriteParamsInExpr()*/ - newArgs = NIL; - - /* we only care about argument to expressions, - it doesn't matter when the opType is */ - /* recursively rewrite the arguments of this expression */ - foreach (l, expr->args) { - newArgs = lappend(newArgs, - tg_rewriteParamsInExpr(lfirst(l), inputQlist)); - } - /* change the arguments of the expression */ - expr->args = newArgs; - } - break; - default: - { - /* ignore other expr types */ - } - } - - return expression; + List *tl; + TargetEntry *param_tle, + *tle; + Param *p; + int childno; + char *resname; + + if (expression == NULL) + return NULL; + + switch (nodeTag(expression)) + { + case T_Param: + { + + /* + * the node is a parameter, substitute the entry from the + * target list of the child that corresponds to the parameter + * number + */ + p = (Param *) expression; + + /* we only deal with the case of numbered parameters */ + if (p->paramkind == PARAM_NUM) + { + /* paramid's start from 1 */ + childno = p->paramid - 1; + + if (p->param_tlist) + { + + /* + * we have a parameter with an attribute like $N.foo + * so match the resname "foo" against the target list + * of the (N-1)th inputQlist + */ + + /* param tlist can only have one entry in them! */ + param_tle = (TargetEntry *) (lfirst(p->param_tlist)); + resname = param_tle->resdom->resname; + + if (inputQlist->qtrees[childno]) + { + foreach(tl, inputQlist->qtrees[childno]->targetList) + { + tle = lfirst(tl); + if (strcmp(resname, tle->resdom->resname) == 0) + { + return tle->expr; + } + } + } + else + { + elog(WARN, "tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid); + } + + } + else + { + /* we have $N without the .foo */ + /* use the first resdom in the targetlist of the */ + /* appropriate child query */ + tl = inputQlist->qtrees[childno]->targetList; + tle = lfirst(tl); + return tle->expr; + } + } + else + { + elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind); + } + } + break; + case T_Expr: + { + + /* + * the node is an expression, we need to recursively call + * ourselves until we find parameter nodes + */ + List *l; + Expr *expr = (Expr *) expression; + List *newArgs; + + /* + * we have to make a new args lists because Params can be + * replaced by Var nodes in tg_rewriteParamsInExpr() + */ + newArgs = NIL; + + /* + * we only care about argument to expressions, it doesn't + * matter when the opType is + */ + /* recursively rewrite the arguments of this expression */ + foreach(l, expr->args) + { + newArgs = lappend(newArgs, + tg_rewriteParamsInExpr(lfirst(l), inputQlist)); + } + /* change the arguments of the expression */ + expr->args = newArgs; + } + break; + default: + { + /* ignore other expr types */ + } + } + + return expression; } /* getParamTypes: - given an element, finds its parameter types. - the typev array argument is set to the parameter types. - the parameterCount is returned - - this code is very similar to ProcedureDefine() in pg_proc.c + given an element, finds its parameter types. + the typev array argument is set to the parameter types. + the parameterCount is returned + + this code is very similar to ProcedureDefine() in pg_proc.c */ static int -getParamTypes (TgElement *elem, Oid typev[]) +getParamTypes(TgElement * elem, Oid typev[]) { - /* this code is similar to ProcedureDefine() */ - int16 parameterCount; - bool defined; - Oid toid; - char *t; - int i,j; - - parameterCount = 0; - for (i=0;i<8;i++) { - typev[i] = 0; - } - for (j=0;j<elem->inTypes->num;j++) { - if (parameterCount == 8) { - elog(WARN, - "getParamTypes: Ingredients cannot take > 8 arguments"); + /* this code is similar to ProcedureDefine() */ + int16 parameterCount; + bool defined; + Oid toid; + char *t; + int i, + j; + + parameterCount = 0; + for (i = 0; i < 8; i++) + { + typev[i] = 0; } - t = elem->inTypes->val[j]; - if (strcmp(t,"opaque") == 0) { - elog(WARN, - "getParamTypes: Ingredient functions cannot take type 'opaque'"); - } else { - toid = TypeGet(elem->inTypes->val[j], &defined); - if (!OidIsValid(toid)) { - elog(WARN, "getParamTypes: arg type '%s' is not defined",t); - } - if (!defined) { - elog(NOTICE, "getParamTypes: arg type '%s' is only a shell",t); - } + for (j = 0; j < elem->inTypes->num; j++) + { + if (parameterCount == 8) + { + elog(WARN, + "getParamTypes: Ingredients cannot take > 8 arguments"); + } + t = elem->inTypes->val[j]; + if (strcmp(t, "opaque") == 0) + { + elog(WARN, + "getParamTypes: Ingredient functions cannot take type 'opaque'"); + } + else + { + toid = TypeGet(elem->inTypes->val[j], &defined); + if (!OidIsValid(toid)) + { + elog(WARN, "getParamTypes: arg type '%s' is not defined", t); + } + if (!defined) + { + elog(NOTICE, "getParamTypes: arg type '%s' is only a shell", t); + } + } + typev[parameterCount++] = toid; } - typev[parameterCount++] = toid; - } - return parameterCount; + return parameterCount; } /* * tg_parseTeeNode - * - * handles the parsing of the tee node - * + * + * handles the parsing of the tee node + * * */ -static QueryTreeList* -tg_parseTeeNode(TgRecipe *r, - TgNode *n, /* the tee node */ - int i, /* which input this node is to its parent */ - QueryTreeList *qList, - TeeInfo* teeInfo) +static QueryTreeList * +tg_parseTeeNode(TgRecipe * r, + TgNode * n, /* the tee node */ + int i, /* which input this node is to its parent */ + QueryTreeList * qList, + TeeInfo * teeInfo) { - QueryTreeList *q; - char* tt; - int rt_ind; - Query* orig; - - /* the input Node is a tee node, so we need to do the following: - * we need to parse the child of the tee node, - we add that to our query tree list - * we need the name of the tee node table - the tee node table is the table into which the tee node - may materialize results. Call it TT - * we add a range table to our existing query with TT in it - * we need to replace the parameter $i with TT - (otherwise the optimizer won't know to use the table - on expression containining $i) - After that rewrite, the optimizer will generate - sequential scans of TT - - Later, in the glue phase, we replace all instances of TT - sequential scans with the actual Tee node - */ - q = tg_parseSubQuery(r,n, teeInfo); - - /* tt is the name of the tee node table */ - tt = n->nodeName; - - if (q) - appendTeeQuery(teeInfo,q,tt); - - orig = qList->qtrees[0]; - rt_ind = RangeTablePosn(orig->rtable,tt); - /* check to see that this table is not part of - the range table already. This usually only - happens if multiple inputs are connected to the - same Tee. */ - if (rt_ind == 0) { - orig->rtable = lappend(orig->rtable, - addRangeTableEntry(NULL, - tt, - tt, - FALSE, - FALSE, - NULL)); - rt_ind = length(orig->rtable); - } - - orig->qual = tg_replaceNumberedParam(orig->qual, - i+1, /* params start at 1*/ - rt_ind, - tt); - return qList; + QueryTreeList *q; + char *tt; + int rt_ind; + Query *orig; + + /* + * the input Node is a tee node, so we need to do the following: we + * need to parse the child of the tee node, we add that to our query + * tree list we need the name of the tee node table the tee node table + * is the table into which the tee node may materialize results. Call + * it TT we add a range table to our existing query with TT in it we + * need to replace the parameter $i with TT (otherwise the optimizer + * won't know to use the table on expression containining $i) After + * that rewrite, the optimizer will generate sequential scans of TT + * + * Later, in the glue phase, we replace all instances of TT sequential + * scans with the actual Tee node + */ + q = tg_parseSubQuery(r, n, teeInfo); + + /* tt is the name of the tee node table */ + tt = n->nodeName; + + if (q) + appendTeeQuery(teeInfo, q, tt); + + orig = qList->qtrees[0]; + rt_ind = RangeTablePosn(orig->rtable, tt); + + /* + * check to see that this table is not part of the range table + * already. This usually only happens if multiple inputs are + * connected to the same Tee. + */ + if (rt_ind == 0) + { + orig->rtable = lappend(orig->rtable, + addRangeTableEntry(NULL, + tt, + tt, + FALSE, + FALSE, + NULL)); + rt_ind = length(orig->rtable); + } + + orig->qual = tg_replaceNumberedParam(orig->qual, + i + 1, /* params start at 1 */ + rt_ind, + tt); + return qList; } /* * tg_parseSubQuery: - * go backwards from a node and parse the query + * go backwards from a node and parse the query * - * the result parse tree is passed back - * - * could return NULL if trying to parse a teeNode + * the result parse tree is passed back + * + * could return NULL if trying to parse a teeNode * that's already been processed by another parent - * + * */ -static QueryTreeList* -tg_parseSubQuery(TgRecipe* r, TgNode* n, TeeInfo* teeInfo) +static QueryTreeList * +tg_parseSubQuery(TgRecipe * r, TgNode * n, TeeInfo * teeInfo) { - TgElement *elem; - char* funcName; - Oid typev[8]; /* eight arguments maximum */ - int i; - int parameterCount; - - QueryTreeList *qList; /* the parse tree of the nodeElement */ - QueryTreeList *inputQlist; /* the list of parse trees for the - inputs to this node */ - QueryTreeList *q; - Oid relid; - TgNode* child; - Relation rel; - unsigned int len; - TupleDesc tupdesc; - - qList = NULL; - - if (n->nodeType == TG_INGRED_NODE) { - /* parse each ingredient node in turn */ - - elem = n->nodeElem; - switch (elem->srcLang) { - case TG_SQL: - { - /* for SQL ingredients, the SQL query is contained in the - 'src' field */ + TgElement *elem; + char *funcName; + Oid typev[8]; /* eight arguments maximum */ + int i; + int parameterCount; + + QueryTreeList *qList; /* the parse tree of the nodeElement */ + QueryTreeList *inputQlist; /* the list of parse trees for the inputs + * to this node */ + QueryTreeList *q; + Oid relid; + TgNode *child; + Relation rel; + unsigned int len; + TupleDesc tupdesc; + + qList = NULL; + + if (n->nodeType == TG_INGRED_NODE) + { + /* parse each ingredient node in turn */ + + elem = n->nodeElem; + switch (elem->srcLang) + { + case TG_SQL: + { + + /* + * for SQL ingredients, the SQL query is contained in the + * 'src' field + */ #ifdef DEBUG_RECIPE -elog(NOTICE,"calling parser with %s",elem->src); -#endif /* DEBUG_RECIPE */ + elog(NOTICE, "calling parser with %s", elem->src); +#endif /* DEBUG_RECIPE */ - parameterCount = getParamTypes(elem,typev); + parameterCount = getParamTypes(elem, typev); - qList = parser(elem->src,typev,parameterCount); + qList = parser(elem->src, typev, parameterCount); - if (qList->len > 1) { - elog(NOTICE, - "tg_parseSubQuery: parser produced > 1 query tree"); - } - } - break; - case TG_C: - { - /* C ingredients are registered functions in postgres */ - /* we create a new query string by using the function name - (found in the 'src' field) and adding parameters to it - so if the function was FOOBAR and took in two arguments, - we would create a string - select FOOBAR($1,$2) - */ - char newquery[1000]; - - funcName = elem->src; - parameterCount = getParamTypes(elem,typev); - - if (parameterCount > 0) { - int i; - sprintf(newquery,"select %s($1",funcName); - for (i=1;i<parameterCount;i++) { - sprintf(newquery,"%s,$%d",newquery,i); - } - sprintf(newquery,"%s)",newquery); - } else - sprintf(newquery,"select %s()",funcName); + if (qList->len > 1) + { + elog(NOTICE, + "tg_parseSubQuery: parser produced > 1 query tree"); + } + } + break; + case TG_C: + { + /* C ingredients are registered functions in postgres */ + + /* + * we create a new query string by using the function name + * (found in the 'src' field) and adding parameters to it + * so if the function was FOOBAR and took in two + * arguments, we would create a string select + * FOOBAR($1,$2) + */ + char newquery[1000]; + + funcName = elem->src; + parameterCount = getParamTypes(elem, typev); + + if (parameterCount > 0) + { + int i; + + sprintf(newquery, "select %s($1", funcName); + for (i = 1; i < parameterCount; i++) + { + sprintf(newquery, "%s,$%d", newquery, i); + } + sprintf(newquery, "%s)", newquery); + } + else + sprintf(newquery, "select %s()", funcName); #ifdef DEBUG_RECIPE -elog(NOTICE,"calling parser with %s", newquery); -#endif /* DEBUG_RECIPE */ + elog(NOTICE, "calling parser with %s", newquery); +#endif /* DEBUG_RECIPE */ + + qList = parser(newquery, typev, parameterCount); + if (qList->len > 1) + { + elog(NOTICE, + "tg_parseSubQuery: parser produced > 1 query tree"); + } + } + break; + case TG_RECIPE_GRAPH: + elog(NOTICE, "tg_parseSubQuery: can't parse recipe graph ingredients yet!"); + break; + case TG_COMPILED: + elog(NOTICE, "tg_parseSubQuery: can't parse compiled ingredients yet!"); + break; + default: + elog(NOTICE, "tg_parseSubQuery: unknown srcLang: %d", elem->srcLang); + } + + /* parse each of the subrecipes that are input to this node */ + + if (n->inNodes->num > 0) + { + inputQlist = malloc(sizeof(QueryTreeList)); + inputQlist->len = n->inNodes->num + 1; + inputQlist->qtrees = (Query **) malloc(inputQlist->len * sizeof(Query *)); + for (i = 0; i < n->inNodes->num; i++) + { + + inputQlist->qtrees[i] = NULL; + if (n->inNodes->val[i]) + { + if (n->inNodes->val[i]->nodeType == TG_TEE_NODE) + { + qList = tg_parseTeeNode(r, n->inNodes->val[i], + i, qList, teeInfo); + } + else + { /* input node is not a Tee */ + q = tg_parseSubQuery(r, n->inNodes->val[i], + teeInfo); + Assert(q->len == 1); + inputQlist->qtrees[i] = q->qtrees[0]; + } + } + } - qList = parser(newquery,typev,parameterCount); - if (qList->len > 1) { - elog(NOTICE, - "tg_parseSubQuery: parser produced > 1 query tree"); + /* now, we have all the query trees from our input nodes */ + /* transform the original parse tree appropriately */ + tg_rewriteQuery(r, n, qList, inputQlist); } - } - break; - case TG_RECIPE_GRAPH: - elog(NOTICE,"tg_parseSubQuery: can't parse recipe graph ingredients yet!"); - break; - case TG_COMPILED: - elog(NOTICE,"tg_parseSubQuery: can't parse compiled ingredients yet!"); - break; - default: - elog(NOTICE,"tg_parseSubQuery: unknown srcLang: %d",elem->srcLang); } + else if (n->nodeType == TG_EYE_NODE) + { - /* parse each of the subrecipes that are input to this node*/ - - if (n->inNodes->num > 0) { - inputQlist = malloc(sizeof(QueryTreeList)); - inputQlist->len = n->inNodes->num + 1 ; - inputQlist->qtrees = (Query**)malloc(inputQlist->len * sizeof(Query*)); - for (i=0;i<n->inNodes->num;i++) { - - inputQlist->qtrees[i] = NULL; - if (n->inNodes->val[i]) { - if (n->inNodes->val[i]->nodeType == TG_TEE_NODE) { - qList = tg_parseTeeNode(r,n->inNodes->val[i], - i,qList,teeInfo); - } - else - { /* input node is not a Tee */ - q = tg_parseSubQuery(r,n->inNodes->val[i], - teeInfo); - Assert (q->len == 1); - inputQlist->qtrees[i] = q->qtrees[0]; - } + /* + * if we hit an eye, we need to stop and make what we have into a + * subrecipe query block + */ + elog(NOTICE, "tg_parseSubQuery: can't handle eye nodes yet"); + } + else if (n->nodeType == TG_TEE_NODE) + { + + /* + * if we hit a tee, check to see if the parsing has been done for + * this tee already by the other parent + */ + + rel = RelationNameGetRelation(n->nodeName); + if (RelationIsValid(rel)) + { + + /* + * this tee has already been visited, no need to do any + * further processing + */ + return NULL; } - } + else + { + /* we need to process the child of the tee first, */ + child = n->inNodes->val[0]; + + if (child->nodeType == TG_TEE_NODE) + { + /* nested Tee nodes */ + qList = tg_parseTeeNode(r, child, 0, qList, teeInfo); + return qList; + } - /* now, we have all the query trees from our input nodes */ - /* transform the original parse tree appropriately */ - tg_rewriteQuery(r,n,qList,inputQlist); + Assert(child != NULL); + + /* parse the input node */ + q = tg_parseSubQuery(r, child, teeInfo); + Assert(q->len == 1); + + /* add the parsed query to the main list of queries */ + qList = appendQlist(qList, q); + + /* need to create the tee table here */ + + /* + * the tee table created is used both for materializing the + * values at the tee node, and for parsing and optimization. + * The optimization needs to have a real table before it will + * consider scans on it + */ + + /* + * first, find the type of the tuples being produced by the + * tee. The type is the same as the output type of the child + * node. + * + * NOTE: we are assuming that the child node only has a single + * output here! + */ + getParamTypes(child->nodeElem, typev); + + /* + * the output type is either a complex type, (and is thus a + * relation) or is a simple type + */ + + rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]); + + if (RelationIsValid(rel)) + { + + /* + * for complex types, create new relation with the same + * tuple descriptor as the output table type + */ + len = length(q->qtrees[0]->targetList); + tupdesc = rel->rd_att; + + relid = heap_create(child->nodeElem->outTypes->val[0], + NULL, /* XXX */ + 'n', + DEFAULT_SMGR, + tupdesc); + } + else + { + + /* + * we have to create a relation with one attribute of the + * simple base type. That attribute will have an attr + * name of "result" + */ + /* NOTE: ignore array types for the time being */ + + len = 1; + tupdesc = CreateTemplateTupleDesc(len); + + if (!TupleDescInitEntry(tupdesc, 1, + "result", + NULL, + 0, false)) + { + elog(NOTICE, "tg_parseSubQuery: unexpected result from TupleDescInitEntry"); + } + else + { + relid = heap_create(child->nodeElem->outTypes->val[0], + NULL, /* XXX */ + 'n', + DEFAULT_SMGR, + tupdesc); + } + } + } } - } - else if (n->nodeType == TG_EYE_NODE) { - /* if we hit an eye, we need to stop and make what we have - into a subrecipe query block*/ - elog(NOTICE,"tg_parseSubQuery: can't handle eye nodes yet"); - } - else if (n->nodeType == TG_TEE_NODE) { - /* if we hit a tee, check to see if the parsing has been done - for this tee already by the other parent */ - - rel = RelationNameGetRelation(n->nodeName); - if (RelationIsValid(rel)) { - /* this tee has already been visited, - no need to do any further processing */ - return NULL; - } else { - /* we need to process the child of the tee first, */ - child = n->inNodes->val[0]; - - if (child->nodeType == TG_TEE_NODE) { - /* nested Tee nodes */ - qList = tg_parseTeeNode(r,child,0,qList,teeInfo); - return qList; - } - - Assert (child != NULL); - - /* parse the input node */ - q = tg_parseSubQuery(r,child, teeInfo); - Assert (q->len == 1); - - /* add the parsed query to the main list of queries */ - qList = appendQlist(qList,q); - - /* need to create the tee table here */ - /* the tee table created is used both for materializing the values - at the tee node, and for parsing and optimization. - The optimization needs to have a real table before it will - consider scans on it */ - - /* first, find the type of the tuples being produced by the - tee. The type is the same as the output type of - the child node. - - NOTE: we are assuming that the child node only has a single - output here! */ - getParamTypes(child->nodeElem,typev); - - /* the output type is either a complex type, - (and is thus a relation) or is a simple type */ - - rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]); - - if (RelationIsValid(rel)) { - /* for complex types, create new relation with the same - tuple descriptor as the output table type*/ - len = length(q->qtrees[0]->targetList); - tupdesc = rel->rd_att; - - relid = heap_create(child->nodeElem->outTypes->val[0], - NULL, /* XXX */ - 'n', - DEFAULT_SMGR, - tupdesc); - } - else { - /* we have to create a relation with one attribute of - the simple base type. That attribute will have - an attr name of "result" */ - /*NOTE: ignore array types for the time being */ - - len = 1; - tupdesc = CreateTemplateTupleDesc(len); - - if ( !TupleDescInitEntry(tupdesc,1, - "result", - NULL, - 0, false)) { - elog(NOTICE,"tg_parseSubQuery: unexpected result from TupleDescInitEntry"); - } else { - relid = heap_create(child->nodeElem->outTypes->val[0], - NULL, /* XXX */ - 'n', - DEFAULT_SMGR, - tupdesc); - } - } + else if (n->nodeType == TG_RECIPE_NODE) + { + elog(NOTICE, "tg_parseSubQuery: can't handle embedded recipes yet!"); } - } - else if (n->nodeType == TG_RECIPE_NODE) { - elog(NOTICE,"tg_parseSubQuery: can't handle embedded recipes yet!"); - } else - elog (NOTICE, "unknown nodeType: %d", n->nodeType); + else + elog(NOTICE, "unknown nodeType: %d", n->nodeType); - return qList; + return qList; } /* * OffsetVarAttno - - * recursively find all the var nodes with the specified varno + * recursively find all the var nodes with the specified varno * and offset their varattno with the offset - * - * code is similar to OffsetVarNodes in rewriteManip.c + * + * code is similar to OffsetVarNodes in rewriteManip.c */ void -OffsetVarAttno(Node* node, int varno, int offset) +OffsetVarAttno(Node * node, int varno, int offset) { - if (node == NULL) return; - switch (nodeTag(node)) { - case T_TargetEntry: - { - TargetEntry *tle = (TargetEntry *)node; - OffsetVarAttno(tle->expr, varno, offset); - } - break; - case T_Expr: - { - Expr *expr = (Expr*)node; - OffsetVarAttno((Node*)expr->args, varno, offset); - } - break; - case T_Var: + if (node == NULL) + return; + switch (nodeTag(node)) { - Var *var = (Var*)node; - if (var->varno == varno) - var->varattno += offset; - } - break; - case T_List: - { - List *l; + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *) node; + + OffsetVarAttno(tle->expr, varno, offset); + } + break; + case T_Expr: + { + Expr *expr = (Expr *) node; + + OffsetVarAttno((Node *) expr->args, varno, offset); + } + break; + case T_Var: + { + Var *var = (Var *) node; - foreach(l, (List*)node) { - OffsetVarAttno(lfirst(l), varno, offset); - } + if (var->varno == varno) + var->varattno += offset; + } + break; + case T_List: + { + List *l; + + foreach(l, (List *) node) + { + OffsetVarAttno(lfirst(l), varno, offset); + } + } + break; + default: + /* ignore the others */ + break; } - break; - default: - /* ignore the others */ - break; - } } /* - * appendQlist - * add the contents of a QueryTreeList q2 to the end of the QueryTreeList - * q1 + * appendQlist + * add the contents of a QueryTreeList q2 to the end of the QueryTreeList + * q1 * - * returns a new querytree list + * returns a new querytree list */ -QueryTreeList* -appendQlist(QueryTreeList *q1, QueryTreeList *q2) +QueryTreeList * +appendQlist(QueryTreeList * q1, QueryTreeList * q2) { - QueryTreeList* newq; - int i,j; - int newlen; - - if (q1 == NULL) - return q2; - - if (q2 == NULL) - return q1; - - newlen = q1->len + q2->len; - newq = (QueryTreeList*)malloc(sizeof(QueryTreeList)); - newq->len = newlen; - newq->qtrees = (Query**)malloc(newlen * sizeof(Query*)); - for (i=0;i<q1->len;i++) - newq->qtrees[i] = q1->qtrees[i]; - for (j=0;j<q2->len;j++) { - newq->qtrees[i + j] = q2->qtrees[j]; - } - return newq; + QueryTreeList *newq; + int i, + j; + int newlen; + + if (q1 == NULL) + return q2; + + if (q2 == NULL) + return q1; + + newlen = q1->len + q2->len; + newq = (QueryTreeList *) malloc(sizeof(QueryTreeList)); + newq->len = newlen; + newq->qtrees = (Query **) malloc(newlen * sizeof(Query *)); + for (i = 0; i < q1->len; i++) + newq->qtrees[i] = q1->qtrees[i]; + for (j = 0; j < q2->len; j++) + { + newq->qtrees[i + j] = q2->qtrees[j]; + } + return newq; } /* - * appendTeeQuery - * - * modify the query field of the teeInfo list of the particular tee node + * appendTeeQuery + * + * modify the query field of the teeInfo list of the particular tee node */ static void -appendTeeQuery(TeeInfo *teeInfo, QueryTreeList *q, char* teeNodeName) +appendTeeQuery(TeeInfo * teeInfo, QueryTreeList * q, char *teeNodeName) { - int i; - - Assert(teeInfo); + int i; - for (i=0;i<teeInfo->num;i++) { - if ( strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0) { + Assert(teeInfo); + + for (i = 0; i < teeInfo->num; i++) + { + if (strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0) + { - Assert(q->len == 1); - teeInfo->val[i].tpi_parsetree = q->qtrees[0]; - return; + Assert(q->len == 1); + teeInfo->val[i].tpi_parsetree = q->qtrees[0]; + return; + } } - } - elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo"); + elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo"); } /* - * replaceSeqScan - * replaces sequential scans of a specified relation with the tee plan - * the relation is specified by its index in the range table, rt_ind + * replaceSeqScan + * replaces sequential scans of a specified relation with the tee plan + * the relation is specified by its index in the range table, rt_ind * * returns the modified plan * the offset_attno is the offset that needs to be added to the parent's * qual or targetlist because the child plan has been replaced with a tee node */ static void -replaceSeqScan(Plan* plan, Plan* parent, - int rt_ind, Plan* tplan) +replaceSeqScan(Plan * plan, Plan * parent, + int rt_ind, Plan * tplan) { - Scan* snode; - Tee* teePlan; - Result* newPlan; - - if (plan == NULL) { - return; - } - - if (plan->type == T_SeqScan) { - snode = (Scan*)plan; - if (snode->scanrelid == rt_ind) { - /* found the sequential scan that should be replaced - with the tplan. */ - /* we replace the plan, but we also need to modify its parent*/ - - /* replace the sequential scan with a Result node - the reason we use a result node is so that we get the proper - projection behavior. The Result node is simply (ab)used as - a projection node */ - - newPlan = makeNode(Result); - newPlan->plan.cost = 0.0; - newPlan->plan.state = (EState*)NULL; - newPlan->plan.targetlist = plan->targetlist; - newPlan->plan.lefttree = tplan; - newPlan->plan.righttree = NULL; - newPlan->resconstantqual = NULL; - newPlan->resstate = NULL; - - /* change all the varno's to 1*/ - ChangeVarNodes((Node*)newPlan->plan.targetlist, - snode->scanrelid, 1); - - if (parent) { - teePlan = (Tee*)tplan; - - if (parent->lefttree == plan) - parent->lefttree = (Plan*)newPlan; - else - parent->righttree = (Plan*)newPlan; - + Scan *snode; + Tee *teePlan; + Result *newPlan; - if (teePlan->leftParent == NULL) - teePlan->leftParent = (Plan*)newPlan; - else - teePlan->rightParent = (Plan*)newPlan; + if (plan == NULL) + { + return; + } + + if (plan->type == T_SeqScan) + { + snode = (Scan *) plan; + if (snode->scanrelid == rt_ind) + { + + /* + * found the sequential scan that should be replaced with the + * tplan. + */ + /* we replace the plan, but we also need to modify its parent */ + + /* + * replace the sequential scan with a Result node the reason + * we use a result node is so that we get the proper + * projection behavior. The Result node is simply (ab)used as + * a projection node + */ + + newPlan = makeNode(Result); + newPlan->plan.cost = 0.0; + newPlan->plan.state = (EState *) NULL; + newPlan->plan.targetlist = plan->targetlist; + newPlan->plan.lefttree = tplan; + newPlan->plan.righttree = NULL; + newPlan->resconstantqual = NULL; + newPlan->resstate = NULL; + + /* change all the varno's to 1 */ + ChangeVarNodes((Node *) newPlan->plan.targetlist, + snode->scanrelid, 1); + + if (parent) + { + teePlan = (Tee *) tplan; + + if (parent->lefttree == plan) + parent->lefttree = (Plan *) newPlan; + else + parent->righttree = (Plan *) newPlan; + + + if (teePlan->leftParent == NULL) + teePlan->leftParent = (Plan *) newPlan; + else + teePlan->rightParent = (Plan *) newPlan; /* comment for now to test out executor-stuff - if (parent->state) { - ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan); - } + if (parent->state) { + ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan); + } */ - } - } + } + } - } else { - if (plan->lefttree) { - replaceSeqScan(plan->lefttree, plan, rt_ind, tplan); } - if (plan->righttree) { - replaceSeqScan(plan->righttree, plan, rt_ind, tplan); + else + { + if (plan->lefttree) + { + replaceSeqScan(plan->lefttree, plan, rt_ind, tplan); + } + if (plan->righttree) + { + replaceSeqScan(plan->righttree, plan, rt_ind, tplan); + } } - } } /* - * replaceTeeScans - * places the sequential scans of the Tee table with + * replaceTeeScans + * places the sequential scans of the Tee table with * a connection to the actual tee plan node */ -static Plan* -replaceTeeScans(Plan* plan, Query* parsetree, TeeInfo *teeInfo) +static Plan * +replaceTeeScans(Plan * plan, Query * parsetree, TeeInfo * teeInfo) { - int i; - List* rtable; - RangeTblEntry *rte; - char prefix[5]; - int rt_ind; - Plan* tplan; - - rtable = parsetree->rtable; - if (rtable == NULL) - return plan; + int i; + List *rtable; + RangeTblEntry *rte; + char prefix[5]; + int rt_ind; + Plan *tplan; + + rtable = parsetree->rtable; + if (rtable == NULL) + return plan; + + /* + * look through the range table for the tee relation entry, that will + * give use the varno we need to detect which sequential scans need to + * be replaced with tee nodes + */ + + rt_ind = 0; + while (rtable != NIL) + { + rte = lfirst(rtable); + rtable = lnext(rtable); + rt_ind++; /* range table references in varno fields + * start w/ 1 */ + + /* + * look for the "tee_" prefix in the refname, also check to see + * that the relname and the refname are the same this should + * eliminate any user-specified table and leave us with the tee + * table entries only + */ + if ((strlen(rte->refname) < 4) || + (strcmp(rte->relname, rte->refname) != 0)) + continue; + strNcpy(prefix, rte->refname, 4); + if (strcmp(prefix, "tee_") == 0) + { + /* okay, we found a tee node entry in the range table */ + + /* find the appropriate plan in the teeInfo list */ + tplan = NULL; + for (i = 0; i < teeInfo->num; i++) + { + if (strcmp(teeInfo->val[i].tpi_relName, + rte->refname) == 0) + { + tplan = teeInfo->val[i].tpi_plan; + } + } + if (tplan == NULL) + { + elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan"); + } - /* look through the range table for the tee relation entry, - that will give use the varno we need to detect which - sequential scans need to be replaced with tee nodes*/ - - rt_ind = 0; - while (rtable != NIL) { - rte = lfirst(rtable); - rtable = lnext(rtable); - rt_ind++; /* range table references in varno fields start w/ 1 */ - - /* look for the "tee_" prefix in the refname, - also check to see that the relname and the refname are the same - this should eliminate any user-specified table and leave - us with the tee table entries only*/ - if ((strlen(rte->refname) < 4) || - (strcmp (rte->relname, rte->refname) != 0)) - continue; - strNcpy(prefix,rte->refname,4); - if (strcmp(prefix,"tee_") == 0) { - /* okay, we found a tee node entry in the range table */ - - /* find the appropriate plan in the teeInfo list */ - tplan = NULL; - for (i=0;i<teeInfo->num;i++) { - if (strcmp(teeInfo->val[i].tpi_relName, - rte->refname) == 0) { - tplan = teeInfo->val[i].tpi_plan; + /* + * replace the sequential scan node with that var number with + * the tee plan node + */ + replaceSeqScan(plan, NULL, rt_ind, tplan); } - } - if (tplan == NULL) { - elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan"); } - - /* replace the sequential scan node with that var number - with the tee plan node */ - replaceSeqScan(plan, NULL, rt_ind, tplan); } - } - return plan; + return plan; } -#endif /* TIOGA */ +#endif /* TIOGA */ diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c index 7f0198a10b7..cafe4d09710 100644 --- a/src/backend/commands/remove.c +++ b/src/backend/commands/remove.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * remove.c-- - * POSTGRES remove (function | type | operator ) utilty code. + * POSTGRES remove (function | type | operator ) utilty code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.10 1997/08/18 20:52:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.11 1997/09/07 04:40:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,100 +28,112 @@ #include <storage/bufmgr.h> #include <fmgr.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* * RemoveOperator -- - * Deletes an operator. + * Deletes an operator. * * Exceptions: - * BadArg if name is invalid. - * BadArg if type1 is invalid. - * "WARN" if operator nonexistent. - * ... + * BadArg if name is invalid. + * BadArg if type1 is invalid. + * "WARN" if operator nonexistent. + * ... */ void -RemoveOperator(char *operatorName, /* operator name */ - char *typeName1, /* first type name */ - char *typeName2) /* optional second type name */ +RemoveOperator(char *operatorName, /* operator name */ + char *typeName1, /* first type name */ + char *typeName2) /* optional second type name */ { - Relation relation; - HeapScanDesc scan; - HeapTuple tup; - Oid typeId1 = InvalidOid; - Oid typeId2 = InvalidOid; - bool defined; - ItemPointerData itemPointerData; - Buffer buffer; - ScanKeyData operatorKey[3]; - char *userName; - - if (typeName1) { - typeId1 = TypeGet(typeName1, &defined); - if (!OidIsValid(typeId1)) { - elog(WARN, "RemoveOperator: type '%s' does not exist", typeName1); - return; + Relation relation; + HeapScanDesc scan; + HeapTuple tup; + Oid typeId1 = InvalidOid; + Oid typeId2 = InvalidOid; + bool defined; + ItemPointerData itemPointerData; + Buffer buffer; + ScanKeyData operatorKey[3]; + char *userName; + + if (typeName1) + { + typeId1 = TypeGet(typeName1, &defined); + if (!OidIsValid(typeId1)) + { + elog(WARN, "RemoveOperator: type '%s' does not exist", typeName1); + return; + } } - } - - if (typeName2) { - typeId2 = TypeGet(typeName2, &defined); - if (!OidIsValid(typeId2)) { - elog(WARN, "RemoveOperator: type '%s' does not exist", typeName2); - return; + + if (typeName2) + { + typeId2 = TypeGet(typeName2, &defined); + if (!OidIsValid(typeId2)) + { + elog(WARN, "RemoveOperator: type '%s' does not exist", typeName2); + return; + } } - } - - ScanKeyEntryInitialize(&operatorKey[0], 0x0, - Anum_pg_operator_oprname, - NameEqualRegProcedure, - PointerGetDatum(operatorName)); - - ScanKeyEntryInitialize(&operatorKey[1], 0x0, - Anum_pg_operator_oprleft, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(typeId1)); - - ScanKeyEntryInitialize(&operatorKey[2], 0x0, - Anum_pg_operator_oprright, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(typeId2)); - - relation = heap_openr(OperatorRelationName); - scan = heap_beginscan(relation, 0, NowTimeQual, 3, operatorKey); - tup = heap_getnext(scan, 0, &buffer); - if (HeapTupleIsValid(tup)) { + + ScanKeyEntryInitialize(&operatorKey[0], 0x0, + Anum_pg_operator_oprname, + NameEqualRegProcedure, + PointerGetDatum(operatorName)); + + ScanKeyEntryInitialize(&operatorKey[1], 0x0, + Anum_pg_operator_oprleft, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(typeId1)); + + ScanKeyEntryInitialize(&operatorKey[2], 0x0, + Anum_pg_operator_oprright, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(typeId2)); + + relation = heap_openr(OperatorRelationName); + scan = heap_beginscan(relation, 0, NowTimeQual, 3, operatorKey); + tup = heap_getnext(scan, 0, &buffer); + if (HeapTupleIsValid(tup)) + { #ifndef NO_SECURITY - userName = GetPgUserName(); - if (!pg_ownercheck(userName, - (char *) ObjectIdGetDatum(tup->t_oid), - OPROID)) - elog(WARN, "RemoveOperator: operator '%s': permission denied", - operatorName); + userName = GetPgUserName(); + if (!pg_ownercheck(userName, + (char *) ObjectIdGetDatum(tup->t_oid), + OPROID)) + elog(WARN, "RemoveOperator: operator '%s': permission denied", + operatorName); #endif - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - heap_delete(relation, &itemPointerData); - } else { - if (OidIsValid(typeId1) && OidIsValid(typeId2)) { - elog(WARN, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist", - operatorName, - typeName1, - typeName2); - } else if (OidIsValid(typeId1)) { - elog(WARN, "RemoveOperator: right unary operator '%s' taking '%s' does not exist", - operatorName, - typeName1); - } else { - elog(WARN, "RemoveOperator: left unary operator '%s' taking '%s' does not exist", - operatorName, - typeName2); + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); } - } - heap_endscan(scan); - heap_close(relation); + else + { + if (OidIsValid(typeId1) && OidIsValid(typeId2)) + { + elog(WARN, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist", + operatorName, + typeName1, + typeName2); + } + else if (OidIsValid(typeId1)) + { + elog(WARN, "RemoveOperator: right unary operator '%s' taking '%s' does not exist", + operatorName, + typeName1); + } + else + { + elog(WARN, "RemoveOperator: left unary operator '%s' taking '%s' does not exist", + operatorName, + typeName2); + } + } + heap_endscan(scan); + heap_close(relation); } #ifdef NOTYET @@ -130,353 +142,379 @@ RemoveOperator(char *operatorName, /* operator name */ * don't use it - pma 2/1/94 */ /* - * SingleOpOperatorRemove - * Removes all operators that have operands or a result of type 'typeOid'. + * SingleOpOperatorRemove + * Removes all operators that have operands or a result of type 'typeOid'. */ static void SingleOpOperatorRemove(Oid typeOid) { - Relation rdesc; - ScanKeyData key[3]; - HeapScanDesc sdesc; - HeapTuple tup; - ItemPointerData itemPointerData; - Buffer buffer; - static attnums[3] = { 7, 8, 9 }; /* left, right, return */ - register i; - - ScanKeyEntryInitialize(&key[0], - 0, 0, ObjectIdEqualRegProcedure, (Datum)typeOid); - rdesc = heap_openr(OperatorRelationName); - for (i = 0; i < 3; ++i) { - key[0].sk_attno = attnums[i]; - sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key); - while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) { - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - /* XXX LOCK not being passed */ - heap_delete(rdesc, &itemPointerData); + Relation rdesc; + ScanKeyData key[3]; + HeapScanDesc sdesc; + HeapTuple tup; + ItemPointerData itemPointerData; + Buffer buffer; + static attnums[3] = {7, 8, 9}; /* left, right, return */ + register i; + + ScanKeyEntryInitialize(&key[0], + 0, 0, ObjectIdEqualRegProcedure, (Datum) typeOid); + rdesc = heap_openr(OperatorRelationName); + for (i = 0; i < 3; ++i) + { + key[0].sk_attno = attnums[i]; + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key); + while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) + { + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + /* XXX LOCK not being passed */ + heap_delete(rdesc, &itemPointerData); + } + heap_endscan(sdesc); } - heap_endscan(sdesc); - } - heap_close(rdesc); + heap_close(rdesc); } /* - * AttributeAndRelationRemove - * Removes all entries in the attribute and relation relations - * that contain entries of type 'typeOid'. - * Currently nothing calls this code, it is untested. + * AttributeAndRelationRemove + * Removes all entries in the attribute and relation relations + * that contain entries of type 'typeOid'. + * Currently nothing calls this code, it is untested. */ static void AttributeAndRelationRemove(Oid typeOid) { - struct oidlist { - Oid reloid; - struct oidlist *next; - }; - struct oidlist *oidptr, *optr; - Relation rdesc; - ScanKeyData key[1]; - HeapScanDesc sdesc; - HeapTuple tup; - ItemPointerData itemPointerData; - Buffer buffer; - - /* - * Get the oid's of the relations to be removed by scanning the - * entire attribute relation. - * We don't need to remove the attributes here, - * because amdestroy will remove all attributes of the relation. - * XXX should check for duplicate relations - */ - - ScanKeyEntryInitialize(&key[0], - 0, 3, ObjectIdEqualRegProcedure, (Datum)typeOid); - - oidptr = (struct oidlist *) palloc(sizeof(*oidptr)); - oidptr->next = NULL; - optr = oidptr; - rdesc = heap_openr(AttributeRelationName); - sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key); - while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) { - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - optr->reloid = ((AttributeTupleForm)GETSTRUCT(tup))->attrelid; - optr->next = (struct oidlist *) palloc(sizeof(*oidptr)); - optr = optr->next; - } - optr->next = NULL; - heap_endscan(sdesc); - heap_close(rdesc); - - - ScanKeyEntryInitialize(&key[0], 0, - ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, (Datum)0); - optr = oidptr; - rdesc = heap_openr(RelationRelationName); - while (PointerIsValid((char *) optr->next)) { - key[0].sk_argument = (Datum) (optr++)->reloid; + struct oidlist + { + Oid reloid; + struct oidlist *next; + }; + struct oidlist *oidptr, + *optr; + Relation rdesc; + ScanKeyData key[1]; + HeapScanDesc sdesc; + HeapTuple tup; + ItemPointerData itemPointerData; + Buffer buffer; + + /* + * Get the oid's of the relations to be removed by scanning the entire + * attribute relation. We don't need to remove the attributes here, + * because amdestroy will remove all attributes of the relation. XXX + * should check for duplicate relations + */ + + ScanKeyEntryInitialize(&key[0], + 0, 3, ObjectIdEqualRegProcedure, (Datum) typeOid); + + oidptr = (struct oidlist *) palloc(sizeof(*oidptr)); + oidptr->next = NULL; + optr = oidptr; + rdesc = heap_openr(AttributeRelationName); sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key); - tup = heap_getnext(sdesc, 0, &buffer); - if (PointerIsValid(tup)) { - char *name; - - name = (((Form_pg_class)GETSTRUCT(tup))->relname).data; - heap_destroy(name); + while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) + { + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + optr->reloid = ((AttributeTupleForm) GETSTRUCT(tup))->attrelid; + optr->next = (struct oidlist *) palloc(sizeof(*oidptr)); + optr = optr->next; } - } - heap_endscan(sdesc); - heap_close(rdesc); + optr->next = NULL; + heap_endscan(sdesc); + heap_close(rdesc); + + + ScanKeyEntryInitialize(&key[0], 0, + ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, (Datum) 0); + optr = oidptr; + rdesc = heap_openr(RelationRelationName); + while (PointerIsValid((char *) optr->next)) + { + key[0].sk_argument = (Datum) (optr++)->reloid; + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key); + tup = heap_getnext(sdesc, 0, &buffer); + if (PointerIsValid(tup)) + { + char *name; + + name = (((Form_pg_class) GETSTRUCT(tup))->relname).data; + heap_destroy(name); + } + } + heap_endscan(sdesc); + heap_close(rdesc); } -#endif /* NOTYET */ + +#endif /* NOTYET */ /* - * TypeRemove - * Removes the type 'typeName' and all attributes and relations that - * use it. + * TypeRemove + * Removes the type 'typeName' and all attributes and relations that + * use it. */ void -RemoveType(char *typeName) /* type name to be removed */ +RemoveType(char *typeName) /* type name to be removed */ { - Relation relation; - HeapScanDesc scan; - HeapTuple tup; - Oid typeOid; - ItemPointerData itemPointerData; - static ScanKeyData typeKey[1] = { - { 0, Anum_pg_type_typname, NameEqualRegProcedure } - }; - char *shadow_type; - char *userName; - + Relation relation; + HeapScanDesc scan; + HeapTuple tup; + Oid typeOid; + ItemPointerData itemPointerData; + static ScanKeyData typeKey[1] = { + {0, Anum_pg_type_typname, NameEqualRegProcedure} + }; + char *shadow_type; + char *userName; + #ifndef NO_SECURITY - userName = GetPgUserName(); - if (!pg_ownercheck(userName, typeName, TYPNAME)) - elog(WARN, "RemoveType: type '%s': permission denied", - typeName); + userName = GetPgUserName(); + if (!pg_ownercheck(userName, typeName, TYPNAME)) + elog(WARN, "RemoveType: type '%s': permission denied", + typeName); #endif - - relation = heap_openr(TypeRelationName); - fmgr_info(typeKey[0].sk_procedure, &typeKey[0].sk_func, - &typeKey[0].sk_nargs); - - /* Delete the primary type */ - - typeKey[0].sk_argument = PointerGetDatum(typeName); - - scan = heap_beginscan(relation, 0, NowTimeQual, 1, typeKey); - tup = heap_getnext(scan, 0, (Buffer *) 0); - if (!HeapTupleIsValid(tup)) { + + relation = heap_openr(TypeRelationName); + fmgr_info(typeKey[0].sk_procedure, &typeKey[0].sk_func, + &typeKey[0].sk_nargs); + + /* Delete the primary type */ + + typeKey[0].sk_argument = PointerGetDatum(typeName); + + scan = heap_beginscan(relation, 0, NowTimeQual, 1, typeKey); + tup = heap_getnext(scan, 0, (Buffer *) 0); + if (!HeapTupleIsValid(tup)) + { + heap_endscan(scan); + heap_close(relation); + elog(WARN, "RemoveType: type '%s' does not exist", + typeName); + } + typeOid = tup->t_oid; + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); heap_endscan(scan); - heap_close(relation); - elog(WARN, "RemoveType: type '%s' does not exist", - typeName); - } - typeOid = tup->t_oid; - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - heap_delete(relation, &itemPointerData); - heap_endscan(scan); - - /* Now, Delete the "array of" that type */ - shadow_type = makeArrayTypeName(typeName); - typeKey[0].sk_argument = NameGetDatum(shadow_type); - - scan = heap_beginscan(relation, 0, NowTimeQual, - 1, (ScanKey) typeKey); - tup = heap_getnext(scan, 0, (Buffer *) 0); - - if (!HeapTupleIsValid(tup)) + + /* Now, Delete the "array of" that type */ + shadow_type = makeArrayTypeName(typeName); + typeKey[0].sk_argument = NameGetDatum(shadow_type); + + scan = heap_beginscan(relation, 0, NowTimeQual, + 1, (ScanKey) typeKey); + tup = heap_getnext(scan, 0, (Buffer *) 0); + + if (!HeapTupleIsValid(tup)) { - elog(WARN, "RemoveType: type '%s': array stub not found", - typeName); + elog(WARN, "RemoveType: type '%s': array stub not found", + typeName); } - typeOid = tup->t_oid; - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - heap_delete(relation, &itemPointerData); - heap_endscan(scan); - - heap_close(relation); + typeOid = tup->t_oid; + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); + heap_endscan(scan); + + heap_close(relation); } /* * RemoveFunction -- - * Deletes a function. + * Deletes a function. * * Exceptions: - * BadArg if name is invalid. - * "WARN" if function nonexistent. - * ... + * BadArg if name is invalid. + * "WARN" if function nonexistent. + * ... */ void -RemoveFunction(char *functionName, /* function name to be removed */ - int nargs, - List *argNameList /* list of TypeNames */) +RemoveFunction(char *functionName, /* function name to be removed */ + int nargs, + List * argNameList /* list of TypeNames */ ) { - Relation relation; - HeapScanDesc scan; - HeapTuple tup; - Buffer buffer = InvalidBuffer; - bool bufferUsed = FALSE; - Oid argList[8]; - Form_pg_proc the_proc = NULL; - ItemPointerData itemPointerData; - static ScanKeyData key[3] = { - { 0, Anum_pg_proc_proname, NameEqualRegProcedure } - }; - char *userName; - char *typename; - int i; - - memset(argList, 0, 8 * sizeof(Oid)); - for (i=0; i<nargs; i++) { -/* typename = ((TypeName*)(lfirst(argNameList)))->name; */ - typename = strVal(lfirst(argNameList)); - argNameList = lnext(argNameList); - - if (strcmp(typename, "opaque") == 0) - argList[i] = 0; - else { - tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typename), - 0,0,0); - - if (!HeapTupleIsValid(tup)) { - elog(WARN, "RemoveFunction: type '%s' not found",typename); - } - argList[i] = tup->t_oid; + Relation relation; + HeapScanDesc scan; + HeapTuple tup; + Buffer buffer = InvalidBuffer; + bool bufferUsed = FALSE; + Oid argList[8]; + Form_pg_proc the_proc = NULL; + ItemPointerData itemPointerData; + static ScanKeyData key[3] = { + {0, Anum_pg_proc_proname, NameEqualRegProcedure} + }; + char *userName; + char *typename; + int i; + + memset(argList, 0, 8 * sizeof(Oid)); + for (i = 0; i < nargs; i++) + { +/* typename = ((TypeName*)(lfirst(argNameList)))->name; */ + typename = strVal(lfirst(argNameList)); + argNameList = lnext(argNameList); + + if (strcmp(typename, "opaque") == 0) + argList[i] = 0; + else + { + tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typename), + 0, 0, 0); + + if (!HeapTupleIsValid(tup)) + { + elog(WARN, "RemoveFunction: type '%s' not found", typename); + } + argList[i] = tup->t_oid; + } } - } - - tup = SearchSysCacheTuple(PRONAME, PointerGetDatum(functionName), - Int32GetDatum(nargs), - PointerGetDatum(argList),0); - if (!HeapTupleIsValid(tup)) - func_error("RemoveFunction", functionName, nargs, argList); - + + tup = SearchSysCacheTuple(PRONAME, PointerGetDatum(functionName), + Int32GetDatum(nargs), + PointerGetDatum(argList), 0); + if (!HeapTupleIsValid(tup)) + func_error("RemoveFunction", functionName, nargs, argList); + #ifndef NO_SECURITY - userName = GetPgUserName(); - if (!pg_func_ownercheck(userName, functionName, nargs, argList)) { - elog(WARN, "RemoveFunction: function '%s': permission denied", - functionName); - } -#endif - - key[0].sk_argument = PointerGetDatum(functionName); - - fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs); - - relation = heap_openr(ProcedureRelationName); - scan = heap_beginscan(relation, 0, NowTimeQual, 1, key); - - do { /* hope this is ok because it's indexed */ - if (bufferUsed) { - ReleaseBuffer(buffer); - bufferUsed = FALSE; + userName = GetPgUserName(); + if (!pg_func_ownercheck(userName, functionName, nargs, argList)) + { + elog(WARN, "RemoveFunction: function '%s': permission denied", + functionName); } - tup = heap_getnext(scan, 0, (Buffer *) &buffer); - if (!HeapTupleIsValid(tup)) - break; - bufferUsed = TRUE; - the_proc = (Form_pg_proc) GETSTRUCT(tup); - } while ( (namestrcmp(&(the_proc->proname), functionName) == 0) && - (the_proc->pronargs != nargs || - !oid8eq(&(the_proc->proargtypes[0]), &argList[0]))); - - - if (!HeapTupleIsValid(tup) || namestrcmp(&(the_proc->proname), - functionName) != 0) - { - heap_endscan(scan); - heap_close(relation); - func_error("RemoveFunction", functionName,nargs, argList); +#endif + + key[0].sk_argument = PointerGetDatum(functionName); + + fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs); + + relation = heap_openr(ProcedureRelationName); + scan = heap_beginscan(relation, 0, NowTimeQual, 1, key); + + do + { /* hope this is ok because it's indexed */ + if (bufferUsed) + { + ReleaseBuffer(buffer); + bufferUsed = FALSE; + } + tup = heap_getnext(scan, 0, (Buffer *) & buffer); + if (!HeapTupleIsValid(tup)) + break; + bufferUsed = TRUE; + the_proc = (Form_pg_proc) GETSTRUCT(tup); + } while ((namestrcmp(&(the_proc->proname), functionName) == 0) && + (the_proc->pronargs != nargs || + !oid8eq(&(the_proc->proargtypes[0]), &argList[0]))); + + + if (!HeapTupleIsValid(tup) || namestrcmp(&(the_proc->proname), + functionName) != 0) + { + heap_endscan(scan); + heap_close(relation); + func_error("RemoveFunction", functionName, nargs, argList); } - - /* ok, function has been found */ - - if (the_proc->prolang == INTERNALlanguageId) - elog(WARN, "RemoveFunction: function \"%s\" is built-in", - functionName); - - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - heap_delete(relation, &itemPointerData); - heap_endscan(scan); - heap_close(relation); + + /* ok, function has been found */ + + if (the_proc->prolang == INTERNALlanguageId) + elog(WARN, "RemoveFunction: function \"%s\" is built-in", + functionName); + + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); + heap_endscan(scan); + heap_close(relation); } void RemoveAggregate(char *aggName, char *aggType) { - Relation relation; - HeapScanDesc scan; - HeapTuple tup; - ItemPointerData itemPointerData; - char *userName; - Oid basetypeID = InvalidOid; - bool defined; - ScanKeyData aggregateKey[3]; - - - /* - * if a basetype is passed in, then attempt to find an aggregate for that - * specific type. - * - * else if the basetype is blank, then attempt to find an aggregate with a - * basetype of zero. This is valid. It means that the aggregate is to apply - * to all basetypes. ie, a counter of some sort. - * - */ - - if (aggType) { - basetypeID = TypeGet(aggType, &defined); - if (!OidIsValid(basetypeID)) { - elog(WARN, "RemoveAggregate: type '%s' does not exist", aggType); - } - } else { - basetypeID = 0; - } + Relation relation; + HeapScanDesc scan; + HeapTuple tup; + ItemPointerData itemPointerData; + char *userName; + Oid basetypeID = InvalidOid; + bool defined; + ScanKeyData aggregateKey[3]; + + + /* + * if a basetype is passed in, then attempt to find an aggregate for + * that specific type. + * + * else if the basetype is blank, then attempt to find an aggregate with + * a basetype of zero. This is valid. It means that the aggregate is + * to apply to all basetypes. ie, a counter of some sort. + * + */ + + if (aggType) + { + basetypeID = TypeGet(aggType, &defined); + if (!OidIsValid(basetypeID)) + { + elog(WARN, "RemoveAggregate: type '%s' does not exist", aggType); + } + } + else + { + basetypeID = 0; + } /* #ifndef NO_SECURITY */ - userName = GetPgUserName(); - if (!pg_aggr_ownercheck(userName, aggName, basetypeID)) { - if (aggType) { - elog(WARN, "RemoveAggregate: aggregate '%s' on type '%s': permission denied", - aggName, aggType); - } else { - elog(WARN, "RemoveAggregate: aggregate '%s': permission denied", - aggName); - } - } + userName = GetPgUserName(); + if (!pg_aggr_ownercheck(userName, aggName, basetypeID)) + { + if (aggType) + { + elog(WARN, "RemoveAggregate: aggregate '%s' on type '%s': permission denied", + aggName, aggType); + } + else + { + elog(WARN, "RemoveAggregate: aggregate '%s': permission denied", + aggName); + } + } /* #endif */ - ScanKeyEntryInitialize(&aggregateKey[0], 0x0, - Anum_pg_aggregate_aggname, - NameEqualRegProcedure, - PointerGetDatum(aggName)); - - ScanKeyEntryInitialize(&aggregateKey[1], 0x0, - Anum_pg_aggregate_aggbasetype, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(basetypeID)); - - relation = heap_openr(AggregateRelationName); - scan = heap_beginscan(relation, 0, NowTimeQual, 2, aggregateKey); - tup = heap_getnext(scan, 0, (Buffer *) 0); - if (!HeapTupleIsValid(tup)) { - heap_endscan(scan); - heap_close(relation); - if (aggType) { - elog(WARN, "RemoveAggregate: aggregate '%s' for '%s' does not exist", - aggName, aggType); - } else { - elog(WARN, "RemoveAggregate: aggregate '%s' for all types does not exist", - aggName); - } - } - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - heap_delete(relation, &itemPointerData); - heap_endscan(scan); - heap_close(relation); + ScanKeyEntryInitialize(&aggregateKey[0], 0x0, + Anum_pg_aggregate_aggname, + NameEqualRegProcedure, + PointerGetDatum(aggName)); + + ScanKeyEntryInitialize(&aggregateKey[1], 0x0, + Anum_pg_aggregate_aggbasetype, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(basetypeID)); + + relation = heap_openr(AggregateRelationName); + scan = heap_beginscan(relation, 0, NowTimeQual, 2, aggregateKey); + tup = heap_getnext(scan, 0, (Buffer *) 0); + if (!HeapTupleIsValid(tup)) + { + heap_endscan(scan); + heap_close(relation); + if (aggType) + { + elog(WARN, "RemoveAggregate: aggregate '%s' for '%s' does not exist", + aggName, aggType); + } + else + { + elog(WARN, "RemoveAggregate: aggregate '%s' for all types does not exist", + aggName); + } + } + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); + heap_endscan(scan); + heap_close(relation); } diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c index 5d4e4ab2bb8..9b8df698346 100644 --- a/src/backend/commands/rename.c +++ b/src/backend/commands/rename.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * rename.c-- - * renameatt() and renamerel() reside here. + * renameatt() and renamerel() reside here. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.7 1997/08/18 20:52:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.8 1997/09/07 04:40:55 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -32,227 +32,246 @@ #include <catalog/pg_proc.h> #include <catalog/pg_class.h> #include <optimizer/internal.h> -#include <optimizer/prep.h> /* for find_all_inheritors */ +#include <optimizer/prep.h> /* for find_all_inheritors */ #ifndef NO_SECURITY -# include <utils/acl.h> -#endif /* !NO_SECURITY */ +#include <utils/acl.h> +#endif /* !NO_SECURITY */ #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* - * renameatt - changes the name of a attribute in a relation + * renameatt - changes the name of a attribute in a relation * - * Attname attribute is changed in attribute catalog. - * No record of the previous attname is kept (correct?). + * Attname attribute is changed in attribute catalog. + * No record of the previous attname is kept (correct?). * - * get proper reldesc from relation catalog (if not arg) - * scan attribute catalog - * for name conflict (within rel) - * for original attribute (if not arg) - * modify attname in attribute tuple - * insert modified attribute in attribute catalog - * delete original attribute from attribute catalog + * get proper reldesc from relation catalog (if not arg) + * scan attribute catalog + * for name conflict (within rel) + * for original attribute (if not arg) + * modify attname in attribute tuple + * insert modified attribute in attribute catalog + * delete original attribute from attribute catalog * - * XXX Renaming an indexed attribute must (eventually) also change - * the attribute name in the associated indexes. + * XXX Renaming an indexed attribute must (eventually) also change + * the attribute name in the associated indexes. */ void renameatt(char *relname, - char *oldattname, - char *newattname, - char *userName, - int recurse) + char *oldattname, + char *newattname, + char *userName, + int recurse) { - Relation relrdesc, attrdesc; - HeapTuple reltup, oldatttup, newatttup; - ItemPointerData oldTID; - Relation idescs[Num_pg_attr_indices]; - - /* - * permissions checking. this would normally be done in utility.c, - * but this particular routine is recursive. - * - * normally, only the owner of a class can change its schema. - */ - if (IsSystemRelationName(relname)) - elog(WARN, "renameatt: class \"%s\" is a system catalog", - relname); + Relation relrdesc, + attrdesc; + HeapTuple reltup, + oldatttup, + newatttup; + ItemPointerData oldTID; + Relation idescs[Num_pg_attr_indices]; + + /* + * permissions checking. this would normally be done in utility.c, + * but this particular routine is recursive. + * + * normally, only the owner of a class can change its schema. + */ + if (IsSystemRelationName(relname)) + elog(WARN, "renameatt: class \"%s\" is a system catalog", + relname); #ifndef NO_SECURITY - if (!IsBootstrapProcessingMode() && - !pg_ownercheck(userName, relname, RELNAME)) - elog(WARN, "renameatt: you do not own class \"%s\"", - relname); + if (!IsBootstrapProcessingMode() && + !pg_ownercheck(userName, relname, RELNAME)) + elog(WARN, "renameatt: you do not own class \"%s\"", + relname); #endif - - /* - * if the 'recurse' flag is set then we are supposed to rename this - * attribute in all classes that inherit from 'relname' (as well as - * in 'relname'). - * - * any permissions or problems with duplicate attributes will cause - * the whole transaction to abort, which is what we want -- all or - * nothing. - */ - if (recurse) { - Oid myrelid, childrelid; - List *child, *children; - - relrdesc = heap_openr(relname); - if (!RelationIsValid(relrdesc)) { - elog(WARN, "renameatt: unknown relation: \"%s\"", - relname); - } - myrelid = relrdesc->rd_id; - heap_close(relrdesc); - - /* this routine is actually in the planner */ - children = find_all_inheritors(lconsi(myrelid, NIL), NIL); - /* - * find_all_inheritors does the recursive search of the - * inheritance hierarchy, so all we have to do is process - * all of the relids in the list that it returns. + * if the 'recurse' flag is set then we are supposed to rename this + * attribute in all classes that inherit from 'relname' (as well as in + * 'relname'). + * + * any permissions or problems with duplicate attributes will cause the + * whole transaction to abort, which is what we want -- all or + * nothing. */ - foreach (child, children) { - char *childname; - - childrelid = lfirsti(child); - if (childrelid == myrelid) - continue; - relrdesc = heap_open(childrelid); - if (!RelationIsValid(relrdesc)) { - elog(WARN, "renameatt: can't find catalog entry for inheriting class with oid %d", - childrelid); - } - childname = (relrdesc->rd_rel->relname).data; - heap_close(relrdesc); - renameatt(childname, oldattname, newattname, - userName, 0); /* no more recursion! */ + if (recurse) + { + Oid myrelid, + childrelid; + List *child, + *children; + + relrdesc = heap_openr(relname); + if (!RelationIsValid(relrdesc)) + { + elog(WARN, "renameatt: unknown relation: \"%s\"", + relname); + } + myrelid = relrdesc->rd_id; + heap_close(relrdesc); + + /* this routine is actually in the planner */ + children = find_all_inheritors(lconsi(myrelid, NIL), NIL); + + + /* + * find_all_inheritors does the recursive search of the + * inheritance hierarchy, so all we have to do is process all of + * the relids in the list that it returns. + */ + foreach(child, children) + { + char *childname; + + childrelid = lfirsti(child); + if (childrelid == myrelid) + continue; + relrdesc = heap_open(childrelid); + if (!RelationIsValid(relrdesc)) + { + elog(WARN, "renameatt: can't find catalog entry for inheriting class with oid %d", + childrelid); + } + childname = (relrdesc->rd_rel->relname).data; + heap_close(relrdesc); + renameatt(childname, oldattname, newattname, + userName, 0); /* no more recursion! */ + } + } + + relrdesc = heap_openr(RelationRelationName); + reltup = ClassNameIndexScan(relrdesc, relname); + if (!PointerIsValid(reltup)) + { + heap_close(relrdesc); + elog(WARN, "renameatt: relation \"%s\" nonexistent", + relname); + return; } - } - - relrdesc = heap_openr(RelationRelationName); - reltup = ClassNameIndexScan(relrdesc, relname); - if (!PointerIsValid(reltup)) { heap_close(relrdesc); - elog(WARN, "renameatt: relation \"%s\" nonexistent", - relname); - return; - } - heap_close(relrdesc); - - attrdesc = heap_openr(AttributeRelationName); - oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname); - if (!PointerIsValid(oldatttup)) { + + attrdesc = heap_openr(AttributeRelationName); + oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname); + if (!PointerIsValid(oldatttup)) + { + heap_close(attrdesc); + elog(WARN, "renameatt: attribute \"%s\" nonexistent", + oldattname); + } + if (((AttributeTupleForm) GETSTRUCT(oldatttup))->attnum < 0) + { + elog(WARN, "renameatt: system attribute \"%s\" not renamed", + oldattname); + } + + newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname); + if (PointerIsValid(newatttup)) + { + pfree(oldatttup); + heap_close(attrdesc); + elog(WARN, "renameatt: attribute \"%s\" exists", + newattname); + } + + namestrcpy(&(((AttributeTupleForm) (GETSTRUCT(oldatttup)))->attname), + newattname); + oldTID = oldatttup->t_ctid; + + /* insert "fixed" tuple */ + heap_replace(attrdesc, &oldTID, oldatttup); + + /* keep system catalog indices current */ + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, oldatttup); + CatalogCloseIndices(Num_pg_attr_indices, idescs); + heap_close(attrdesc); - elog(WARN, "renameatt: attribute \"%s\" nonexistent", - oldattname); - } - if (((AttributeTupleForm ) GETSTRUCT(oldatttup))->attnum < 0) { - elog(WARN, "renameatt: system attribute \"%s\" not renamed", - oldattname); - } - - newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname); - if (PointerIsValid(newatttup)) { pfree(oldatttup); - heap_close(attrdesc); - elog(WARN, "renameatt: attribute \"%s\" exists", - newattname); - } - - namestrcpy(&(((AttributeTupleForm)(GETSTRUCT(oldatttup)))->attname), - newattname); - oldTID = oldatttup->t_ctid; - - /* insert "fixed" tuple */ - heap_replace(attrdesc, &oldTID, oldatttup); - - /* keep system catalog indices current */ - CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, oldatttup); - CatalogCloseIndices(Num_pg_attr_indices, idescs); - - heap_close(attrdesc); - pfree(oldatttup); } /* - * renamerel - change the name of a relation + * renamerel - change the name of a relation * - * Relname attribute is changed in relation catalog. - * No record of the previous relname is kept (correct?). + * Relname attribute is changed in relation catalog. + * No record of the previous relname is kept (correct?). * - * scan relation catalog - * for name conflict - * for original relation (if not arg) - * modify relname in relation tuple - * insert modified relation in relation catalog - * delete original relation from relation catalog + * scan relation catalog + * for name conflict + * for original relation (if not arg) + * modify relname in relation tuple + * insert modified relation in relation catalog + * delete original relation from relation catalog * - * XXX Will currently lose track of a relation if it is unable to - * properly replace the new relation tuple. + * XXX Will currently lose track of a relation if it is unable to + * properly replace the new relation tuple. */ void renamerel(char oldrelname[], char newrelname[]) { - Relation relrdesc; /* for RELATION relation */ - HeapTuple oldreltup, newreltup; - ItemPointerData oldTID; - char oldpath[MAXPGPATH], newpath[MAXPGPATH]; - Relation idescs[Num_pg_class_indices]; - - if (IsSystemRelationName(oldrelname)) { - elog(WARN, "renamerel: system relation \"%s\" not renamed", - oldrelname); - return; - } - if (IsSystemRelationName(newrelname)) { - elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs", - newrelname); - return; - } - - relrdesc = heap_openr(RelationRelationName); - oldreltup = ClassNameIndexScan(relrdesc, oldrelname); - - if (!PointerIsValid(oldreltup)) { - heap_close(relrdesc); - elog(WARN, "renamerel: relation \"%s\" does not exist", - oldrelname); - } - - newreltup = ClassNameIndexScan(relrdesc, newrelname); - if (PointerIsValid(newreltup)) { + Relation relrdesc; /* for RELATION relation */ + HeapTuple oldreltup, + newreltup; + ItemPointerData oldTID; + char oldpath[MAXPGPATH], + newpath[MAXPGPATH]; + Relation idescs[Num_pg_class_indices]; + + if (IsSystemRelationName(oldrelname)) + { + elog(WARN, "renamerel: system relation \"%s\" not renamed", + oldrelname); + return; + } + if (IsSystemRelationName(newrelname)) + { + elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs", + newrelname); + return; + } + + relrdesc = heap_openr(RelationRelationName); + oldreltup = ClassNameIndexScan(relrdesc, oldrelname); + + if (!PointerIsValid(oldreltup)) + { + heap_close(relrdesc); + elog(WARN, "renamerel: relation \"%s\" does not exist", + oldrelname); + } + + newreltup = ClassNameIndexScan(relrdesc, newrelname); + if (PointerIsValid(newreltup)) + { + pfree(oldreltup); + heap_close(relrdesc); + elog(WARN, "renamerel: relation \"%s\" exists", + newrelname); + } + + /* rename the directory first, so if this fails the rename's not done */ + strcpy(oldpath, relpath(oldrelname)); + strcpy(newpath, relpath(newrelname)); + if (rename(oldpath, newpath) < 0) + elog(WARN, "renamerel: unable to rename file: %m"); + + memmove((char *) (((Form_pg_class) GETSTRUCT(oldreltup))->relname.data), + newrelname, + NAMEDATALEN); + oldTID = oldreltup->t_ctid; + + /* insert fixed rel tuple */ + heap_replace(relrdesc, &oldTID, oldreltup); + + /* keep the system catalog indices current */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, relrdesc, oldreltup); + CatalogCloseIndices(Num_pg_class_indices, idescs); + pfree(oldreltup); heap_close(relrdesc); - elog(WARN, "renamerel: relation \"%s\" exists", - newrelname); - } - - /* rename the directory first, so if this fails the rename's not done */ - strcpy(oldpath, relpath(oldrelname)); - strcpy(newpath, relpath(newrelname)); - if (rename(oldpath, newpath) < 0) - elog(WARN, "renamerel: unable to rename file: %m"); - - memmove((char *) (((Form_pg_class) GETSTRUCT(oldreltup))->relname.data), - newrelname, - NAMEDATALEN); - oldTID = oldreltup->t_ctid; - - /* insert fixed rel tuple */ - heap_replace(relrdesc, &oldTID, oldreltup); - - /* keep the system catalog indices current */ - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, relrdesc, oldreltup); - CatalogCloseIndices(Num_pg_class_indices, idescs); - - pfree(oldreltup); - heap_close(relrdesc); } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 5d35f7b60f5..c4bd8c40dcf 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * sequence.c-- - * PostgreSQL sequences support code. + * PostgreSQL sequences support code. * *------------------------------------------------------------------------- */ @@ -19,523 +19,539 @@ #include <commands/sequence.h> #include <utils/builtins.h> -#define SEQ_MAGIC 0x1717 +#define SEQ_MAGIC 0x1717 #define SEQ_MAXVALUE ((int4)0x7FFFFFFF) #define SEQ_MINVALUE -(SEQ_MAXVALUE) -bool ItsSequenceCreation = false; +bool ItsSequenceCreation = false; -typedef struct FormData_pg_sequence { - NameData sequence_name; - int4 last_value; - int4 increment_by; - int4 max_value; - int4 min_value; - int4 cache_value; - char is_cycled; - char is_called; -} FormData_pg_sequence; - -typedef FormData_pg_sequence *SequenceTupleForm; - -typedef struct sequence_magic { - uint32 magic; -} sequence_magic; +typedef struct FormData_pg_sequence +{ + NameData sequence_name; + int4 last_value; + int4 increment_by; + int4 max_value; + int4 min_value; + int4 cache_value; + char is_cycled; + char is_called; +} FormData_pg_sequence; + +typedef FormData_pg_sequence *SequenceTupleForm; + +typedef struct sequence_magic +{ + uint32 magic; +} sequence_magic; -typedef struct SeqTableData { - char *name; - Oid relid; +typedef struct SeqTableData +{ + char *name; + Oid relid; Relation rel; int4 cached; int4 last; int4 increment; - struct SeqTableData *next; -} SeqTableData; + struct SeqTableData *next; +} SeqTableData; typedef SeqTableData *SeqTable; static SeqTable seqtab = NULL; -static SeqTable init_sequence (char *caller, char *name); -static SequenceTupleForm read_info (char * caller, SeqTable elm, Buffer * buf); -static void init_params (CreateSeqStmt *seq, SequenceTupleForm new); -static int get_param (DefElem *def); +static SeqTable init_sequence(char *caller, char *name); +static SequenceTupleForm read_info(char *caller, SeqTable elm, Buffer * buf); +static void init_params(CreateSeqStmt * seq, SequenceTupleForm new); +static int get_param(DefElem * def); /* * DefineSequence -- - * Creates a new sequence relation + * Creates a new sequence relation */ void -DefineSequence (CreateSeqStmt *seq) +DefineSequence(CreateSeqStmt * seq) { - FormData_pg_sequence new; - CreateStmt *stmt = makeNode (CreateStmt); - ColumnDef *coldef; - TypeName *typnam; - Relation rel; - Buffer buf; - PageHeader page; - sequence_magic *sm; - HeapTuple tuple; - TupleDesc tupDesc; - Datum value[SEQ_COL_LASTCOL]; - char null[SEQ_COL_LASTCOL]; - int i; - - /* Check and set values */ - init_params (seq, &new); - - /* - * Create relation (and fill null[] & value[]) - */ - stmt->tableElts = NIL; - for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++) - { - typnam = makeNode(TypeName); - typnam->setof = FALSE; - typnam->arrayBounds = NULL; - coldef = makeNode(ColumnDef); - coldef->typename = typnam; - coldef->defval = NULL; - coldef->is_not_null = false; - null[i-1] = ' '; - - switch (i) + FormData_pg_sequence new; + CreateStmt *stmt = makeNode(CreateStmt); + ColumnDef *coldef; + TypeName *typnam; + Relation rel; + Buffer buf; + PageHeader page; + sequence_magic *sm; + HeapTuple tuple; + TupleDesc tupDesc; + Datum value[SEQ_COL_LASTCOL]; + char null[SEQ_COL_LASTCOL]; + int i; + + /* Check and set values */ + init_params(seq, &new); + + /* + * Create relation (and fill null[] & value[]) + */ + stmt->tableElts = NIL; + for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++) { - case SEQ_COL_NAME: - typnam->name = "name"; - coldef->colname = "sequence_name"; - value[i-1] = PointerGetDatum (seq->seqname); - break; - case SEQ_COL_LASTVAL: - typnam->name = "int4"; - coldef->colname = "last_value"; - value[i-1] = Int32GetDatum (new.last_value); - break; - case SEQ_COL_INCBY: - typnam->name = "int4"; - coldef->colname = "increment_by"; - value[i-1] = Int32GetDatum (new.increment_by); - break; - case SEQ_COL_MAXVALUE: - typnam->name = "int4"; - coldef->colname = "max_value"; - value[i-1] = Int32GetDatum (new.max_value); - break; - case SEQ_COL_MINVALUE: - typnam->name = "int4"; - coldef->colname = "min_value"; - value[i-1] = Int32GetDatum (new.min_value); - break; - case SEQ_COL_CACHE: - typnam->name = "int4"; - coldef->colname = "cache_value"; - value[i-1] = Int32GetDatum (new.cache_value); - break; - case SEQ_COL_CYCLE: - typnam->name = "char"; - coldef->colname = "is_cycled"; - value[i-1] = CharGetDatum (new.is_cycled); - break; - case SEQ_COL_CALLED: - typnam->name = "char"; - coldef->colname = "is_called"; - value[i-1] = CharGetDatum ('f'); - break; - } - stmt->tableElts = lappend (stmt->tableElts, coldef); - } - - stmt->relname = seq->seqname; - stmt->archiveLoc = -1; /* default */ - stmt->archiveType = ARCH_NONE; - stmt->inhRelnames = NIL; - stmt->constraints = NIL; - - ItsSequenceCreation = true; /* hack */ - - DefineRelation (stmt); - - /* Xact abort calls CloseSequences, which turns ItsSequenceCreation off */ - ItsSequenceCreation = false; /* hack */ - - rel = heap_openr (seq->seqname); - Assert ( RelationIsValid (rel) ); - - RelationSetLockForWrite (rel); - - tupDesc = RelationGetTupleDescriptor(rel); - - Assert ( RelationGetNumberOfBlocks (rel) == 0 ); - buf = ReadBuffer (rel, P_NEW); - - if ( !BufferIsValid (buf) ) - elog (WARN, "DefineSequence: ReadBuffer failed"); - - page = (PageHeader) BufferGetPage (buf); - - PageInit((Page)page, BufferGetPageSize(buf), sizeof(sequence_magic)); - sm = (sequence_magic *) PageGetSpecialPointer (page); - sm->magic = SEQ_MAGIC; - - /* Now - form & insert sequence tuple */ - tuple = heap_formtuple (tupDesc, value, null); - heap_insert (rel, tuple); - - if ( WriteBuffer (buf) == STATUS_ERROR ) - elog (WARN, "DefineSequence: WriteBuffer failed"); - - RelationUnsetLockForWrite (rel); - heap_close (rel); - - return; + typnam = makeNode(TypeName); + typnam->setof = FALSE; + typnam->arrayBounds = NULL; + coldef = makeNode(ColumnDef); + coldef->typename = typnam; + coldef->defval = NULL; + coldef->is_not_null = false; + null[i - 1] = ' '; + + switch (i) + { + case SEQ_COL_NAME: + typnam->name = "name"; + coldef->colname = "sequence_name"; + value[i - 1] = PointerGetDatum(seq->seqname); + break; + case SEQ_COL_LASTVAL: + typnam->name = "int4"; + coldef->colname = "last_value"; + value[i - 1] = Int32GetDatum(new.last_value); + break; + case SEQ_COL_INCBY: + typnam->name = "int4"; + coldef->colname = "increment_by"; + value[i - 1] = Int32GetDatum(new.increment_by); + break; + case SEQ_COL_MAXVALUE: + typnam->name = "int4"; + coldef->colname = "max_value"; + value[i - 1] = Int32GetDatum(new.max_value); + break; + case SEQ_COL_MINVALUE: + typnam->name = "int4"; + coldef->colname = "min_value"; + value[i - 1] = Int32GetDatum(new.min_value); + break; + case SEQ_COL_CACHE: + typnam->name = "int4"; + coldef->colname = "cache_value"; + value[i - 1] = Int32GetDatum(new.cache_value); + break; + case SEQ_COL_CYCLE: + typnam->name = "char"; + coldef->colname = "is_cycled"; + value[i - 1] = CharGetDatum(new.is_cycled); + break; + case SEQ_COL_CALLED: + typnam->name = "char"; + coldef->colname = "is_called"; + value[i - 1] = CharGetDatum('f'); + break; + } + stmt->tableElts = lappend(stmt->tableElts, coldef); + } + + stmt->relname = seq->seqname; + stmt->archiveLoc = -1; /* default */ + stmt->archiveType = ARCH_NONE; + stmt->inhRelnames = NIL; + stmt->constraints = NIL; + + ItsSequenceCreation = true; /* hack */ + + DefineRelation(stmt); + + /* + * Xact abort calls CloseSequences, which turns ItsSequenceCreation + * off + */ + ItsSequenceCreation = false;/* hack */ + + rel = heap_openr(seq->seqname); + Assert(RelationIsValid(rel)); + + RelationSetLockForWrite(rel); + + tupDesc = RelationGetTupleDescriptor(rel); + + Assert(RelationGetNumberOfBlocks(rel) == 0); + buf = ReadBuffer(rel, P_NEW); + + if (!BufferIsValid(buf)) + elog(WARN, "DefineSequence: ReadBuffer failed"); + + page = (PageHeader) BufferGetPage(buf); + + PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic)); + sm = (sequence_magic *) PageGetSpecialPointer(page); + sm->magic = SEQ_MAGIC; + + /* Now - form & insert sequence tuple */ + tuple = heap_formtuple(tupDesc, value, null); + heap_insert(rel, tuple); + + if (WriteBuffer(buf) == STATUS_ERROR) + elog(WARN, "DefineSequence: WriteBuffer failed"); + + RelationUnsetLockForWrite(rel); + heap_close(rel); + + return; } int4 -nextval (struct varlena * seqin) +nextval(struct varlena * seqin) { - char *seqname = textout(seqin); - SeqTable elm; - Buffer buf; - SequenceTupleForm seq; - ItemPointerData iptr; - int4 incby, maxv, minv, cache; - int4 result, next, rescnt = 0; - - /* open and WIntentLock sequence */ - elm = init_sequence ("nextval", seqname); - pfree (seqname); - - if ( elm->last != elm->cached ) /* some numbers were cached */ - { - elm->last += elm->increment; - return (elm->last); - } - - seq = read_info ("nextval", elm, &buf); /* lock page and read tuple */ - - next = result = seq->last_value; - incby = seq->increment_by; - maxv = seq->max_value; - minv = seq->min_value; - cache = seq->cache_value; - - if ( seq->is_called != 't' ) - rescnt++; /* last_value if not called */ - - while ( rescnt < cache ) /* try to fetch cache numbers */ - { - /* - * Check MAXVALUE for ascending sequences - * and MINVALUE for descending sequences - */ - if ( incby > 0 ) /* ascending sequence */ - { - if ( ( maxv >= 0 && next > maxv - incby) || - ( maxv < 0 && next + incby > maxv ) ) - { - if ( rescnt > 0 ) - break; /* stop caching */ - if ( seq->is_cycled != 't' ) - elog (WARN, "%s.nextval: got MAXVALUE (%d)", - elm->name, maxv); - next = minv; - } - else - next += incby; + char *seqname = textout(seqin); + SeqTable elm; + Buffer buf; + SequenceTupleForm seq; + ItemPointerData iptr; + int4 incby, + maxv, + minv, + cache; + int4 result, + next, + rescnt = 0; + + /* open and WIntentLock sequence */ + elm = init_sequence("nextval", seqname); + pfree(seqname); + + if (elm->last != elm->cached) /* some numbers were cached */ + { + elm->last += elm->increment; + return (elm->last); } - else /* descending sequence */ + + seq = read_info("nextval", elm, &buf); /* lock page and read + * tuple */ + + next = result = seq->last_value; + incby = seq->increment_by; + maxv = seq->max_value; + minv = seq->min_value; + cache = seq->cache_value; + + if (seq->is_called != 't') + rescnt++; /* last_value if not called */ + + while (rescnt < cache) /* try to fetch cache numbers */ { - if ( ( minv < 0 && next < minv - incby ) || - ( minv >= 0 && next + incby < minv ) ) - { - if ( rescnt > 0 ) - break; /* stop caching */ - if ( seq->is_cycled != 't' ) - elog (WARN, "%s.nextval: got MINVALUE (%d)", - elm->name, minv); - next = maxv; - } - else - next += incby; - } - rescnt++; /* got result */ - if ( rescnt == 1 ) /* if it's first one - */ - result = next; /* it's what to return */ - } - - /* save info in local cache */ - elm->last = result; /* last returned number */ - elm->cached = next; /* last cached number */ - - /* save info in sequence relation */ - seq->last_value = next; /* last fetched number */ - seq->is_called = 't'; - - if ( WriteBuffer (buf) == STATUS_ERROR ) - elog (WARN, "%s.nextval: WriteBuffer failed", elm->name); - - ItemPointerSet(&iptr, 0, FirstOffsetNumber); - RelationUnsetSingleWLockPage (elm->rel, &iptr); - - return (result); - + + /* + * Check MAXVALUE for ascending sequences and MINVALUE for + * descending sequences + */ + if (incby > 0) /* ascending sequence */ + { + if ((maxv >= 0 && next > maxv - incby) || + (maxv < 0 && next + incby > maxv)) + { + if (rescnt > 0) + break; /* stop caching */ + if (seq->is_cycled != 't') + elog(WARN, "%s.nextval: got MAXVALUE (%d)", + elm->name, maxv); + next = minv; + } + else + next += incby; + } + else +/* descending sequence */ + { + if ((minv < 0 && next < minv - incby) || + (minv >= 0 && next + incby < minv)) + { + if (rescnt > 0) + break; /* stop caching */ + if (seq->is_cycled != 't') + elog(WARN, "%s.nextval: got MINVALUE (%d)", + elm->name, minv); + next = maxv; + } + else + next += incby; + } + rescnt++; /* got result */ + if (rescnt == 1) /* if it's first one - */ + result = next; /* it's what to return */ + } + + /* save info in local cache */ + elm->last = result; /* last returned number */ + elm->cached = next; /* last cached number */ + + /* save info in sequence relation */ + seq->last_value = next; /* last fetched number */ + seq->is_called = 't'; + + if (WriteBuffer(buf) == STATUS_ERROR) + elog(WARN, "%s.nextval: WriteBuffer failed", elm->name); + + ItemPointerSet(&iptr, 0, FirstOffsetNumber); + RelationUnsetSingleWLockPage(elm->rel, &iptr); + + return (result); + } int4 -currval (struct varlena * seqin) +currval(struct varlena * seqin) { - char *seqname = textout(seqin); - SeqTable elm; - int4 result; - - /* open and WIntentLock sequence */ - elm = init_sequence ("currval", seqname); - pfree (seqname); - - if ( elm->increment == 0 ) /* nextval/read_info were not called */ - { - elog (WARN, "%s.currval is not yet defined in this session", elm->name); - } - - result = elm->last; - - return (result); - + char *seqname = textout(seqin); + SeqTable elm; + int4 result; + + /* open and WIntentLock sequence */ + elm = init_sequence("currval", seqname); + pfree(seqname); + + if (elm->increment == 0) /* nextval/read_info were not called */ + { + elog(WARN, "%s.currval is not yet defined in this session", elm->name); + } + + result = elm->last; + + return (result); + } -static SequenceTupleForm -read_info (char * caller, SeqTable elm, Buffer * buf) +static SequenceTupleForm +read_info(char *caller, SeqTable elm, Buffer * buf) { - ItemPointerData iptr; - PageHeader page; - ItemId lp; - HeapTuple tuple; - sequence_magic *sm; - SequenceTupleForm seq; - - ItemPointerSet(&iptr, 0, FirstOffsetNumber); - RelationSetSingleWLockPage (elm->rel, &iptr); - - if ( RelationGetNumberOfBlocks (elm->rel) != 1 ) - elog (WARN, "%s.%s: invalid number of blocks in sequence", - elm->name, caller); - - *buf = ReadBuffer (elm->rel, 0); - if ( !BufferIsValid (*buf) ) - elog (WARN, "%s.%s: ReadBuffer failed", elm->name, caller); - - page = (PageHeader) BufferGetPage (*buf); - sm = (sequence_magic *) PageGetSpecialPointer (page); - - if ( sm->magic != SEQ_MAGIC ) - elog (WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic); - - lp = PageGetItemId (page, FirstOffsetNumber); - Assert (ItemIdIsUsed (lp)); - tuple = (HeapTuple) PageGetItem ((Page) page, lp); - - seq = (SequenceTupleForm) GETSTRUCT(tuple); - - elm->increment = seq->increment_by; - - return (seq); + ItemPointerData iptr; + PageHeader page; + ItemId lp; + HeapTuple tuple; + sequence_magic *sm; + SequenceTupleForm seq; + + ItemPointerSet(&iptr, 0, FirstOffsetNumber); + RelationSetSingleWLockPage(elm->rel, &iptr); + + if (RelationGetNumberOfBlocks(elm->rel) != 1) + elog(WARN, "%s.%s: invalid number of blocks in sequence", + elm->name, caller); + + *buf = ReadBuffer(elm->rel, 0); + if (!BufferIsValid(*buf)) + elog(WARN, "%s.%s: ReadBuffer failed", elm->name, caller); + + page = (PageHeader) BufferGetPage(*buf); + sm = (sequence_magic *) PageGetSpecialPointer(page); + + if (sm->magic != SEQ_MAGIC) + elog(WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic); + + lp = PageGetItemId(page, FirstOffsetNumber); + Assert(ItemIdIsUsed(lp)); + tuple = (HeapTuple) PageGetItem((Page) page, lp); + + seq = (SequenceTupleForm) GETSTRUCT(tuple); + + elm->increment = seq->increment_by; + + return (seq); } -static SeqTable -init_sequence (char * caller, char * name) +static SeqTable +init_sequence(char *caller, char *name) { - SeqTable elm, priv = (SeqTable) NULL; - SeqTable temp; - - for (elm = seqtab; elm != (SeqTable) NULL; ) - { - if ( strcmp (elm->name, name) == 0 ) - break; - priv = elm; - elm = elm->next; - } - - if ( elm == (SeqTable) NULL ) /* not found */ - { - temp = (SeqTable) malloc (sizeof(SeqTableData)); - temp->name = malloc (strlen(name) + 1); - strcpy (temp->name, name); - temp->rel = (Relation) NULL; - temp->cached = temp->last = temp->increment = 0; - temp->next = (SeqTable) NULL; - } - else /* found */ - { - if ( elm->rel != (Relation) NULL) /* already opened */ - return (elm); - temp = elm; - } - - temp->rel = heap_openr (name); - - if ( ! RelationIsValid (temp->rel) ) - elog (WARN, "%s.%s: sequence does not exist", name, caller); - - RelationSetWIntentLock (temp->rel); - - if ( temp->rel->rd_rel->relkind != RELKIND_SEQUENCE ) - elog (WARN, "%s.%s: %s is not sequence !", name, caller, name); - - if ( elm != (SeqTable) NULL ) /* we opened sequence from our */ - { /* SeqTable - check relid ! */ - if ( RelationGetRelationId (elm->rel) != elm->relid ) - { - elog (NOTICE, "%s.%s: sequence was re-created", - name, caller, name); - elm->cached = elm->last = elm->increment = 0; - elm->relid = RelationGetRelationId (elm->rel); - } - } - else - { - elm = temp; - elm->relid = RelationGetRelationId (elm->rel); - if ( seqtab == (SeqTable) NULL ) - seqtab = elm; - else - priv->next = elm; - } - - return (elm); - + SeqTable elm, + priv = (SeqTable) NULL; + SeqTable temp; + + for (elm = seqtab; elm != (SeqTable) NULL;) + { + if (strcmp(elm->name, name) == 0) + break; + priv = elm; + elm = elm->next; + } + + if (elm == (SeqTable) NULL) /* not found */ + { + temp = (SeqTable) malloc(sizeof(SeqTableData)); + temp->name = malloc(strlen(name) + 1); + strcpy(temp->name, name); + temp->rel = (Relation) NULL; + temp->cached = temp->last = temp->increment = 0; + temp->next = (SeqTable) NULL; + } + else +/* found */ + { + if (elm->rel != (Relation) NULL) /* already opened */ + return (elm); + temp = elm; + } + + temp->rel = heap_openr(name); + + if (!RelationIsValid(temp->rel)) + elog(WARN, "%s.%s: sequence does not exist", name, caller); + + RelationSetWIntentLock(temp->rel); + + if (temp->rel->rd_rel->relkind != RELKIND_SEQUENCE) + elog(WARN, "%s.%s: %s is not sequence !", name, caller, name); + + if (elm != (SeqTable) NULL) /* we opened sequence from our */ + { /* SeqTable - check relid ! */ + if (RelationGetRelationId(elm->rel) != elm->relid) + { + elog(NOTICE, "%s.%s: sequence was re-created", + name, caller, name); + elm->cached = elm->last = elm->increment = 0; + elm->relid = RelationGetRelationId(elm->rel); + } + } + else + { + elm = temp; + elm->relid = RelationGetRelationId(elm->rel); + if (seqtab == (SeqTable) NULL) + seqtab = elm; + else + priv->next = elm; + } + + return (elm); + } /* * CloseSequences -- - * is calling by xact mgr at commit/abort. + * is calling by xact mgr at commit/abort. */ void -CloseSequences (void) +CloseSequences(void) { - SeqTable elm; - Relation rel; - - ItsSequenceCreation = false; - - for (elm = seqtab; elm != (SeqTable) NULL; ) - { - if ( elm->rel != (Relation) NULL ) /* opened in current xact */ + SeqTable elm; + Relation rel; + + ItsSequenceCreation = false; + + for (elm = seqtab; elm != (SeqTable) NULL;) { - rel = elm->rel; - elm->rel = (Relation) NULL; - RelationUnsetWIntentLock (rel); - heap_close (rel); - } - elm = elm->next; - } - - return; - + if (elm->rel != (Relation) NULL) /* opened in current xact */ + { + rel = elm->rel; + elm->rel = (Relation) NULL; + RelationUnsetWIntentLock(rel); + heap_close(rel); + } + elm = elm->next; + } + + return; + } -static void -init_params (CreateSeqStmt *seq, SequenceTupleForm new) +static void +init_params(CreateSeqStmt * seq, SequenceTupleForm new) { - DefElem *last_value = NULL; - DefElem *increment_by = NULL; - DefElem *max_value = NULL; - DefElem *min_value = NULL; - DefElem *cache_value = NULL; - List *option; - - new->is_cycled = 'f'; - foreach (option, seq->options) - { - DefElem *defel = (DefElem *)lfirst(option); - - if ( !strcasecmp(defel->defname, "increment") ) - increment_by = defel; - else if ( !strcasecmp(defel->defname, "start") ) - last_value = defel; - else if ( !strcasecmp(defel->defname, "maxvalue") ) - max_value = defel; - else if ( !strcasecmp(defel->defname, "minvalue") ) - min_value = defel; - else if ( !strcasecmp(defel->defname, "cache") ) - cache_value = defel; - else if ( !strcasecmp(defel->defname, "cycle") ) - { - if ( defel->arg != (Node*) NULL ) - elog (WARN, "DefineSequence: CYCLE ??"); - new->is_cycled = 't'; - } - else - elog (WARN, "DefineSequence: option \"%s\" not recognized", - defel->defname); - } - - if ( increment_by == (DefElem*) NULL ) /* INCREMENT BY */ - new->increment_by = 1; - else if ( ( new->increment_by = get_param (increment_by) ) == 0 ) - elog (WARN, "DefineSequence: can't INCREMENT by 0"); - - if ( max_value == (DefElem*) NULL ) /* MAXVALUE */ - if ( new->increment_by > 0 ) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + DefElem *last_value = NULL; + DefElem *increment_by = NULL; + DefElem *max_value = NULL; + DefElem *min_value = NULL; + DefElem *cache_value = NULL; + List *option; + + new->is_cycled = 'f'; + foreach(option, seq->options) + { + DefElem *defel = (DefElem *) lfirst(option); + + if (!strcasecmp(defel->defname, "increment")) + increment_by = defel; + else if (!strcasecmp(defel->defname, "start")) + last_value = defel; + else if (!strcasecmp(defel->defname, "maxvalue")) + max_value = defel; + else if (!strcasecmp(defel->defname, "minvalue")) + min_value = defel; + else if (!strcasecmp(defel->defname, "cache")) + cache_value = defel; + else if (!strcasecmp(defel->defname, "cycle")) + { + if (defel->arg != (Node *) NULL) + elog(WARN, "DefineSequence: CYCLE ??"); + new->is_cycled = 't'; + } + else + elog(WARN, "DefineSequence: option \"%s\" not recognized", + defel->defname); + } + + if (increment_by == (DefElem *) NULL) /* INCREMENT BY */ + new->increment_by = 1; + else if ((new->increment_by = get_param(increment_by)) == 0) + elog(WARN, "DefineSequence: can't INCREMENT by 0"); + + if (max_value == (DefElem *) NULL) /* MAXVALUE */ + if (new->increment_by > 0) + new->max_value = SEQ_MAXVALUE; /* ascending seq */ + else + new->max_value = -1;/* descending seq */ else - new->max_value = -1; /* descending seq */ - else - new->max_value = get_param (max_value); + new->max_value = get_param(max_value); - if ( min_value == (DefElem*) NULL ) /* MINVALUE */ - if ( new->increment_by > 0 ) - new->min_value = 1; /* ascending seq */ + if (min_value == (DefElem *) NULL) /* MINVALUE */ + if (new->increment_by > 0) + new->min_value = 1; /* ascending seq */ + else + new->min_value = SEQ_MINVALUE; /* descending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ - else - new->min_value = get_param (min_value); - - if ( new->min_value >= new->max_value ) - elog (WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)", - new->min_value, new->max_value); - - if ( last_value == (DefElem*) NULL ) /* START WITH */ - if ( new->increment_by > 0 ) - new->last_value = new->min_value; /* ascending seq */ + new->min_value = get_param(min_value); + + if (new->min_value >= new->max_value) + elog(WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)", + new->min_value, new->max_value); + + if (last_value == (DefElem *) NULL) /* START WITH */ + if (new->increment_by > 0) + new->last_value = new->min_value; /* ascending seq */ + else + new->last_value = new->max_value; /* descending seq */ else - new->last_value = new->max_value; /* descending seq */ - else - new->last_value = get_param (last_value); - - if ( new->last_value < new->min_value ) - elog (WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)", - new->last_value, new->min_value); - if ( new->last_value > new->max_value ) - elog (WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)", - new->last_value, new->max_value); - - if ( cache_value == (DefElem*) NULL ) /* CACHE */ - new->cache_value = 1; - else if ( ( new->cache_value = get_param (cache_value) ) <= 0 ) - elog (WARN, "DefineSequence: CACHE (%d) can't be <= 0", - new->cache_value); + new->last_value = get_param(last_value); + + if (new->last_value < new->min_value) + elog(WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)", + new->last_value, new->min_value); + if (new->last_value > new->max_value) + elog(WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)", + new->last_value, new->max_value); + + if (cache_value == (DefElem *) NULL) /* CACHE */ + new->cache_value = 1; + else if ((new->cache_value = get_param(cache_value)) <= 0) + elog(WARN, "DefineSequence: CACHE (%d) can't be <= 0", + new->cache_value); } static int -get_param (DefElem *def) +get_param(DefElem * def) { - if ( def->arg == (Node*) NULL ) - elog (WARN, "DefineSequence: \"%s\" value unspecified", def->defname); - - if ( nodeTag (def->arg) == T_Integer ) - return (intVal (def->arg)); - - elog (WARN, "DefineSequence: \"%s\" is to be integer", def->defname); - return (-1); + if (def->arg == (Node *) NULL) + elog(WARN, "DefineSequence: \"%s\" value unspecified", def->defname); + + if (nodeTag(def->arg) == T_Integer) + return (intVal(def->arg)); + + elog(WARN, "DefineSequence: \"%s\" is to be integer", def->defname); + return (-1); } diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2919df473fd..53ab1838cfe 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * trigger.c-- - * PostgreSQL TRIGGERs support code. + * PostgreSQL TRIGGERs support code. * *------------------------------------------------------------------------- */ @@ -32,581 +32,587 @@ #include "utils/syscache.h" #endif -TriggerData *CurrentTriggerData = NULL; +TriggerData *CurrentTriggerData = NULL; -void RelationBuildTriggers (Relation relation); -void FreeTriggerDesc (Relation relation); +void RelationBuildTriggers(Relation relation); +void FreeTriggerDesc(Relation relation); -static void DescribeTrigger (TriggerDesc *trigdesc, Trigger *trigger); +static void DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger); -extern void fmgr_info(Oid procedureId, func_ptr *function, int *nargs); +extern void fmgr_info(Oid procedureId, func_ptr * function, int *nargs); extern GlobalMemory CacheCxt; void -CreateTrigger (CreateTrigStmt *stmt) +CreateTrigger(CreateTrigStmt * stmt) { - int16 tgtype; - int16 tgattr[8] = {0}; - Datum values[Natts_pg_trigger]; - char nulls[Natts_pg_trigger]; - Relation rel; - Relation tgrel; - HeapScanDesc tgscan; - ScanKeyData key; - Relation relrdesc; - HeapTuple tuple; - ItemPointerData oldTID; - Relation idescs[Num_pg_trigger_indices]; - Relation ridescs[Num_pg_class_indices]; - MemoryContext oldcxt; - Oid fargtypes[8]; - int found = 0; - int i; - - if ( IsSystemRelationName (stmt->relname) ) - elog(WARN, "CreateTrigger: can't create trigger for system relation %s", stmt->relname); + int16 tgtype; + int16 tgattr[8] = {0}; + Datum values[Natts_pg_trigger]; + char nulls[Natts_pg_trigger]; + Relation rel; + Relation tgrel; + HeapScanDesc tgscan; + ScanKeyData key; + Relation relrdesc; + HeapTuple tuple; + ItemPointerData oldTID; + Relation idescs[Num_pg_trigger_indices]; + Relation ridescs[Num_pg_class_indices]; + MemoryContext oldcxt; + Oid fargtypes[8]; + int found = 0; + int i; + + if (IsSystemRelationName(stmt->relname)) + elog(WARN, "CreateTrigger: can't create trigger for system relation %s", stmt->relname); #ifndef NO_SECURITY - if ( !pg_ownercheck (GetPgUserName (), stmt->relname, RELNAME)) - elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); + if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME)) + elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); #endif - - rel = heap_openr (stmt->relname); - if ( !RelationIsValid (rel) ) - elog (WARN, "CreateTrigger: there is no relation %s", stmt->relname); - - RelationSetLockForWrite (rel); - - TRIGGER_CLEAR_TYPE (tgtype); - if ( stmt->before ) - TRIGGER_SETT_BEFORE (tgtype); - if ( stmt->row ) - TRIGGER_SETT_ROW (tgtype); - for (i = 0; i < 3 && stmt->actions[i]; i++) - { - switch ( stmt->actions[i] ) - { - case 'i': - if ( TRIGGER_FOR_INSERT (tgtype) ) - elog (WARN, "CreateTrigger: double INSERT event specified"); - TRIGGER_SETT_INSERT (tgtype); - break; - case 'd': - if ( TRIGGER_FOR_DELETE (tgtype) ) - elog (WARN, "CreateTrigger: double DELETE event specified"); - TRIGGER_SETT_DELETE (tgtype); - break; - case 'u': - if ( TRIGGER_FOR_UPDATE (tgtype) ) - elog (WARN, "CreateTrigger: double UPDATE event specified"); - TRIGGER_SETT_UPDATE (tgtype); - break; - default: - elog (WARN, "CreateTrigger: unknown event specified"); - break; - } - } - - /* Scan pg_trigger */ - tgrel = heap_openr (TriggerRelationName); - RelationSetLockForWrite (tgrel); - ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key); - while (tuple = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tuple)) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple); - if ( namestrcmp (&(pg_trigger->tgname), stmt->trigname) == 0 ) - elog (WARN, "CreateTrigger: trigger %s already defined on relation %s", - stmt->trigname, stmt->relname); - else - found++; - } - heap_endscan (tgscan); - - memset (fargtypes, 0, 8 * sizeof(Oid)); - tuple = SearchSysCacheTuple (PRONAME, - PointerGetDatum (stmt->funcname), - 0, PointerGetDatum (fargtypes), 0); - if ( !HeapTupleIsValid (tuple) || - ((Form_pg_proc)GETSTRUCT(tuple))->prorettype != 0 || - ((Form_pg_proc)GETSTRUCT(tuple))->pronargs != 0 ) - elog (WARN, "CreateTrigger: function %s () does not exist", stmt->funcname); - - if ( ((Form_pg_proc)GETSTRUCT(tuple))->prolang != ClanguageId ) - elog (WARN, "CreateTrigger: only C functions are supported"); - - memset (nulls, ' ', Natts_pg_trigger * sizeof (char)); - - values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum (rel->rd_id); - values[Anum_pg_trigger_tgname - 1] = NameGetDatum (namein (stmt->trigname)); - values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum (tuple->t_oid); - values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum (tgtype); - if ( stmt->args ) - { - List *le; - char *args; - int16 nargs = length (stmt->args); - int len = 0; - - foreach (le, stmt->args) - { - char *ar = (char *) lfirst (le); - len += strlen (ar) + 4; - } - args = (char *) palloc (len + 1); - args[0] = 0; - foreach (le, stmt->args) - sprintf (args + strlen (args), "%s\\000", (char *)lfirst (le)); - values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum (nargs); - values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum (byteain (args)); - } - else - { - values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum (0); - values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum (byteain ("")); - } - values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum (tgattr); - - tuple = heap_formtuple (tgrel->rd_att, values, nulls); - heap_insert (tgrel, tuple); - CatalogOpenIndices (Num_pg_trigger_indices, Name_pg_trigger_indices, idescs); - CatalogIndexInsert (idescs, Num_pg_trigger_indices, tgrel, tuple); - CatalogCloseIndices (Num_pg_trigger_indices, idescs); - pfree (tuple); - RelationUnsetLockForWrite (tgrel); - heap_close (tgrel); - - pfree (DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); - pfree (DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); - - /* update pg_class */ - relrdesc = heap_openr (RelationRelationName); - tuple = ClassNameIndexScan (relrdesc, stmt->relname); - if ( !PointerIsValid (tuple) ) - { + + rel = heap_openr(stmt->relname); + if (!RelationIsValid(rel)) + elog(WARN, "CreateTrigger: there is no relation %s", stmt->relname); + + RelationSetLockForWrite(rel); + + TRIGGER_CLEAR_TYPE(tgtype); + if (stmt->before) + TRIGGER_SETT_BEFORE(tgtype); + if (stmt->row) + TRIGGER_SETT_ROW(tgtype); + for (i = 0; i < 3 && stmt->actions[i]; i++) + { + switch (stmt->actions[i]) + { + case 'i': + if (TRIGGER_FOR_INSERT(tgtype)) + elog(WARN, "CreateTrigger: double INSERT event specified"); + TRIGGER_SETT_INSERT(tgtype); + break; + case 'd': + if (TRIGGER_FOR_DELETE(tgtype)) + elog(WARN, "CreateTrigger: double DELETE event specified"); + TRIGGER_SETT_DELETE(tgtype); + break; + case 'u': + if (TRIGGER_FOR_UPDATE(tgtype)) + elog(WARN, "CreateTrigger: double UPDATE event specified"); + TRIGGER_SETT_UPDATE(tgtype); + break; + default: + elog(WARN, "CreateTrigger: unknown event specified"); + break; + } + } + + /* Scan pg_trigger */ + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForWrite(tgrel); + ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + tgscan = heap_beginscan(tgrel, 0, NowTimeQual, 1, &key); + while (tuple = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tuple)) + { + Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + + if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) + elog(WARN, "CreateTrigger: trigger %s already defined on relation %s", + stmt->trigname, stmt->relname); + else + found++; + } + heap_endscan(tgscan); + + memset(fargtypes, 0, 8 * sizeof(Oid)); + tuple = SearchSysCacheTuple(PRONAME, + PointerGetDatum(stmt->funcname), + 0, PointerGetDatum(fargtypes), 0); + if (!HeapTupleIsValid(tuple) || + ((Form_pg_proc) GETSTRUCT(tuple))->prorettype != 0 || + ((Form_pg_proc) GETSTRUCT(tuple))->pronargs != 0) + elog(WARN, "CreateTrigger: function %s () does not exist", stmt->funcname); + + if (((Form_pg_proc) GETSTRUCT(tuple))->prolang != ClanguageId) + elog(WARN, "CreateTrigger: only C functions are supported"); + + memset(nulls, ' ', Natts_pg_trigger * sizeof(char)); + + values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(rel->rd_id); + values[Anum_pg_trigger_tgname - 1] = NameGetDatum(namein(stmt->trigname)); + values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(tuple->t_oid); + values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); + if (stmt->args) + { + List *le; + char *args; + int16 nargs = length(stmt->args); + int len = 0; + + foreach(le, stmt->args) + { + char *ar = (char *) lfirst(le); + + len += strlen(ar) + 4; + } + args = (char *) palloc(len + 1); + args[0] = 0; + foreach(le, stmt->args) + sprintf(args + strlen(args), "%s\\000", (char *) lfirst(le)); + values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs); + values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain(args)); + } + else + { + values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0); + values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain("")); + } + values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr); + + tuple = heap_formtuple(tgrel->rd_att, values, nulls); + heap_insert(tgrel, tuple); + CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple); + CatalogCloseIndices(Num_pg_trigger_indices, idescs); + pfree(tuple); + RelationUnsetLockForWrite(tgrel); + heap_close(tgrel); + + pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); + pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); + + /* update pg_class */ + relrdesc = heap_openr(RelationRelationName); + tuple = ClassNameIndexScan(relrdesc, stmt->relname); + if (!PointerIsValid(tuple)) + { + heap_close(relrdesc); + elog(WARN, "CreateTrigger: relation %s not found in pg_class", stmt->relname); + } + ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; + RelationInvalidateHeapTuple(relrdesc, tuple); + oldTID = tuple->t_ctid; + heap_replace(relrdesc, &oldTID, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); + CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, tuple); + CatalogCloseIndices(Num_pg_class_indices, ridescs); + pfree(tuple); heap_close(relrdesc); - elog(WARN, "CreateTrigger: relation %s not found in pg_class", stmt->relname); - } - ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; - RelationInvalidateHeapTuple (relrdesc, tuple); - oldTID = tuple->t_ctid; - heap_replace (relrdesc, &oldTID, tuple); - CatalogOpenIndices (Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert (ridescs, Num_pg_class_indices, relrdesc, tuple); - CatalogCloseIndices (Num_pg_class_indices, ridescs); - pfree(tuple); - heap_close(relrdesc); - - CommandCounterIncrement (); - oldcxt = MemoryContextSwitchTo ((MemoryContext)CacheCxt); - FreeTriggerDesc (rel); - rel->rd_rel->reltriggers = found + 1; - RelationBuildTriggers (rel); - MemoryContextSwitchTo (oldcxt); - heap_close (rel); - return; + + CommandCounterIncrement(); + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + FreeTriggerDesc(rel); + rel->rd_rel->reltriggers = found + 1; + RelationBuildTriggers(rel); + MemoryContextSwitchTo(oldcxt); + heap_close(rel); + return; } void -DropTrigger (DropTrigStmt *stmt) +DropTrigger(DropTrigStmt * stmt) { - Relation rel; - Relation tgrel; - HeapScanDesc tgscan; - ScanKeyData key; - Relation relrdesc; - HeapTuple tuple; - ItemPointerData oldTID; - Relation ridescs[Num_pg_class_indices]; - MemoryContext oldcxt; - int found = 0; - int tgfound = 0; - + Relation rel; + Relation tgrel; + HeapScanDesc tgscan; + ScanKeyData key; + Relation relrdesc; + HeapTuple tuple; + ItemPointerData oldTID; + Relation ridescs[Num_pg_class_indices]; + MemoryContext oldcxt; + int found = 0; + int tgfound = 0; + #ifndef NO_SECURITY - if ( !pg_ownercheck (GetPgUserName (), stmt->relname, RELNAME)) - elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); + if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME)) + elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); #endif - - rel = heap_openr (stmt->relname); - if ( !RelationIsValid (rel) ) - elog (WARN, "DropTrigger: there is no relation %s", stmt->relname); - - RelationSetLockForWrite (rel); - - tgrel = heap_openr (TriggerRelationName); - RelationSetLockForWrite (tgrel); - ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key); - while (tuple = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tuple)) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple); - if ( namestrcmp (&(pg_trigger->tgname), stmt->trigname) == 0 ) - { - heap_delete (tgrel, &tuple->t_ctid); - tgfound++; - } - else - found++; - } - if ( tgfound == 0 ) - elog (WARN, "DropTrigger: there is no trigger %s on relation %s", - stmt->trigname, stmt->relname); - if ( tgfound > 1 ) - elog (NOTICE, "DropTrigger: found (and deleted) %d trigger %s on relation %s", - tgfound, stmt->trigname, stmt->relname); - heap_endscan (tgscan); - RelationUnsetLockForWrite (tgrel); - heap_close (tgrel); - - /* update pg_class */ - relrdesc = heap_openr (RelationRelationName); - tuple = ClassNameIndexScan (relrdesc, stmt->relname); - if ( !PointerIsValid (tuple) ) - { + + rel = heap_openr(stmt->relname); + if (!RelationIsValid(rel)) + elog(WARN, "DropTrigger: there is no relation %s", stmt->relname); + + RelationSetLockForWrite(rel); + + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForWrite(tgrel); + ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + tgscan = heap_beginscan(tgrel, 0, NowTimeQual, 1, &key); + while (tuple = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tuple)) + { + Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + + if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) + { + heap_delete(tgrel, &tuple->t_ctid); + tgfound++; + } + else + found++; + } + if (tgfound == 0) + elog(WARN, "DropTrigger: there is no trigger %s on relation %s", + stmt->trigname, stmt->relname); + if (tgfound > 1) + elog(NOTICE, "DropTrigger: found (and deleted) %d trigger %s on relation %s", + tgfound, stmt->trigname, stmt->relname); + heap_endscan(tgscan); + RelationUnsetLockForWrite(tgrel); + heap_close(tgrel); + + /* update pg_class */ + relrdesc = heap_openr(RelationRelationName); + tuple = ClassNameIndexScan(relrdesc, stmt->relname); + if (!PointerIsValid(tuple)) + { + heap_close(relrdesc); + elog(WARN, "DropTrigger: relation %s not found in pg_class", stmt->relname); + } + ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found; + RelationInvalidateHeapTuple(relrdesc, tuple); + oldTID = tuple->t_ctid; + heap_replace(relrdesc, &oldTID, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); + CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, tuple); + CatalogCloseIndices(Num_pg_class_indices, ridescs); + pfree(tuple); heap_close(relrdesc); - elog(WARN, "DropTrigger: relation %s not found in pg_class", stmt->relname); - } - ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found; - RelationInvalidateHeapTuple (relrdesc, tuple); - oldTID = tuple->t_ctid; - heap_replace (relrdesc, &oldTID, tuple); - CatalogOpenIndices (Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert (ridescs, Num_pg_class_indices, relrdesc, tuple); - CatalogCloseIndices (Num_pg_class_indices, ridescs); - pfree(tuple); - heap_close(relrdesc); - - CommandCounterIncrement (); - oldcxt = MemoryContextSwitchTo ((MemoryContext)CacheCxt); - FreeTriggerDesc (rel); - rel->rd_rel->reltriggers = found; - if ( found > 0 ) - RelationBuildTriggers (rel); - MemoryContextSwitchTo (oldcxt); - heap_close (rel); - return; + + CommandCounterIncrement(); + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + FreeTriggerDesc(rel); + rel->rd_rel->reltriggers = found; + if (found > 0) + RelationBuildTriggers(rel); + MemoryContextSwitchTo(oldcxt); + heap_close(rel); + return; } -void -RelationRemoveTriggers (Relation rel) +void +RelationRemoveTriggers(Relation rel) { - Relation tgrel; - HeapScanDesc tgscan; - ScanKeyData key; - HeapTuple tup; - - tgrel = heap_openr (TriggerRelationName); - RelationSetLockForWrite (tgrel); - ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - - tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key); - - while (tup = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tup)) - heap_delete (tgrel, &tup->t_ctid); - - heap_endscan (tgscan); - RelationUnsetLockForWrite (tgrel); - heap_close (tgrel); + Relation tgrel; + HeapScanDesc tgscan; + ScanKeyData key; + HeapTuple tup; + + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForWrite(tgrel); + ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + + tgscan = heap_beginscan(tgrel, 0, NowTimeQual, 1, &key); + + while (tup = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tup)) + heap_delete(tgrel, &tup->t_ctid); + + heap_endscan(tgscan); + RelationUnsetLockForWrite(tgrel); + heap_close(tgrel); } void -RelationBuildTriggers (Relation relation) +RelationBuildTriggers(Relation relation) { - TriggerDesc *trigdesc = (TriggerDesc *) palloc (sizeof (TriggerDesc)); - int ntrigs = relation->rd_rel->reltriggers; - Trigger *triggers = NULL; - Trigger *build; - Relation tgrel; - Form_pg_trigger pg_trigger; - Relation irel; - ScanKeyData skey; - HeapTuple tuple; - IndexScanDesc sd; - RetrieveIndexResult indexRes; - Buffer buffer; - ItemPointer iptr; - struct varlena *val; - bool isnull; - int found; - - memset (trigdesc, 0, sizeof (TriggerDesc)); - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relation->rd_id)); - - tgrel = heap_openr(TriggerRelationName); - RelationSetLockForRead (tgrel); - irel = index_openr(TriggerRelidIndex); - sd = index_beginscan(irel, false, 1, &skey); - - for (found = 0; ; ) - { - indexRes = index_getnext(sd, ForwardScanDirection); - if (!indexRes) - break; - - iptr = &indexRes->heap_iptr; - tuple = heap_fetch(tgrel, NowTimeQual, iptr, &buffer); - pfree(indexRes); - if (!HeapTupleIsValid(tuple)) - continue; - if ( found == ntrigs ) - elog (WARN, "RelationBuildTriggers: unexpected record found for rel %.*s", - NAMEDATALEN, relation->rd_rel->relname.data); - - pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple); - - if ( triggers == NULL ) - triggers = (Trigger *) palloc (sizeof (Trigger)); - else - triggers = (Trigger *) repalloc (triggers, (found + 1) * sizeof (Trigger)); - build = &(triggers[found]); - - build->tgname = nameout (&(pg_trigger->tgname)); - build->tgfoid = pg_trigger->tgfoid; - build->tgfunc = NULL; - build->tgtype = pg_trigger->tgtype; - build->tgnargs = pg_trigger->tgnargs; - memcpy (build->tgattr, &(pg_trigger->tgattr), 8 * sizeof (int16)); - val = (struct varlena*) fastgetattr (tuple, - Anum_pg_trigger_tgargs, - tgrel->rd_att, &isnull); - if ( isnull ) - elog (WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", - NAMEDATALEN, relation->rd_rel->relname.data); - if ( build->tgnargs > 0 ) - { - char *p; - int i; - - val = (struct varlena*) fastgetattr (tuple, - Anum_pg_trigger_tgargs, - tgrel->rd_att, &isnull); - if ( isnull ) - elog (WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", - NAMEDATALEN, relation->rd_rel->relname.data); - p = (char *) VARDATA (val); - build->tgargs = (char**) palloc (build->tgnargs * sizeof (char*)); - for (i = 0; i < build->tgnargs; i++) - { - build->tgargs[i] = (char*) palloc (strlen (p) + 1); - strcpy (build->tgargs[i], p); - p += strlen (p) + 1; - } - } - else - build->tgargs = NULL; - - found++; - ReleaseBuffer(buffer); - } - - if ( found < ntrigs ) - elog (WARN, "RelationBuildTriggers: %d record not found for rel %.*s", - ntrigs - found, - NAMEDATALEN, relation->rd_rel->relname.data); - - index_endscan (sd); - pfree (sd); - index_close (irel); - RelationUnsetLockForRead (tgrel); - heap_close (tgrel); - - /* Build trigdesc */ - trigdesc->triggers = triggers; - for (found = 0; found < ntrigs; found++) - { - build = &(triggers[found]); - DescribeTrigger (trigdesc, build); - } - - relation->trigdesc = trigdesc; - + TriggerDesc *trigdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc)); + int ntrigs = relation->rd_rel->reltriggers; + Trigger *triggers = NULL; + Trigger *build; + Relation tgrel; + Form_pg_trigger pg_trigger; + Relation irel; + ScanKeyData skey; + HeapTuple tuple; + IndexScanDesc sd; + RetrieveIndexResult indexRes; + Buffer buffer; + ItemPointer iptr; + struct varlena *val; + bool isnull; + int found; + + memset(trigdesc, 0, sizeof(TriggerDesc)); + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relation->rd_id)); + + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForRead(tgrel); + irel = index_openr(TriggerRelidIndex); + sd = index_beginscan(irel, false, 1, &skey); + + for (found = 0;;) + { + indexRes = index_getnext(sd, ForwardScanDirection); + if (!indexRes) + break; + + iptr = &indexRes->heap_iptr; + tuple = heap_fetch(tgrel, NowTimeQual, iptr, &buffer); + pfree(indexRes); + if (!HeapTupleIsValid(tuple)) + continue; + if (found == ntrigs) + elog(WARN, "RelationBuildTriggers: unexpected record found for rel %.*s", + NAMEDATALEN, relation->rd_rel->relname.data); + + pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + + if (triggers == NULL) + triggers = (Trigger *) palloc(sizeof(Trigger)); + else + triggers = (Trigger *) repalloc(triggers, (found + 1) * sizeof(Trigger)); + build = &(triggers[found]); + + build->tgname = nameout(&(pg_trigger->tgname)); + build->tgfoid = pg_trigger->tgfoid; + build->tgfunc = NULL; + build->tgtype = pg_trigger->tgtype; + build->tgnargs = pg_trigger->tgnargs; + memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16)); + val = (struct varlena *) fastgetattr(tuple, + Anum_pg_trigger_tgargs, + tgrel->rd_att, &isnull); + if (isnull) + elog(WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", + NAMEDATALEN, relation->rd_rel->relname.data); + if (build->tgnargs > 0) + { + char *p; + int i; + + val = (struct varlena *) fastgetattr(tuple, + Anum_pg_trigger_tgargs, + tgrel->rd_att, &isnull); + if (isnull) + elog(WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", + NAMEDATALEN, relation->rd_rel->relname.data); + p = (char *) VARDATA(val); + build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *)); + for (i = 0; i < build->tgnargs; i++) + { + build->tgargs[i] = (char *) palloc(strlen(p) + 1); + strcpy(build->tgargs[i], p); + p += strlen(p) + 1; + } + } + else + build->tgargs = NULL; + + found++; + ReleaseBuffer(buffer); + } + + if (found < ntrigs) + elog(WARN, "RelationBuildTriggers: %d record not found for rel %.*s", + ntrigs - found, + NAMEDATALEN, relation->rd_rel->relname.data); + + index_endscan(sd); + pfree(sd); + index_close(irel); + RelationUnsetLockForRead(tgrel); + heap_close(tgrel); + + /* Build trigdesc */ + trigdesc->triggers = triggers; + for (found = 0; found < ntrigs; found++) + { + build = &(triggers[found]); + DescribeTrigger(trigdesc, build); + } + + relation->trigdesc = trigdesc; + } -void -FreeTriggerDesc (Relation relation) +void +FreeTriggerDesc(Relation relation) { - TriggerDesc *trigdesc = relation->trigdesc; - Trigger ***t; - Trigger *trigger; - int i; - - if ( trigdesc == NULL ) - return; - - t = trigdesc->tg_before_statement; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - t = trigdesc->tg_before_row; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - t = trigdesc->tg_after_row; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - t = trigdesc->tg_after_statement; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - - trigger = trigdesc->triggers; - for (i = 0; i < relation->rd_rel->reltriggers; i++) - { - pfree (trigger->tgname); - if ( trigger->tgnargs > 0 ) - { - while ( --(trigger->tgnargs) >= 0 ) - pfree (trigger->tgargs[trigger->tgnargs]); - pfree (trigger->tgargs); - } - trigger++; - } - pfree (trigdesc->triggers); - pfree (trigdesc); - relation->trigdesc = NULL; - return; + TriggerDesc *trigdesc = relation->trigdesc; + Trigger ***t; + Trigger *trigger; + int i; + + if (trigdesc == NULL) + return; + + t = trigdesc->tg_before_statement; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + t = trigdesc->tg_before_row; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + t = trigdesc->tg_after_row; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + t = trigdesc->tg_after_statement; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + + trigger = trigdesc->triggers; + for (i = 0; i < relation->rd_rel->reltriggers; i++) + { + pfree(trigger->tgname); + if (trigger->tgnargs > 0) + { + while (--(trigger->tgnargs) >= 0) + pfree(trigger->tgargs[trigger->tgnargs]); + pfree(trigger->tgargs); + } + trigger++; + } + pfree(trigdesc->triggers); + pfree(trigdesc); + relation->trigdesc = NULL; + return; } static void -DescribeTrigger (TriggerDesc *trigdesc, Trigger *trigger) +DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger) { - uint16 *n; - Trigger ***t, ***tp; - - if ( TRIGGER_FOR_ROW (trigger->tgtype) ) /* Is ROW/STATEMENT trigger */ - { - if ( TRIGGER_FOR_BEFORE (trigger->tgtype) ) - { - n = trigdesc->n_before_row; - t = trigdesc->tg_before_row; - } - else - { - n = trigdesc->n_after_row; - t = trigdesc->tg_after_row; - } - } - else /* STATEMENT (NI) */ - { - if ( TRIGGER_FOR_BEFORE (trigger->tgtype) ) - { - n = trigdesc->n_before_statement; - t = trigdesc->tg_before_statement; - } - else - { - n = trigdesc->n_after_statement; - t = trigdesc->tg_after_statement; - } - } - - if ( TRIGGER_FOR_INSERT (trigger->tgtype) ) - { - tp = &(t[TRIGGER_EVENT_INSERT]); - if ( *tp == NULL ) - *tp = (Trigger **) palloc (sizeof (Trigger *)); - else - *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_INSERT] + 1) * - sizeof (Trigger *)); - (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger; - (n[TRIGGER_EVENT_INSERT])++; - } - - if ( TRIGGER_FOR_DELETE (trigger->tgtype) ) - { - tp = &(t[TRIGGER_EVENT_DELETE]); - if ( *tp == NULL ) - *tp = (Trigger **) palloc (sizeof (Trigger *)); - else - *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_DELETE] + 1) * - sizeof (Trigger *)); - (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger; - (n[TRIGGER_EVENT_DELETE])++; - } - - if ( TRIGGER_FOR_UPDATE (trigger->tgtype) ) - { - tp = &(t[TRIGGER_EVENT_UPDATE]); - if ( *tp == NULL ) - *tp = (Trigger **) palloc (sizeof (Trigger *)); - else - *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * - sizeof (Trigger *)); - (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger; - (n[TRIGGER_EVENT_UPDATE])++; - } - + uint16 *n; + Trigger ***t, + ***tp; + + if (TRIGGER_FOR_ROW(trigger->tgtype)) /* Is ROW/STATEMENT + * trigger */ + { + if (TRIGGER_FOR_BEFORE(trigger->tgtype)) + { + n = trigdesc->n_before_row; + t = trigdesc->tg_before_row; + } + else + { + n = trigdesc->n_after_row; + t = trigdesc->tg_after_row; + } + } + else +/* STATEMENT (NI) */ + { + if (TRIGGER_FOR_BEFORE(trigger->tgtype)) + { + n = trigdesc->n_before_statement; + t = trigdesc->tg_before_statement; + } + else + { + n = trigdesc->n_after_statement; + t = trigdesc->tg_after_statement; + } + } + + if (TRIGGER_FOR_INSERT(trigger->tgtype)) + { + tp = &(t[TRIGGER_EVENT_INSERT]); + if (*tp == NULL) + *tp = (Trigger **) palloc(sizeof(Trigger *)); + else + *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) * + sizeof(Trigger *)); + (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger; + (n[TRIGGER_EVENT_INSERT])++; + } + + if (TRIGGER_FOR_DELETE(trigger->tgtype)) + { + tp = &(t[TRIGGER_EVENT_DELETE]); + if (*tp == NULL) + *tp = (Trigger **) palloc(sizeof(Trigger *)); + else + *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) * + sizeof(Trigger *)); + (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger; + (n[TRIGGER_EVENT_DELETE])++; + } + + if (TRIGGER_FOR_UPDATE(trigger->tgtype)) + { + tp = &(t[TRIGGER_EVENT_UPDATE]); + if (*tp == NULL) + *tp = (Trigger **) palloc(sizeof(Trigger *)); + else + *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * + sizeof(Trigger *)); + (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger; + (n[TRIGGER_EVENT_UPDATE])++; + } + } -HeapTuple -ExecBRInsertTriggers (Relation rel, HeapTuple tuple) +HeapTuple +ExecBRInsertTriggers(Relation rel, HeapTuple tuple) { - int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; - Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; - HeapTuple newtuple = tuple; - int nargs; - int i; - - CurrentTriggerData = (TriggerData *) palloc (sizeof (TriggerData)); - CurrentTriggerData->tg_event = TRIGGER_EVENT_INSERT|TRIGGER_EVENT_ROW; - CurrentTriggerData->tg_relation = rel; - CurrentTriggerData->tg_newtuple = NULL; - for (i = 0; i < ntrigs; i++) - { - CurrentTriggerData->tg_trigtuple = newtuple; - CurrentTriggerData->tg_trigger = trigger[i]; - if ( trigger[i]->tgfunc == NULL ) - fmgr_info (trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs); - newtuple = (HeapTuple) ( (*(trigger[i]->tgfunc)) () ); - if ( newtuple == NULL ) - break; - } - pfree (CurrentTriggerData); - CurrentTriggerData = NULL; - return (newtuple); + int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; + Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; + HeapTuple newtuple = tuple; + int nargs; + int i; + + CurrentTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); + CurrentTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; + CurrentTriggerData->tg_relation = rel; + CurrentTriggerData->tg_newtuple = NULL; + for (i = 0; i < ntrigs; i++) + { + CurrentTriggerData->tg_trigtuple = newtuple; + CurrentTriggerData->tg_trigger = trigger[i]; + if (trigger[i]->tgfunc == NULL) + fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs); + newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ()); + if (newtuple == NULL) + break; + } + pfree(CurrentTriggerData); + CurrentTriggerData = NULL; + return (newtuple); } void -ExecARInsertTriggers (Relation rel, HeapTuple tuple) +ExecARInsertTriggers(Relation rel, HeapTuple tuple) { - - return; + + return; } bool -ExecBRDeleteTriggers (Relation rel, ItemPointer tupleid) +ExecBRDeleteTriggers(Relation rel, ItemPointer tupleid) { - - return (true); + + return (true); } void -ExecARDeleteTriggers (Relation rel, ItemPointer tupleid) +ExecARDeleteTriggers(Relation rel, ItemPointer tupleid) { - - return; + + return; } HeapTuple -ExecBRUpdateTriggers (Relation rel, ItemPointer tupleid, HeapTuple tuple) +ExecBRUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple tuple) { - - return (tuple); + + return (tuple); } void -ExecARUpdateTriggers (Relation rel, ItemPointer tupleid, HeapTuple tuple) +ExecARUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple tuple) { - - return; + + return; } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 0c480581179..30690f0f32b 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * vacuum.c-- - * the postgres vacuum cleaner + * the postgres vacuum cleaner * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.42 1997/08/22 04:13:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.43 1997/09/07 04:41:02 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -48,2220 +48,2364 @@ #include <storage/bufpage.h> #include "storage/shmem.h" #ifndef HAVE_GETRUSAGE -# include <rusagestub.h> -#else -# include <sys/time.h> -# include <sys/resource.h> -#endif +#include <rusagestub.h> +#else +#include <sys/time.h> +#include <sys/resource.h> +#endif #include <port-protos.h> -bool VacuumRunning = false; +bool VacuumRunning = false; -static Portal vc_portal; +static Portal vc_portal; -static int MESSAGE_LEVEL; /* message level */ +static int MESSAGE_LEVEL; /* message level */ #define swapLong(a,b) {long tmp; tmp=a; a=b; b=tmp;} #define swapInt(a,b) {int tmp; tmp=a; a=b; b=tmp;} #define swapDatum(a,b) {Datum tmp; tmp=a; a=b; b=tmp;} #define VacAttrStatsEqValid(stats) ( stats->f_cmpeq != NULL ) #define VacAttrStatsLtGtValid(stats) ( stats->f_cmplt != NULL && \ - stats->f_cmpgt != NULL && \ - RegProcedureIsValid(stats->outfunc) ) - + stats->f_cmpgt != NULL && \ + RegProcedureIsValid(stats->outfunc) ) + /* non-export function prototypes */ -static void vc_init(void); -static void vc_shutdown(void); -static void vc_vacuum(NameData *VacRelP, bool analyze, List *va_cols); -static VRelList vc_getrels(NameData *VacRelP); -static void vc_vacone (Oid relid, bool analyze, List *va_cols); -static void vc_scanheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl); -static void vc_rpfheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel); -static void vc_vacheap (VRelStats *vacrelstats, Relation onerel, VPageList vpl); -static void vc_vacpage (Page page, VPageDescr vpd, Relation archrel); -static void vc_vaconeind (VPageList vpl, Relation indrel, int nhtups); -static void vc_scanoneind (Relation indrel, int nhtups); -static void vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple htup); -static void vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum *bucket, int16 *bucket_len); -static void vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VRelStats *vacrelstats); -static void vc_delhilowstats (Oid relid, int attcnt, int *attnums); -static void vc_setpagelock(Relation rel, BlockNumber blkno); -static VPageDescr vc_tidreapped (ItemPointer itemptr, VPageList vpl); -static void vc_reappage (VPageList vpl, VPageDescr vpc); -static void vc_vpinsert (VPageList vpl, VPageDescr vpnew); -static void vc_free(VRelList vrl); -static void vc_getindices (Oid relid, int *nindices, Relation **Irel); -static void vc_clsindices (int nindices, Relation *Irel); +static void vc_init(void); +static void vc_shutdown(void); +static void vc_vacuum(NameData * VacRelP, bool analyze, List * va_cols); +static VRelList vc_getrels(NameData * VacRelP); +static void vc_vacone(Oid relid, bool analyze, List * va_cols); +static void vc_scanheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl); +static void vc_rpfheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl, int nindices, Relation * Irel); +static void vc_vacheap(VRelStats * vacrelstats, Relation onerel, VPageList vpl); +static void vc_vacpage(Page page, VPageDescr vpd, Relation archrel); +static void vc_vaconeind(VPageList vpl, Relation indrel, int nhtups); +static void vc_scanoneind(Relation indrel, int nhtups); +static void vc_attrstats(Relation onerel, VRelStats * vacrelstats, HeapTuple htup); +static void vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum * bucket, int16 * bucket_len); +static void vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VRelStats * vacrelstats); +static void vc_delhilowstats(Oid relid, int attcnt, int *attnums); +static void vc_setpagelock(Relation rel, BlockNumber blkno); +static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl); +static void vc_reappage(VPageList vpl, VPageDescr vpc); +static void vc_vpinsert(VPageList vpl, VPageDescr vpnew); +static void vc_free(VRelList vrl); +static void vc_getindices(Oid relid, int *nindices, Relation ** Irel); +static void vc_clsindices(int nindices, Relation * Irel); static Relation vc_getarchrel(Relation heaprel); -static void vc_archive(Relation archrel, HeapTuple htup); -static bool vc_isarchrel(char *rname); -static void vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc); -static char * vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *)); -static int vc_cmp_blk (char *left, char *right); -static int vc_cmp_offno (char *left, char *right); -static bool vc_enough_space (VPageDescr vpd, Size len); +static void vc_archive(Relation archrel, HeapTuple htup); +static bool vc_isarchrel(char *rname); +static void vc_mkindesc(Relation onerel, int nindices, Relation * Irel, IndDesc ** Idesc); +static char *vc_find_eq(char *bot, int nelem, int size, char *elm, int (*compar) (char *, char *)); +static int vc_cmp_blk(char *left, char *right); +static int vc_cmp_offno(char *left, char *right); +static bool vc_enough_space(VPageDescr vpd, Size len); void -vacuum(char *vacrel, bool verbose, bool analyze, List *va_spec) +vacuum(char *vacrel, bool verbose, bool analyze, List * va_spec) { - char *pname; - MemoryContext old; - PortalVariableMemory pmem; - NameData VacRel; - List *le; - List *va_cols = NIL; - - /* - * Create a portal for safe memory across transctions. We need to - * palloc the name space for it because our hash function expects - * the name to be on a longword boundary. CreatePortal copies the - * name to safe storage for us. - */ - pname = (char *) palloc(strlen(VACPNAME) + 1); - strcpy(pname, VACPNAME); - vc_portal = CreatePortal(pname); - pfree(pname); - - if (verbose) - MESSAGE_LEVEL = NOTICE; - else - MESSAGE_LEVEL = DEBUG; - - /* vacrel gets de-allocated on transaction commit */ - if (vacrel) - strcpy(VacRel.data,vacrel); - - pmem = PortalGetVariableMemory(vc_portal); - old = MemoryContextSwitchTo((MemoryContext)pmem); - - Assert ( va_spec == NIL || analyze ); - foreach (le, va_spec) - { - char *col = (char*)lfirst(le); - char *dest; - - dest = (char*) palloc (strlen (col) + 1); - strcpy (dest, col); - va_cols = lappend (va_cols, dest); - } - MemoryContextSwitchTo(old); - - /* initialize vacuum cleaner */ - vc_init(); - - /* vacuum the database */ - if (vacrel) - vc_vacuum (&VacRel, analyze, va_cols); - else - vc_vacuum (NULL, analyze, NIL); - - PortalDestroy (&vc_portal); - - /* clean up */ - vc_shutdown(); + char *pname; + MemoryContext old; + PortalVariableMemory pmem; + NameData VacRel; + List *le; + List *va_cols = NIL; + + /* + * Create a portal for safe memory across transctions. We need to + * palloc the name space for it because our hash function expects the + * name to be on a longword boundary. CreatePortal copies the name to + * safe storage for us. + */ + pname = (char *) palloc(strlen(VACPNAME) + 1); + strcpy(pname, VACPNAME); + vc_portal = CreatePortal(pname); + pfree(pname); + + if (verbose) + MESSAGE_LEVEL = NOTICE; + else + MESSAGE_LEVEL = DEBUG; + + /* vacrel gets de-allocated on transaction commit */ + if (vacrel) + strcpy(VacRel.data, vacrel); + + pmem = PortalGetVariableMemory(vc_portal); + old = MemoryContextSwitchTo((MemoryContext) pmem); + + Assert(va_spec == NIL || analyze); + foreach(le, va_spec) + { + char *col = (char *) lfirst(le); + char *dest; + + dest = (char *) palloc(strlen(col) + 1); + strcpy(dest, col); + va_cols = lappend(va_cols, dest); + } + MemoryContextSwitchTo(old); + + /* initialize vacuum cleaner */ + vc_init(); + + /* vacuum the database */ + if (vacrel) + vc_vacuum(&VacRel, analyze, va_cols); + else + vc_vacuum(NULL, analyze, NIL); + + PortalDestroy(&vc_portal); + + /* clean up */ + vc_shutdown(); } /* - * vc_init(), vc_shutdown() -- start up and shut down the vacuum cleaner. + * vc_init(), vc_shutdown() -- start up and shut down the vacuum cleaner. * - * We run exactly one vacuum cleaner at a time. We use the file system - * to guarantee an exclusive lock on vacuuming, since a single vacuum - * cleaner instantiation crosses transaction boundaries, and we'd lose - * postgres-style locks at the end of every transaction. + * We run exactly one vacuum cleaner at a time. We use the file system + * to guarantee an exclusive lock on vacuuming, since a single vacuum + * cleaner instantiation crosses transaction boundaries, and we'd lose + * postgres-style locks at the end of every transaction. * - * The strangeness with committing and starting transactions in the - * init and shutdown routines is due to the fact that the vacuum cleaner - * is invoked via a sql command, and so is already executing inside - * a transaction. We need to leave ourselves in a predictable state - * on entry and exit to the vacuum cleaner. We commit the transaction - * started in PostgresMain() inside vc_init(), and start one in - * vc_shutdown() to match the commit waiting for us back in - * PostgresMain(). + * The strangeness with committing and starting transactions in the + * init and shutdown routines is due to the fact that the vacuum cleaner + * is invoked via a sql command, and so is already executing inside + * a transaction. We need to leave ourselves in a predictable state + * on entry and exit to the vacuum cleaner. We commit the transaction + * started in PostgresMain() inside vc_init(), and start one in + * vc_shutdown() to match the commit waiting for us back in + * PostgresMain(). */ static void vc_init() { - int fd; + int fd; - if ((fd = open("pg_vlock", O_CREAT|O_EXCL, 0600)) < 0) - elog(WARN, "can't create lock file -- another vacuum cleaner running?"); + if ((fd = open("pg_vlock", O_CREAT | O_EXCL, 0600)) < 0) + elog(WARN, "can't create lock file -- another vacuum cleaner running?"); - close(fd); + close(fd); - /* - * By here, exclusive open on the lock file succeeded. If we abort - * for any reason during vacuuming, we need to remove the lock file. - * This global variable is checked in the transaction manager on xact - * abort, and the routine vc_abort() is called if necessary. - */ + /* + * By here, exclusive open on the lock file succeeded. If we abort + * for any reason during vacuuming, we need to remove the lock file. + * This global variable is checked in the transaction manager on xact + * abort, and the routine vc_abort() is called if necessary. + */ - VacuumRunning = true; + VacuumRunning = true; - /* matches the StartTransaction in PostgresMain() */ - CommitTransactionCommand(); + /* matches the StartTransaction in PostgresMain() */ + CommitTransactionCommand(); } static void vc_shutdown() { - /* on entry, not in a transaction */ - if (unlink("pg_vlock") < 0) - elog(WARN, "vacuum: can't destroy lock file!"); + /* on entry, not in a transaction */ + if (unlink("pg_vlock") < 0) + elog(WARN, "vacuum: can't destroy lock file!"); - /* okay, we're done */ - VacuumRunning = false; + /* okay, we're done */ + VacuumRunning = false; - /* matches the CommitTransaction in PostgresMain() */ - StartTransactionCommand(); + /* matches the CommitTransaction in PostgresMain() */ + StartTransactionCommand(); } void vc_abort() { - /* on abort, remove the vacuum cleaner lock file */ - unlink("pg_vlock"); + /* on abort, remove the vacuum cleaner lock file */ + unlink("pg_vlock"); - VacuumRunning = false; + VacuumRunning = false; } /* - * vc_vacuum() -- vacuum the database. + * vc_vacuum() -- vacuum the database. * - * This routine builds a list of relations to vacuum, and then calls - * code that vacuums them one at a time. We are careful to vacuum each - * relation in a separate transaction in order to avoid holding too many - * locks at one time. + * This routine builds a list of relations to vacuum, and then calls + * code that vacuums them one at a time. We are careful to vacuum each + * relation in a separate transaction in order to avoid holding too many + * locks at one time. */ static void -vc_vacuum(NameData *VacRelP, bool analyze, List *va_cols) +vc_vacuum(NameData * VacRelP, bool analyze, List * va_cols) { - VRelList vrl, cur; + VRelList vrl, + cur; + + /* get list of relations */ + vrl = vc_getrels(VacRelP); - /* get list of relations */ - vrl = vc_getrels(VacRelP); + if (analyze && VacRelP == NULL && vrl != NULL) + vc_delhilowstats(InvalidOid, 0, NULL); - if ( analyze && VacRelP == NULL && vrl != NULL ) - vc_delhilowstats (InvalidOid, 0, NULL); - - /* vacuum each heap relation */ - for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) - vc_vacone (cur->vrl_relid, analyze, va_cols); + /* vacuum each heap relation */ + for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) + vc_vacone(cur->vrl_relid, analyze, va_cols); - vc_free(vrl); + vc_free(vrl); } -static VRelList -vc_getrels(NameData *VacRelP) +static VRelList +vc_getrels(NameData * VacRelP) { - Relation pgclass; - TupleDesc pgcdesc; - HeapScanDesc pgcscan; - HeapTuple pgctup; - Buffer buf; - PortalVariableMemory portalmem; - MemoryContext old; - VRelList vrl, cur; - Datum d; - char *rname; - char rkind; - int16 smgrno; - bool n; - ScanKeyData pgckey; - bool found = false; - - StartTransactionCommand(); - - if (VacRelP->data) { - ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname, - NameEqualRegProcedure, - PointerGetDatum(VacRelP->data)); - } else { - ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind, - CharacterEqualRegProcedure, CharGetDatum('r')); - } - - portalmem = PortalGetVariableMemory(vc_portal); - vrl = cur = (VRelList) NULL; - - pgclass = heap_openr(RelationRelationName); - pgcdesc = RelationGetTupleDescriptor(pgclass); - - pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey); - - while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &buf))) { - - found = true; - - /* - * We have to be careful not to vacuum the archive (since it - * already contains vacuumed tuples), and not to vacuum - * relations on write-once storage managers like the Sony - * jukebox at Berkeley. - */ + Relation pgclass; + TupleDesc pgcdesc; + HeapScanDesc pgcscan; + HeapTuple pgctup; + Buffer buf; + PortalVariableMemory portalmem; + MemoryContext old; + VRelList vrl, + cur; + Datum d; + char *rname; + char rkind; + int16 smgrno; + bool n; + ScanKeyData pgckey; + bool found = false; + + StartTransactionCommand(); + + if (VacRelP->data) + { + ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname, + NameEqualRegProcedure, + PointerGetDatum(VacRelP->data)); + } + else + { + ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind, + CharacterEqualRegProcedure, CharGetDatum('r')); + } - d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname, - pgcdesc, &n); - rname = (char*)d; + portalmem = PortalGetVariableMemory(vc_portal); + vrl = cur = (VRelList) NULL; - /* skip archive relations */ - if (vc_isarchrel(rname)) { - ReleaseBuffer(buf); - continue; - } + pgclass = heap_openr(RelationRelationName); + pgcdesc = RelationGetTupleDescriptor(pgclass); + + pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey); - /* don't vacuum large objects for now - something breaks when we do */ - if ( (strlen(rname) >= 5) && rname[0] == 'x' && - rname[1] == 'i' && rname[2] == 'n' && - (rname[3] == 'v' || rname[3] == 'x') && - rname[4] >= '0' && rname[4] <= '9') + while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &buf))) { - elog (NOTICE, "Rel %s: can't vacuum LargeObjects now", - rname); - ReleaseBuffer(buf); - continue; - } - d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relsmgr, - pgcdesc, &n); - smgrno = DatumGetInt16(d); + found = true; - /* skip write-once storage managers */ - if (smgriswo(smgrno)) { - ReleaseBuffer(buf); - continue; - } + /* + * We have to be careful not to vacuum the archive (since it + * already contains vacuumed tuples), and not to vacuum relations + * on write-once storage managers like the Sony jukebox at + * Berkeley. + */ - d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relkind, - pgcdesc, &n); + d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname, + pgcdesc, &n); + rname = (char *) d; - rkind = DatumGetChar(d); + /* skip archive relations */ + if (vc_isarchrel(rname)) + { + ReleaseBuffer(buf); + continue; + } - /* skip system relations */ - if (rkind != 'r') { - ReleaseBuffer(buf); - elog(NOTICE, "Vacuum: can not process index and certain system tables" ); - continue; - } - - /* get a relation list entry for this guy */ - old = MemoryContextSwitchTo((MemoryContext)portalmem); - if (vrl == (VRelList) NULL) { - vrl = cur = (VRelList) palloc(sizeof(VRelListData)); - } else { - cur->vrl_next = (VRelList) palloc(sizeof(VRelListData)); - cur = cur->vrl_next; - } - MemoryContextSwitchTo(old); + /* + * don't vacuum large objects for now - something breaks when we + * do + */ + if ((strlen(rname) >= 5) && rname[0] == 'x' && + rname[1] == 'i' && rname[2] == 'n' && + (rname[3] == 'v' || rname[3] == 'x') && + rname[4] >= '0' && rname[4] <= '9') + { + elog(NOTICE, "Rel %s: can't vacuum LargeObjects now", + rname); + ReleaseBuffer(buf); + continue; + } + + d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relsmgr, + pgcdesc, &n); + smgrno = DatumGetInt16(d); + + /* skip write-once storage managers */ + if (smgriswo(smgrno)) + { + ReleaseBuffer(buf); + continue; + } + + d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relkind, + pgcdesc, &n); + + rkind = DatumGetChar(d); + + /* skip system relations */ + if (rkind != 'r') + { + ReleaseBuffer(buf); + elog(NOTICE, "Vacuum: can not process index and certain system tables"); + continue; + } - cur->vrl_relid = pgctup->t_oid; - cur->vrl_next = (VRelList) NULL; + /* get a relation list entry for this guy */ + old = MemoryContextSwitchTo((MemoryContext) portalmem); + if (vrl == (VRelList) NULL) + { + vrl = cur = (VRelList) palloc(sizeof(VRelListData)); + } + else + { + cur->vrl_next = (VRelList) palloc(sizeof(VRelListData)); + cur = cur->vrl_next; + } + MemoryContextSwitchTo(old); - /* wei hates it if you forget to do this */ - ReleaseBuffer(buf); - } - if (found == false) - elog(NOTICE, "Vacuum: table not found" ); + cur->vrl_relid = pgctup->t_oid; + cur->vrl_next = (VRelList) NULL; - - heap_endscan(pgcscan); - heap_close(pgclass); + /* wei hates it if you forget to do this */ + ReleaseBuffer(buf); + } + if (found == false) + elog(NOTICE, "Vacuum: table not found"); + + + heap_endscan(pgcscan); + heap_close(pgclass); - CommitTransactionCommand(); + CommitTransactionCommand(); - return (vrl); + return (vrl); } /* - * vc_vacone() -- vacuum one heap relation + * vc_vacone() -- vacuum one heap relation * - * This routine vacuums a single heap, cleans out its indices, and - * updates its statistics npages and ntups statistics. + * This routine vacuums a single heap, cleans out its indices, and + * updates its statistics npages and ntups statistics. * - * Doing one heap at a time incurs extra overhead, since we need to - * check that the heap exists again just before we vacuum it. The - * reason that we do this is so that vacuuming can be spread across - * many small transactions. Otherwise, two-phase locking would require - * us to lock the entire database during one pass of the vacuum cleaner. + * Doing one heap at a time incurs extra overhead, since we need to + * check that the heap exists again just before we vacuum it. The + * reason that we do this is so that vacuuming can be spread across + * many small transactions. Otherwise, two-phase locking would require + * us to lock the entire database during one pass of the vacuum cleaner. */ static void -vc_vacone (Oid relid, bool analyze, List *va_cols) +vc_vacone(Oid relid, bool analyze, List * va_cols) { - Relation pgclass; - TupleDesc pgcdesc; - HeapTuple pgctup, pgttup; - Buffer pgcbuf; - HeapScanDesc pgcscan; - Relation onerel; - ScanKeyData pgckey; - VPageListData Vvpl; /* List of pages to vacuum and/or clean indices */ - VPageListData Fvpl; /* List of pages with space enough for re-using */ - VPageDescr *vpp; - Relation *Irel; - int32 nindices, i; - VRelStats *vacrelstats; - - StartTransactionCommand(); - - ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relid)); - - pgclass = heap_openr(RelationRelationName); - pgcdesc = RelationGetTupleDescriptor(pgclass); - pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey); - - /* - * Race condition -- if the pg_class tuple has gone away since the - * last time we saw it, we don't need to vacuum it. - */ - - if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &pgcbuf))) { + Relation pgclass; + TupleDesc pgcdesc; + HeapTuple pgctup, + pgttup; + Buffer pgcbuf; + HeapScanDesc pgcscan; + Relation onerel; + ScanKeyData pgckey; + VPageListData Vvpl; /* List of pages to vacuum and/or clean + * indices */ + VPageListData Fvpl; /* List of pages with space enough for + * re-using */ + VPageDescr *vpp; + Relation *Irel; + int32 nindices, + i; + VRelStats *vacrelstats; + + StartTransactionCommand(); + + ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + + pgclass = heap_openr(RelationRelationName); + pgcdesc = RelationGetTupleDescriptor(pgclass); + pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey); + + /* + * Race condition -- if the pg_class tuple has gone away since the + * last time we saw it, we don't need to vacuum it. + */ + + if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &pgcbuf))) + { + heap_endscan(pgcscan); + heap_close(pgclass); + CommitTransactionCommand(); + return; + } + + /* now open the class and vacuum it */ + onerel = heap_open(relid); + + vacrelstats = (VRelStats *) palloc(sizeof(VRelStats)); + vacrelstats->relid = relid; + vacrelstats->npages = vacrelstats->ntups = 0; + vacrelstats->hasindex = false; + if (analyze && !IsSystemRelationName((RelationGetRelationName(onerel))->data)) + { + int attr_cnt, + *attnums = NULL; + AttributeTupleForm *attr; + + attr_cnt = onerel->rd_att->natts; + attr = onerel->rd_att->attrs; + + if (va_cols != NIL) + { + int tcnt = 0; + List *le; + + if (length(va_cols) > attr_cnt) + elog(WARN, "vacuum: too many attributes specified for relation %s", + (RelationGetRelationName(onerel))->data); + attnums = (int *) palloc(attr_cnt * sizeof(int)); + foreach(le, va_cols) + { + char *col = (char *) lfirst(le); + + for (i = 0; i < attr_cnt; i++) + { + if (namestrcmp(&(attr[i]->attname), col) == 0) + break; + } + if (i < attr_cnt) /* found */ + attnums[tcnt++] = i; + else + { + elog(WARN, "vacuum: there is no attribute %s in %s", + col, (RelationGetRelationName(onerel))->data); + } + } + attr_cnt = tcnt; + } + + vacrelstats->vacattrstats = + (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats)); + + for (i = 0; i < attr_cnt; i++) + { + Operator func_operator; + OperatorTupleForm pgopform; + VacAttrStats *stats; + + stats = &vacrelstats->vacattrstats[i]; + stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); + memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); + stats->best = stats->guess1 = stats->guess2 = 0; + stats->max = stats->min = 0; + stats->best_len = stats->guess1_len = stats->guess2_len = 0; + stats->max_len = stats->min_len = 0; + stats->initialized = false; + stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; + stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; + + func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + int nargs; + + pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmpeq), &nargs); + } + else + stats->f_cmpeq = NULL; + + func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + int nargs; + + pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmplt), &nargs); + } + else + stats->f_cmplt = NULL; + + func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + int nargs; + + pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmpgt), &nargs); + } + else + stats->f_cmpgt = NULL; + + pgttup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(stats->attr->atttypid), + 0, 0, 0); + if (HeapTupleIsValid(pgttup)) + stats->outfunc = ((TypeTupleForm) GETSTRUCT(pgttup))->typoutput; + else + stats->outfunc = InvalidOid; + } + vacrelstats->va_natts = attr_cnt; + vc_delhilowstats(relid, ((attnums) ? attr_cnt : 0), attnums); + if (attnums) + pfree(attnums); + } + else + { + vacrelstats->va_natts = 0; + vacrelstats->vacattrstats = (VacAttrStats *) NULL; + } + + /* we require the relation to be locked until the indices are cleaned */ + RelationSetLockForWrite(onerel); + + /* scan it */ + Vvpl.vpl_npages = Fvpl.vpl_npages = 0; + vc_scanheap(vacrelstats, onerel, &Vvpl, &Fvpl); + + /* Now open indices */ + Irel = (Relation *) NULL; + vc_getindices(vacrelstats->relid, &nindices, &Irel); + + if (nindices > 0) + vacrelstats->hasindex = true; + else + vacrelstats->hasindex = false; + + /* Clean/scan index relation(s) */ + if (Irel != (Relation *) NULL) + { + if (Vvpl.vpl_npages > 0) + { + for (i = 0; i < nindices; i++) + vc_vaconeind(&Vvpl, Irel[i], vacrelstats->ntups); + } + else +/* just scan indices to update statistic */ + { + for (i = 0; i < nindices; i++) + vc_scanoneind(Irel[i], vacrelstats->ntups); + } + } + + if (Fvpl.vpl_npages > 0) /* Try to shrink heap */ + vc_rpfheap(vacrelstats, onerel, &Vvpl, &Fvpl, nindices, Irel); + else + { + if (Irel != (Relation *) NULL) + vc_clsindices(nindices, Irel); + if (Vvpl.vpl_npages > 0)/* Clean pages from Vvpl list */ + vc_vacheap(vacrelstats, onerel, &Vvpl); + } + + /* ok - free Vvpl list of reapped pages */ + if (Vvpl.vpl_npages > 0) + { + vpp = Vvpl.vpl_pgdesc; + for (i = 0; i < Vvpl.vpl_npages; i++, vpp++) + pfree(*vpp); + pfree(Vvpl.vpl_pgdesc); + if (Fvpl.vpl_npages > 0) + pfree(Fvpl.vpl_pgdesc); + } + + /* all done with this class */ + heap_close(onerel); heap_endscan(pgcscan); heap_close(pgclass); - CommitTransactionCommand(); - return; - } - - /* now open the class and vacuum it */ - onerel = heap_open(relid); - - vacrelstats = (VRelStats *) palloc(sizeof(VRelStats)); - vacrelstats->relid = relid; - vacrelstats->npages = vacrelstats->ntups = 0; - vacrelstats->hasindex = false; - if ( analyze && !IsSystemRelationName ((RelationGetRelationName (onerel))->data) ) - { - int attr_cnt, *attnums = NULL; - AttributeTupleForm *attr; - - attr_cnt = onerel->rd_att->natts; - attr = onerel->rd_att->attrs; - - if ( va_cols != NIL ) - { - int tcnt = 0; - List *le; - - if ( length (va_cols) > attr_cnt ) - elog (WARN, "vacuum: too many attributes specified for relation %s", - (RelationGetRelationName(onerel))->data); - attnums = (int*) palloc (attr_cnt * sizeof (int)); - foreach (le, va_cols) - { - char *col = (char*) lfirst(le); - - for (i = 0; i < attr_cnt; i++) - { - if ( namestrcmp (&(attr[i]->attname), col) == 0 ) - break; - } - if ( i < attr_cnt ) /* found */ - attnums[tcnt++] = i; - else - { - elog (WARN, "vacuum: there is no attribute %s in %s", - col, (RelationGetRelationName(onerel))->data); - } - } - attr_cnt = tcnt; - } - - vacrelstats->vacattrstats = - (VacAttrStats *) palloc (attr_cnt * sizeof(VacAttrStats)); - - for (i = 0; i < attr_cnt; i++) - { - Operator func_operator; - OperatorTupleForm pgopform; - VacAttrStats *stats; - - stats = &vacrelstats->vacattrstats[i]; - stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); - memmove (stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); - stats->best = stats->guess1 = stats->guess2 = 0; - stats->max = stats->min = 0; - stats->best_len = stats->guess1_len = stats->guess2_len = 0; - stats->max_len = stats->min_len = 0; - stats->initialized = false; - stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; - stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; - - func_operator = oper("=",stats->attr->atttypid,stats->attr->atttypid,true); - if (func_operator != NULL) - { - int nargs; - - pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); - fmgr_info (pgopform->oprcode, &(stats->f_cmpeq), &nargs); - } - else - stats->f_cmpeq = NULL; - - func_operator = oper("<",stats->attr->atttypid,stats->attr->atttypid,true); - if (func_operator != NULL) - { - int nargs; - - pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); - fmgr_info (pgopform->oprcode, &(stats->f_cmplt), &nargs); - } - else - stats->f_cmplt = NULL; - - func_operator = oper(">",stats->attr->atttypid,stats->attr->atttypid,true); - if (func_operator != NULL) - { - int nargs; - - pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); - fmgr_info (pgopform->oprcode, &(stats->f_cmpgt), &nargs); - } - else - stats->f_cmpgt = NULL; - - pgttup = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(stats->attr->atttypid), - 0,0,0); - if (HeapTupleIsValid(pgttup)) - stats->outfunc = ((TypeTupleForm) GETSTRUCT(pgttup))->typoutput; - else - stats->outfunc = InvalidOid; - } - vacrelstats->va_natts = attr_cnt; - vc_delhilowstats (relid, ((attnums) ? attr_cnt : 0), attnums); - if ( attnums ) - pfree (attnums); - } - else - { - vacrelstats->va_natts = 0; - vacrelstats->vacattrstats = (VacAttrStats *) NULL; - } - - /* we require the relation to be locked until the indices are cleaned */ - RelationSetLockForWrite(onerel); - - /* scan it */ - Vvpl.vpl_npages = Fvpl.vpl_npages = 0; - vc_scanheap(vacrelstats, onerel, &Vvpl, &Fvpl); - - /* Now open indices */ - Irel = (Relation *) NULL; - vc_getindices(vacrelstats->relid, &nindices, &Irel); - - if ( nindices > 0 ) - vacrelstats->hasindex = true; - else - vacrelstats->hasindex = false; - /* Clean/scan index relation(s) */ - if ( Irel != (Relation*) NULL ) - { - if ( Vvpl.vpl_npages > 0 ) - { - for (i = 0; i < nindices; i++) - vc_vaconeind (&Vvpl, Irel[i], vacrelstats->ntups); - } - else /* just scan indices to update statistic */ - { - for (i = 0; i < nindices; i++) - vc_scanoneind (Irel[i], vacrelstats->ntups); - } - } - - if ( Fvpl.vpl_npages > 0 ) /* Try to shrink heap */ - vc_rpfheap (vacrelstats, onerel, &Vvpl, &Fvpl, nindices, Irel); - else - { - if ( Irel != (Relation*) NULL ) - vc_clsindices (nindices, Irel); - if ( Vvpl.vpl_npages > 0 ) /* Clean pages from Vvpl list */ - vc_vacheap (vacrelstats, onerel, &Vvpl); - } - - /* ok - free Vvpl list of reapped pages */ - if ( Vvpl.vpl_npages > 0 ) - { - vpp = Vvpl.vpl_pgdesc; - for (i = 0; i < Vvpl.vpl_npages; i++, vpp++) - pfree(*vpp); - pfree (Vvpl.vpl_pgdesc); - if ( Fvpl.vpl_npages > 0 ) - pfree (Fvpl.vpl_pgdesc); - } - - /* all done with this class */ - heap_close(onerel); - heap_endscan(pgcscan); - heap_close(pgclass); - - /* update statistics in pg_class */ - vc_updstats(vacrelstats->relid, vacrelstats->npages, vacrelstats->ntups, - vacrelstats->hasindex, vacrelstats); - - /* next command frees attribute stats */ - - CommitTransactionCommand(); + /* update statistics in pg_class */ + vc_updstats(vacrelstats->relid, vacrelstats->npages, vacrelstats->ntups, + vacrelstats->hasindex, vacrelstats); + + /* next command frees attribute stats */ + + CommitTransactionCommand(); } /* - * vc_scanheap() -- scan an open heap relation + * vc_scanheap() -- scan an open heap relation * - * This routine sets commit times, constructs Vvpl list of - * empty/uninitialized pages and pages with dead tuples and - * ~LP_USED line pointers, constructs Fvpl list of pages - * appropriate for purposes of shrinking and maintains statistics - * on the number of live tuples in a heap. + * This routine sets commit times, constructs Vvpl list of + * empty/uninitialized pages and pages with dead tuples and + * ~LP_USED line pointers, constructs Fvpl list of pages + * appropriate for purposes of shrinking and maintains statistics + * on the number of live tuples in a heap. */ static void -vc_scanheap (VRelStats *vacrelstats, Relation onerel, +vc_scanheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl) { - int nblocks, blkno; - ItemId itemid; - ItemPointer itemptr; - HeapTuple htup; - Buffer buf; - Page page, tempPage = NULL; - OffsetNumber offnum, maxoff; - bool pgchanged, tupgone, dobufrel, notup; - char *relname; - VPageDescr vpc, vp; - uint32 nvac, ntups, nunused, ncrash, nempg, nnepg, nchpg, nemend; - Size frsize, frsusf; - Size min_tlen = MAXTUPLEN; - Size max_tlen = 0; - int32 i/*, attr_cnt*/; - struct rusage ru0, ru1; - bool do_shrinking = true; - - getrusage(RUSAGE_SELF, &ru0); - - nvac = ntups = nunused = ncrash = nempg = nnepg = nchpg = nemend = 0; - frsize = frsusf = 0; - - relname = (RelationGetRelationName(onerel))->data; - - nblocks = RelationGetNumberOfBlocks(onerel); - - vpc = (VPageDescr) palloc (sizeof(VPageDescrData) + MaxOffsetNumber*sizeof(OffsetNumber)); - vpc->vpd_nusd = 0; - - for (blkno = 0; blkno < nblocks; blkno++) { - buf = ReadBuffer(onerel, blkno); - page = BufferGetPage(buf); - vpc->vpd_blkno = blkno; - vpc->vpd_noff = 0; - - if (PageIsNew(page)) { - elog (NOTICE, "Rel %s: Uninitialized page %u - fixing", - relname, blkno); - PageInit (page, BufferGetPageSize (buf), 0); - vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower; - frsize += (vpc->vpd_free - sizeof (ItemIdData)); - nnepg++; - nemend++; - vc_reappage (Vvpl, vpc); - WriteBuffer(buf); - continue; - } - - if (PageIsEmpty(page)) { - vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower; - frsize += (vpc->vpd_free - sizeof (ItemIdData)); - nempg++; - nemend++; - vc_reappage (Vvpl, vpc); - ReleaseBuffer(buf); - continue; - } + int nblocks, + blkno; + ItemId itemid; + ItemPointer itemptr; + HeapTuple htup; + Buffer buf; + Page page, + tempPage = NULL; + OffsetNumber offnum, + maxoff; + bool pgchanged, + tupgone, + dobufrel, + notup; + char *relname; + VPageDescr vpc, + vp; + uint32 nvac, + ntups, + nunused, + ncrash, + nempg, + nnepg, + nchpg, + nemend; + Size frsize, + frsusf; + Size min_tlen = MAXTUPLEN; + Size max_tlen = 0; + int32 i /* , attr_cnt */ ; + struct rusage ru0, + ru1; + bool do_shrinking = true; + + getrusage(RUSAGE_SELF, &ru0); + + nvac = ntups = nunused = ncrash = nempg = nnepg = nchpg = nemend = 0; + frsize = frsusf = 0; + + relname = (RelationGetRelationName(onerel))->data; + + nblocks = RelationGetNumberOfBlocks(onerel); + + vpc = (VPageDescr) palloc(sizeof(VPageDescrData) + MaxOffsetNumber * sizeof(OffsetNumber)); + vpc->vpd_nusd = 0; + + for (blkno = 0; blkno < nblocks; blkno++) + { + buf = ReadBuffer(onerel, blkno); + page = BufferGetPage(buf); + vpc->vpd_blkno = blkno; + vpc->vpd_noff = 0; - pgchanged = false; - notup = true; - maxoff = PageGetMaxOffsetNumber(page); - for (offnum = FirstOffsetNumber; - offnum <= maxoff; - offnum = OffsetNumberNext(offnum)) { - itemid = PageGetItemId(page, offnum); - - /* - * Collect un-used items too - it's possible to have - * indices pointing here after crash. - */ - if (!ItemIdIsUsed(itemid)) { - vpc->vpd_voff[vpc->vpd_noff++] = offnum; - nunused++; - continue; - } - - htup = (HeapTuple) PageGetItem(page, itemid); - tupgone = false; - - if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) && - TransactionIdIsValid((TransactionId)htup->t_xmin)) { - - if (TransactionIdDidAbort(htup->t_xmin)) { - tupgone = true; - } else if (TransactionIdDidCommit(htup->t_xmin)) { - htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin); - pgchanged = true; - } else if ( !TransactionIdIsInProgress (htup->t_xmin) ) { - /* - * Not Aborted, Not Committed, Not in Progress - - * so it from crashed process. - vadim 11/26/96 - */ - ncrash++; - tupgone = true; + if (PageIsNew(page)) + { + elog(NOTICE, "Rel %s: Uninitialized page %u - fixing", + relname, blkno); + PageInit(page, BufferGetPageSize(buf), 0); + vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower; + frsize += (vpc->vpd_free - sizeof(ItemIdData)); + nnepg++; + nemend++; + vc_reappage(Vvpl, vpc); + WriteBuffer(buf); + continue; } - else + + if (PageIsEmpty(page)) { - elog (NOTICE, "Rel %s: TID %u/%u: InsertTransactionInProgress %u - can't shrink relation", - relname, blkno, offnum, htup->t_xmin); - do_shrinking = false; + vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower; + frsize += (vpc->vpd_free - sizeof(ItemIdData)); + nempg++; + nemend++; + vc_reappage(Vvpl, vpc); + ReleaseBuffer(buf); + continue; } - } - if (TransactionIdIsValid((TransactionId)htup->t_xmax)) - { - if (TransactionIdDidAbort(htup->t_xmax)) + pgchanged = false; + notup = true; + maxoff = PageGetMaxOffsetNumber(page); + for (offnum = FirstOffsetNumber; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) { - StoreInvalidTransactionId(&(htup->t_xmax)); - pgchanged = true; + itemid = PageGetItemId(page, offnum); + + /* + * Collect un-used items too - it's possible to have indices + * pointing here after crash. + */ + if (!ItemIdIsUsed(itemid)) + { + vpc->vpd_voff[vpc->vpd_noff++] = offnum; + nunused++; + continue; + } + + htup = (HeapTuple) PageGetItem(page, itemid); + tupgone = false; + + if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) && + TransactionIdIsValid((TransactionId) htup->t_xmin)) + { + + if (TransactionIdDidAbort(htup->t_xmin)) + { + tupgone = true; + } + else if (TransactionIdDidCommit(htup->t_xmin)) + { + htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin); + pgchanged = true; + } + else if (!TransactionIdIsInProgress(htup->t_xmin)) + { + + /* + * Not Aborted, Not Committed, Not in Progress - so it + * from crashed process. - vadim 11/26/96 + */ + ncrash++; + tupgone = true; + } + else + { + elog(NOTICE, "Rel %s: TID %u/%u: InsertTransactionInProgress %u - can't shrink relation", + relname, blkno, offnum, htup->t_xmin); + do_shrinking = false; + } + } + + if (TransactionIdIsValid((TransactionId) htup->t_xmax)) + { + if (TransactionIdDidAbort(htup->t_xmax)) + { + StoreInvalidTransactionId(&(htup->t_xmax)); + pgchanged = true; + } + else if (TransactionIdDidCommit(htup->t_xmax)) + tupgone = true; + else if (!TransactionIdIsInProgress(htup->t_xmax)) + { + + /* + * Not Aborted, Not Committed, Not in Progress - so it + * from crashed process. - vadim 06/02/97 + */ + StoreInvalidTransactionId(&(htup->t_xmax)); + pgchanged = true; + } + else + { + elog(NOTICE, "Rel %s: TID %u/%u: DeleteTransactionInProgress %u - can't shrink relation", + relname, blkno, offnum, htup->t_xmax); + do_shrinking = false; + } + } + + /* + * Is it possible at all ? - vadim 11/26/96 + */ + if (!TransactionIdIsValid((TransactionId) htup->t_xmin)) + { + elog(NOTICE, "Rel %s: TID %u/%u: INSERT_TRANSACTION_ID IS INVALID. \ +DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.", + relname, blkno, offnum, + TransactionIdIsValid((TransactionId) htup->t_xmax), + tupgone); + } + + /* + * It's possibly! But from where it comes ? And should we fix + * it ? - vadim 11/28/96 + */ + itemptr = &(htup->t_ctid); + if (!ItemPointerIsValid(itemptr) || + BlockIdGetBlockNumber(&(itemptr->ip_blkid)) != blkno) + { + elog(NOTICE, "Rel %s: TID %u/%u: TID IN TUPLEHEADER %u/%u IS NOT THE SAME. TUPGONE %d.", + relname, blkno, offnum, + BlockIdGetBlockNumber(&(itemptr->ip_blkid)), + itemptr->ip_posid, tupgone); + } + + /* + * Other checks... + */ + if (htup->t_len != itemid->lp_len) + { + elog(NOTICE, "Rel %s: TID %u/%u: TUPLE_LEN IN PAGEHEADER %u IS NOT THE SAME AS IN TUPLEHEADER %u. TUPGONE %d.", + relname, blkno, offnum, + itemid->lp_len, htup->t_len, tupgone); + } + if (!OidIsValid(htup->t_oid)) + { + elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.", + relname, blkno, offnum, tupgone); + } + + if (tupgone) + { + ItemId lpp; + + if (tempPage == (Page) NULL) + { + Size pageSize; + + pageSize = PageGetPageSize(page); + tempPage = (Page) palloc(pageSize); + memmove(tempPage, page, pageSize); + } + + lpp = &(((PageHeader) tempPage)->pd_linp[offnum - 1]); + + /* mark it unused */ + lpp->lp_flags &= ~LP_USED; + + vpc->vpd_voff[vpc->vpd_noff++] = offnum; + nvac++; + + } + else + { + ntups++; + notup = false; + if (htup->t_len < min_tlen) + min_tlen = htup->t_len; + if (htup->t_len > max_tlen) + max_tlen = htup->t_len; + vc_attrstats(onerel, vacrelstats, htup); + } } - else if (TransactionIdDidCommit(htup->t_xmax)) - tupgone = true; - else if ( !TransactionIdIsInProgress (htup->t_xmax) ) { - /* - * Not Aborted, Not Committed, Not in Progress - - * so it from crashed process. - vadim 06/02/97 - */ - StoreInvalidTransactionId(&(htup->t_xmax)); - pgchanged = true; + + if (pgchanged) + { + WriteBuffer(buf); + dobufrel = false; + nchpg++; } else - { - elog (NOTICE, "Rel %s: TID %u/%u: DeleteTransactionInProgress %u - can't shrink relation", - relname, blkno, offnum, htup->t_xmax); - do_shrinking = false; + dobufrel = true; + if (tempPage != (Page) NULL) + { /* Some tuples are gone */ + PageRepairFragmentation(tempPage); + vpc->vpd_free = ((PageHeader) tempPage)->pd_upper - ((PageHeader) tempPage)->pd_lower; + frsize += vpc->vpd_free; + vc_reappage(Vvpl, vpc); + pfree(tempPage); + tempPage = (Page) NULL; } - } - - /* - * Is it possible at all ? - vadim 11/26/96 - */ - if ( !TransactionIdIsValid((TransactionId)htup->t_xmin) ) - { - elog (NOTICE, "Rel %s: TID %u/%u: INSERT_TRANSACTION_ID IS INVALID. \ -DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.", - relname, blkno, offnum, - TransactionIdIsValid((TransactionId)htup->t_xmax), - tupgone); - } - - /* - * It's possibly! But from where it comes ? - * And should we fix it ? - vadim 11/28/96 - */ - itemptr = &(htup->t_ctid); - if ( !ItemPointerIsValid (itemptr) || - BlockIdGetBlockNumber(&(itemptr->ip_blkid)) != blkno ) - { - elog (NOTICE, "Rel %s: TID %u/%u: TID IN TUPLEHEADER %u/%u IS NOT THE SAME. TUPGONE %d.", - relname, blkno, offnum, - BlockIdGetBlockNumber(&(itemptr->ip_blkid)), - itemptr->ip_posid, tupgone); - } - - /* - * Other checks... - */ - if ( htup->t_len != itemid->lp_len ) - { - elog (NOTICE, "Rel %s: TID %u/%u: TUPLE_LEN IN PAGEHEADER %u IS NOT THE SAME AS IN TUPLEHEADER %u. TUPGONE %d.", - relname, blkno, offnum, - itemid->lp_len, htup->t_len, tupgone); - } - if ( !OidIsValid(htup->t_oid) ) - { - elog (NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.", - relname, blkno, offnum, tupgone); - } - - if (tupgone) { - ItemId lpp; - - if ( tempPage == (Page) NULL ) - { - Size pageSize; - - pageSize = PageGetPageSize(page); - tempPage = (Page) palloc(pageSize); - memmove (tempPage, page, pageSize); + else if (vpc->vpd_noff > 0) + { /* there are only ~LP_USED line pointers */ + vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower; + frsize += vpc->vpd_free; + vc_reappage(Vvpl, vpc); } - - lpp = &(((PageHeader) tempPage)->pd_linp[offnum - 1]); - - /* mark it unused */ - lpp->lp_flags &= ~LP_USED; - - vpc->vpd_voff[vpc->vpd_noff++] = offnum; - nvac++; - - } else { - ntups++; - notup = false; - if ( htup->t_len < min_tlen ) - min_tlen = htup->t_len; - if ( htup->t_len > max_tlen ) - max_tlen = htup->t_len; - vc_attrstats(onerel, vacrelstats, htup); - } + if (dobufrel) + ReleaseBuffer(buf); + if (notup) + nemend++; + else + nemend = 0; } - if (pgchanged) { - WriteBuffer(buf); - dobufrel = false; - nchpg++; - } - else - dobufrel = true; - if ( tempPage != (Page) NULL ) - { /* Some tuples are gone */ - PageRepairFragmentation(tempPage); - vpc->vpd_free = ((PageHeader)tempPage)->pd_upper - ((PageHeader)tempPage)->pd_lower; - frsize += vpc->vpd_free; - vc_reappage (Vvpl, vpc); - pfree (tempPage); - tempPage = (Page) NULL; - } - else if ( vpc->vpd_noff > 0 ) - { /* there are only ~LP_USED line pointers */ - vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower; - frsize += vpc->vpd_free; - vc_reappage (Vvpl, vpc); - } - if ( dobufrel ) - ReleaseBuffer(buf); - if ( notup ) - nemend++; - else - nemend = 0; - } - - pfree (vpc); - - /* save stats in the rel list for use later */ - vacrelstats->ntups = ntups; - vacrelstats->npages = nblocks; -/* vacrelstats->natts = attr_cnt;*/ - if ( ntups == 0 ) - min_tlen = max_tlen = 0; - vacrelstats->min_tlen = min_tlen; - vacrelstats->max_tlen = max_tlen; - - Vvpl->vpl_nemend = nemend; - Fvpl->vpl_nemend = nemend; - - /* - * Try to make Fvpl keeping in mind that we can't use free space - * of "empty" end-pages and last page if it reapped. - */ - if ( do_shrinking && Vvpl->vpl_npages - nemend > 0 ) - { - int nusf; /* blocks usefull for re-using */ - - nusf = Vvpl->vpl_npages - nemend; - if ( (Vvpl->vpl_pgdesc[nusf-1])->vpd_blkno == nblocks - nemend - 1 ) - nusf--; - - for (i = 0; i < nusf; i++) - { - vp = Vvpl->vpl_pgdesc[i]; - if ( vc_enough_space (vp, min_tlen) ) - { - vc_vpinsert (Fvpl, vp); - frsusf += vp->vpd_free; - } + pfree(vpc); + + /* save stats in the rel list for use later */ + vacrelstats->ntups = ntups; + vacrelstats->npages = nblocks; +/* vacrelstats->natts = attr_cnt;*/ + if (ntups == 0) + min_tlen = max_tlen = 0; + vacrelstats->min_tlen = min_tlen; + vacrelstats->max_tlen = max_tlen; + + Vvpl->vpl_nemend = nemend; + Fvpl->vpl_nemend = nemend; + + /* + * Try to make Fvpl keeping in mind that we can't use free space of + * "empty" end-pages and last page if it reapped. + */ + if (do_shrinking && Vvpl->vpl_npages - nemend > 0) + { + int nusf; /* blocks usefull for re-using */ + + nusf = Vvpl->vpl_npages - nemend; + if ((Vvpl->vpl_pgdesc[nusf - 1])->vpd_blkno == nblocks - nemend - 1) + nusf--; + + for (i = 0; i < nusf; i++) + { + vp = Vvpl->vpl_pgdesc[i]; + if (vc_enough_space(vp, min_tlen)) + { + vc_vpinsert(Fvpl, vp); + frsusf += vp->vpd_free; + } + } } - } - getrusage(RUSAGE_SELF, &ru1); - - elog (MESSAGE_LEVEL, "Rel %s: Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \ + getrusage(RUSAGE_SELF, &ru1); + + elog(MESSAGE_LEVEL, "Rel %s: Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \ Tup %u: Vac %u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; Re-using: Free/Avail. Space %u/%u; EndEmpty/Avail. Pages %u/%u. Elapsed %u/%u sec.", - relname, - nblocks, nchpg, Vvpl->vpl_npages, nempg, nnepg, - ntups, nvac, ncrash, nunused, min_tlen, max_tlen, - frsize, frsusf, nemend, Fvpl->vpl_npages, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + relname, + nblocks, nchpg, Vvpl->vpl_npages, nempg, nnepg, + ntups, nvac, ncrash, nunused, min_tlen, max_tlen, + frsize, frsusf, nemend, Fvpl->vpl_npages, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); -} /* vc_scanheap */ +} /* vc_scanheap */ /* - * vc_rpfheap() -- try to repaire relation' fragmentation + * vc_rpfheap() -- try to repaire relation' fragmentation * - * This routine marks dead tuples as unused and tries re-use dead space - * by moving tuples (and inserting indices if needed). It constructs - * Nvpl list of free-ed pages (moved tuples) and clean indices - * for them after committing (in hack-manner - without losing locks - * and freeing memory!) current transaction. It truncates relation - * if some end-blocks are gone away. + * This routine marks dead tuples as unused and tries re-use dead space + * by moving tuples (and inserting indices if needed). It constructs + * Nvpl list of free-ed pages (moved tuples) and clean indices + * for them after committing (in hack-manner - without losing locks + * and freeing memory!) current transaction. It truncates relation + * if some end-blocks are gone away. */ static void -vc_rpfheap (VRelStats *vacrelstats, Relation onerel, - VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel) +vc_rpfheap(VRelStats * vacrelstats, Relation onerel, + VPageList Vvpl, VPageList Fvpl, int nindices, Relation * Irel) { - TransactionId myXID; - CommandId myCID; - AbsoluteTime myCTM = 0; - Buffer buf, ToBuf; - int nblocks, blkno; - Page page, ToPage = NULL; - OffsetNumber offnum = 0, maxoff = 0, newoff, moff; - ItemId itemid, newitemid; - HeapTuple htup, newtup; - TupleDesc tupdesc = NULL; - Datum *idatum = NULL; - char *inulls = NULL; - InsertIndexResult iresult; - VPageListData Nvpl; - VPageDescr ToVpd = NULL, Fvplast, Vvplast, vpc, *vpp; - int ToVpI = 0; - IndDesc *Idesc, *idcur; - int Fblklast, Vblklast, i; - Size tlen; - int nmoved, Fnpages, Vnpages; - int nchkmvd, ntups; - bool isempty, dowrite; - Relation archrel; - struct rusage ru0, ru1; - - getrusage(RUSAGE_SELF, &ru0); - - myXID = GetCurrentTransactionId(); - myCID = GetCurrentCommandId(); - - if ( Irel != (Relation*) NULL ) /* preparation for index' inserts */ - { - vc_mkindesc (onerel, nindices, Irel, &Idesc); - tupdesc = RelationGetTupleDescriptor(onerel); - idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof (*idatum)); - inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof (*inulls)); - } - - /* if the relation has an archive, open it */ - if (onerel->rd_rel->relarch != 'n') - { - archrel = vc_getarchrel(onerel); - /* Archive tuples from "empty" end-pages */ - for ( vpp = Vvpl->vpl_pgdesc + Vvpl->vpl_npages - 1, - i = Vvpl->vpl_nemend; i > 0; i--, vpp-- ) + TransactionId myXID; + CommandId myCID; + AbsoluteTime myCTM = 0; + Buffer buf, + ToBuf; + int nblocks, + blkno; + Page page, + ToPage = NULL; + OffsetNumber offnum = 0, + maxoff = 0, + newoff, + moff; + ItemId itemid, + newitemid; + HeapTuple htup, + newtup; + TupleDesc tupdesc = NULL; + Datum *idatum = NULL; + char *inulls = NULL; + InsertIndexResult iresult; + VPageListData Nvpl; + VPageDescr ToVpd = NULL, + Fvplast, + Vvplast, + vpc, + *vpp; + int ToVpI = 0; + IndDesc *Idesc, + *idcur; + int Fblklast, + Vblklast, + i; + Size tlen; + int nmoved, + Fnpages, + Vnpages; + int nchkmvd, + ntups; + bool isempty, + dowrite; + Relation archrel; + struct rusage ru0, + ru1; + + getrusage(RUSAGE_SELF, &ru0); + + myXID = GetCurrentTransactionId(); + myCID = GetCurrentCommandId(); + + if (Irel != (Relation *) NULL) /* preparation for index' inserts */ { - if ( (*vpp)->vpd_noff > 0 ) - { - buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); - page = BufferGetPage(buf); - Assert ( !PageIsEmpty(page) ); - vc_vacpage (page, *vpp, archrel); - WriteBuffer (buf); - } + vc_mkindesc(onerel, nindices, Irel, &Idesc); + tupdesc = RelationGetTupleDescriptor(onerel); + idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof(*idatum)); + inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof(*inulls)); } - } - else - archrel = (Relation) NULL; - - Nvpl.vpl_npages = 0; - Fnpages = Fvpl->vpl_npages; - Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; - Fblklast = Fvplast->vpd_blkno; - Assert ( Vvpl->vpl_npages > Vvpl->vpl_nemend ); - Vnpages = Vvpl->vpl_npages - Vvpl->vpl_nemend; - Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; - Vblklast = Vvplast->vpd_blkno; - Assert ( Vblklast >= Fblklast ); - ToBuf = InvalidBuffer; - nmoved = 0; - - vpc = (VPageDescr) palloc (sizeof(VPageDescrData) + MaxOffsetNumber*sizeof(OffsetNumber)); - vpc->vpd_nusd = vpc->vpd_noff = 0; - - nblocks = vacrelstats->npages; - for (blkno = nblocks - Vvpl->vpl_nemend - 1; ; blkno--) - { - /* if it's reapped page and it was used by me - quit */ - if ( blkno == Fblklast && Fvplast->vpd_nusd > 0 ) - break; - - buf = ReadBuffer(onerel, blkno); - page = BufferGetPage(buf); - - vpc->vpd_noff = 0; - - isempty = PageIsEmpty(page); - - dowrite = false; - if ( blkno == Vblklast ) /* it's reapped page */ + + /* if the relation has an archive, open it */ + if (onerel->rd_rel->relarch != 'n') { - if ( Vvplast->vpd_noff > 0 ) /* there are dead tuples */ - { /* on this page - clean */ - Assert ( ! isempty ); - vc_vacpage (page, Vvplast, archrel); - dowrite = true; - } - else - { - Assert ( isempty ); - } - --Vnpages; - Assert ( Vnpages > 0 ); - /* get prev reapped page from Vvpl */ - Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; - Vblklast = Vvplast->vpd_blkno; - if ( blkno == Fblklast ) /* this page in Fvpl too */ - { - --Fnpages; - Assert ( Fnpages > 0 ); - Assert ( Fvplast->vpd_nusd == 0 ); - /* get prev reapped page from Fvpl */ - Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; - Fblklast = Fvplast->vpd_blkno; - } - Assert ( Fblklast <= Vblklast ); - if ( isempty ) - { - ReleaseBuffer(buf); - continue; - } + archrel = vc_getarchrel(onerel); + /* Archive tuples from "empty" end-pages */ + for (vpp = Vvpl->vpl_pgdesc + Vvpl->vpl_npages - 1, + i = Vvpl->vpl_nemend; i > 0; i--, vpp--) + { + if ((*vpp)->vpd_noff > 0) + { + buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); + page = BufferGetPage(buf); + Assert(!PageIsEmpty(page)); + vc_vacpage(page, *vpp, archrel); + WriteBuffer(buf); + } + } } else + archrel = (Relation) NULL; + + Nvpl.vpl_npages = 0; + Fnpages = Fvpl->vpl_npages; + Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; + Fblklast = Fvplast->vpd_blkno; + Assert(Vvpl->vpl_npages > Vvpl->vpl_nemend); + Vnpages = Vvpl->vpl_npages - Vvpl->vpl_nemend; + Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; + Vblklast = Vvplast->vpd_blkno; + Assert(Vblklast >= Fblklast); + ToBuf = InvalidBuffer; + nmoved = 0; + + vpc = (VPageDescr) palloc(sizeof(VPageDescrData) + MaxOffsetNumber * sizeof(OffsetNumber)); + vpc->vpd_nusd = vpc->vpd_noff = 0; + + nblocks = vacrelstats->npages; + for (blkno = nblocks - Vvpl->vpl_nemend - 1;; blkno--) { - Assert ( ! isempty ); - } + /* if it's reapped page and it was used by me - quit */ + if (blkno == Fblklast && Fvplast->vpd_nusd > 0) + break; - vpc->vpd_blkno = blkno; - maxoff = PageGetMaxOffsetNumber(page); - for (offnum = FirstOffsetNumber; - offnum <= maxoff; - offnum = OffsetNumberNext(offnum)) - { - itemid = PageGetItemId(page, offnum); - - if (!ItemIdIsUsed(itemid)) - continue; - - htup = (HeapTuple) PageGetItem(page, itemid); - tlen = htup->t_len; - - /* try to find new page for this tuple */ - if ( ToBuf == InvalidBuffer || - ! vc_enough_space (ToVpd, tlen) ) - { - if ( ToBuf != InvalidBuffer ) + buf = ReadBuffer(onerel, blkno); + page = BufferGetPage(buf); + + vpc->vpd_noff = 0; + + isempty = PageIsEmpty(page); + + dowrite = false; + if (blkno == Vblklast) /* it's reapped page */ { - WriteBuffer(ToBuf); - ToBuf = InvalidBuffer; - /* - * If no one tuple can't be added to this page - - * remove page from Fvpl. - vadim 11/27/96 - */ - if ( !vc_enough_space (ToVpd, vacrelstats->min_tlen) ) - { - if ( ToVpd != Fvplast ) - { - Assert ( Fnpages > ToVpI + 1 ); - memmove (Fvpl->vpl_pgdesc + ToVpI, - Fvpl->vpl_pgdesc + ToVpI + 1, - sizeof (VPageDescr*) * (Fnpages - ToVpI - 1)); - } - Assert ( Fnpages >= 1 ); - Fnpages--; - if ( Fnpages == 0 ) - break; - /* get prev reapped page from Fvpl */ - Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; - Fblklast = Fvplast->vpd_blkno; - } - } - for (i=0; i < Fnpages; i++) + if (Vvplast->vpd_noff > 0) /* there are dead tuples */ + { /* on this page - clean */ + Assert(!isempty); + vc_vacpage(page, Vvplast, archrel); + dowrite = true; + } + else + { + Assert(isempty); + } + --Vnpages; + Assert(Vnpages > 0); + /* get prev reapped page from Vvpl */ + Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; + Vblklast = Vvplast->vpd_blkno; + if (blkno == Fblklast) /* this page in Fvpl too */ + { + --Fnpages; + Assert(Fnpages > 0); + Assert(Fvplast->vpd_nusd == 0); + /* get prev reapped page from Fvpl */ + Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; + Fblklast = Fvplast->vpd_blkno; + } + Assert(Fblklast <= Vblklast); + if (isempty) + { + ReleaseBuffer(buf); + continue; + } + } + else { - if ( vc_enough_space (Fvpl->vpl_pgdesc[i], tlen) ) - break; + Assert(!isempty); } - if ( i == Fnpages ) - break; /* can't move item anywhere */ - ToVpI = i; - ToVpd = Fvpl->vpl_pgdesc[ToVpI]; - ToBuf = ReadBuffer(onerel, ToVpd->vpd_blkno); - ToPage = BufferGetPage(ToBuf); - /* if this page was not used before - clean it */ - if ( ! PageIsEmpty(ToPage) && ToVpd->vpd_nusd == 0 ) - vc_vacpage (ToPage, ToVpd, archrel); - } - - /* copy tuple */ - newtup = (HeapTuple) palloc (tlen); - memmove((char *) newtup, (char *) htup, tlen); - - /* store transaction information */ - TransactionIdStore(myXID, &(newtup->t_xmin)); - newtup->t_cmin = myCID; - StoreInvalidTransactionId(&(newtup->t_xmax)); - newtup->t_tmin = INVALID_ABSTIME; - newtup->t_tmax = CURRENT_ABSTIME; - ItemPointerSetInvalid(&newtup->t_chain); - - /* add tuple to the page */ - newoff = PageAddItem (ToPage, (Item)newtup, tlen, - InvalidOffsetNumber, LP_USED); - if ( newoff == InvalidOffsetNumber ) - { - elog (WARN, "\ + + vpc->vpd_blkno = blkno; + maxoff = PageGetMaxOffsetNumber(page); + for (offnum = FirstOffsetNumber; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) + { + itemid = PageGetItemId(page, offnum); + + if (!ItemIdIsUsed(itemid)) + continue; + + htup = (HeapTuple) PageGetItem(page, itemid); + tlen = htup->t_len; + + /* try to find new page for this tuple */ + if (ToBuf == InvalidBuffer || + !vc_enough_space(ToVpd, tlen)) + { + if (ToBuf != InvalidBuffer) + { + WriteBuffer(ToBuf); + ToBuf = InvalidBuffer; + + /* + * If no one tuple can't be added to this page - + * remove page from Fvpl. - vadim 11/27/96 + */ + if (!vc_enough_space(ToVpd, vacrelstats->min_tlen)) + { + if (ToVpd != Fvplast) + { + Assert(Fnpages > ToVpI + 1); + memmove(Fvpl->vpl_pgdesc + ToVpI, + Fvpl->vpl_pgdesc + ToVpI + 1, + sizeof(VPageDescr *) * (Fnpages - ToVpI - 1)); + } + Assert(Fnpages >= 1); + Fnpages--; + if (Fnpages == 0) + break; + /* get prev reapped page from Fvpl */ + Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; + Fblklast = Fvplast->vpd_blkno; + } + } + for (i = 0; i < Fnpages; i++) + { + if (vc_enough_space(Fvpl->vpl_pgdesc[i], tlen)) + break; + } + if (i == Fnpages) + break; /* can't move item anywhere */ + ToVpI = i; + ToVpd = Fvpl->vpl_pgdesc[ToVpI]; + ToBuf = ReadBuffer(onerel, ToVpd->vpd_blkno); + ToPage = BufferGetPage(ToBuf); + /* if this page was not used before - clean it */ + if (!PageIsEmpty(ToPage) && ToVpd->vpd_nusd == 0) + vc_vacpage(ToPage, ToVpd, archrel); + } + + /* copy tuple */ + newtup = (HeapTuple) palloc(tlen); + memmove((char *) newtup, (char *) htup, tlen); + + /* store transaction information */ + TransactionIdStore(myXID, &(newtup->t_xmin)); + newtup->t_cmin = myCID; + StoreInvalidTransactionId(&(newtup->t_xmax)); + newtup->t_tmin = INVALID_ABSTIME; + newtup->t_tmax = CURRENT_ABSTIME; + ItemPointerSetInvalid(&newtup->t_chain); + + /* add tuple to the page */ + newoff = PageAddItem(ToPage, (Item) newtup, tlen, + InvalidOffsetNumber, LP_USED); + if (newoff == InvalidOffsetNumber) + { + elog(WARN, "\ failed to add item with len = %u to page %u (free space %u, nusd %u, noff %u)", - tlen, ToVpd->vpd_blkno, ToVpd->vpd_free, - ToVpd->vpd_nusd, ToVpd->vpd_noff); - } - newitemid = PageGetItemId(ToPage, newoff); - pfree (newtup); - newtup = (HeapTuple) PageGetItem(ToPage, newitemid); - ItemPointerSet(&(newtup->t_ctid), ToVpd->vpd_blkno, newoff); - - /* now logically delete end-tuple */ - TransactionIdStore(myXID, &(htup->t_xmax)); - htup->t_cmax = myCID; - memmove ((char*)&(htup->t_chain), (char*)&(newtup->t_ctid), sizeof (newtup->t_ctid)); - - ToVpd->vpd_nusd++; - nmoved++; - ToVpd->vpd_free = ((PageHeader)ToPage)->pd_upper - ((PageHeader)ToPage)->pd_lower; - vpc->vpd_voff[vpc->vpd_noff++] = offnum; - - /* insert index' tuples if needed */ - if ( Irel != (Relation*) NULL ) - { - for (i = 0, idcur = Idesc; i < nindices; i++, idcur++) + tlen, ToVpd->vpd_blkno, ToVpd->vpd_free, + ToVpd->vpd_nusd, ToVpd->vpd_noff); + } + newitemid = PageGetItemId(ToPage, newoff); + pfree(newtup); + newtup = (HeapTuple) PageGetItem(ToPage, newitemid); + ItemPointerSet(&(newtup->t_ctid), ToVpd->vpd_blkno, newoff); + + /* now logically delete end-tuple */ + TransactionIdStore(myXID, &(htup->t_xmax)); + htup->t_cmax = myCID; + memmove((char *) &(htup->t_chain), (char *) &(newtup->t_ctid), sizeof(newtup->t_ctid)); + + ToVpd->vpd_nusd++; + nmoved++; + ToVpd->vpd_free = ((PageHeader) ToPage)->pd_upper - ((PageHeader) ToPage)->pd_lower; + vpc->vpd_voff[vpc->vpd_noff++] = offnum; + + /* insert index' tuples if needed */ + if (Irel != (Relation *) NULL) + { + for (i = 0, idcur = Idesc; i < nindices; i++, idcur++) + { + FormIndexDatum( + idcur->natts, + (AttrNumber *) & (idcur->tform->indkey[0]), + newtup, + tupdesc, + InvalidBuffer, + idatum, + inulls, + idcur->finfoP); + iresult = index_insert( + Irel[i], + idatum, + inulls, + &(newtup->t_ctid), + onerel); + if (iresult) + pfree(iresult); + } + } + + } /* walk along page */ + + if (vpc->vpd_noff > 0) /* some tuples were moved */ { - FormIndexDatum ( - idcur->natts, - (AttrNumber *)&(idcur->tform->indkey[0]), - newtup, - tupdesc, - InvalidBuffer, - idatum, - inulls, - idcur->finfoP); - iresult = index_insert ( - Irel[i], - idatum, - inulls, - &(newtup->t_ctid), - onerel); - if (iresult) pfree(iresult); + vc_reappage(&Nvpl, vpc); + WriteBuffer(buf); } - } - - } /* walk along page */ + else if (dowrite) + WriteBuffer(buf); + else + ReleaseBuffer(buf); - if ( vpc->vpd_noff > 0 ) /* some tuples were moved */ + if (offnum <= maxoff) + break; /* some item(s) left */ + + } /* walk along relation */ + + blkno++; /* new number of blocks */ + + if (ToBuf != InvalidBuffer) { - vc_reappage (&Nvpl, vpc); - WriteBuffer(buf); + Assert(nmoved > 0); + WriteBuffer(ToBuf); } - else if ( dowrite ) - WriteBuffer(buf); - else - ReleaseBuffer(buf); - - if ( offnum <= maxoff ) - break; /* some item(s) left */ - - } /* walk along relation */ - - blkno++; /* new number of blocks */ - - if ( ToBuf != InvalidBuffer ) - { - Assert (nmoved > 0); - WriteBuffer(ToBuf); - } - - if ( nmoved > 0 ) - { - /* - * We have to commit our tuple' movings before we'll truncate - * relation, but we shouldn't lose our locks. And so - quick hack: - * flush buffers and record status of current transaction - * as committed, and continue. - vadim 11/13/96 - */ - FlushBufferPool(!TransactionFlushEnabled()); - TransactionIdCommit(myXID); - FlushBufferPool(!TransactionFlushEnabled()); - myCTM = TransactionIdGetCommitTime(myXID); - } - - /* - * Clean uncleaned reapped pages from Vvpl list - * and set commit' times for inserted tuples - */ - nchkmvd = 0; - for (i = 0, vpp = Vvpl->vpl_pgdesc; i < Vnpages; i++, vpp++) - { - Assert ( (*vpp)->vpd_blkno < blkno ); - buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); - page = BufferGetPage(buf); - if ( (*vpp)->vpd_nusd == 0 ) /* this page was not used */ + + if (nmoved > 0) { - /* noff == 0 in empty pages only - such pages should be re-used */ - Assert ( (*vpp)->vpd_noff > 0 ); - vc_vacpage (page, *vpp, archrel); + + /* + * We have to commit our tuple' movings before we'll truncate + * relation, but we shouldn't lose our locks. And so - quick hack: + * flush buffers and record status of current transaction as + * committed, and continue. - vadim 11/13/96 + */ + FlushBufferPool(!TransactionFlushEnabled()); + TransactionIdCommit(myXID); + FlushBufferPool(!TransactionFlushEnabled()); + myCTM = TransactionIdGetCommitTime(myXID); } - else /* this page was used */ + + /* + * Clean uncleaned reapped pages from Vvpl list and set commit' times + * for inserted tuples + */ + nchkmvd = 0; + for (i = 0, vpp = Vvpl->vpl_pgdesc; i < Vnpages; i++, vpp++) { - ntups = 0; - moff = PageGetMaxOffsetNumber(page); - for (newoff = FirstOffsetNumber; - newoff <= moff; - newoff = OffsetNumberNext(newoff)) - { - itemid = PageGetItemId(page, newoff); - if (!ItemIdIsUsed(itemid)) - continue; - htup = (HeapTuple) PageGetItem(page, itemid); - if ( TransactionIdEquals((TransactionId)htup->t_xmin, myXID) ) - { - htup->t_tmin = myCTM; - ntups++; - } - } - Assert ( (*vpp)->vpd_nusd == ntups ); - nchkmvd += ntups; + Assert((*vpp)->vpd_blkno < blkno); + buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); + page = BufferGetPage(buf); + if ((*vpp)->vpd_nusd == 0) /* this page was not used */ + { + + /* + * noff == 0 in empty pages only - such pages should be + * re-used + */ + Assert((*vpp)->vpd_noff > 0); + vc_vacpage(page, *vpp, archrel); + } + else +/* this page was used */ + { + ntups = 0; + moff = PageGetMaxOffsetNumber(page); + for (newoff = FirstOffsetNumber; + newoff <= moff; + newoff = OffsetNumberNext(newoff)) + { + itemid = PageGetItemId(page, newoff); + if (!ItemIdIsUsed(itemid)) + continue; + htup = (HeapTuple) PageGetItem(page, itemid); + if (TransactionIdEquals((TransactionId) htup->t_xmin, myXID)) + { + htup->t_tmin = myCTM; + ntups++; + } + } + Assert((*vpp)->vpd_nusd == ntups); + nchkmvd += ntups; + } + WriteBuffer(buf); } - WriteBuffer (buf); - } - Assert ( nmoved == nchkmvd ); + Assert(nmoved == nchkmvd); + + getrusage(RUSAGE_SELF, &ru1); - getrusage(RUSAGE_SELF, &ru1); - - elog (MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. \ + elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. \ Elapsed %u/%u sec.", - (RelationGetRelationName(onerel))->data, - nblocks, blkno, nmoved, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); - - if ( Nvpl.vpl_npages > 0 ) - { - /* vacuum indices again if needed */ - if ( Irel != (Relation*) NULL ) + (RelationGetRelationName(onerel))->data, + nblocks, blkno, nmoved, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + + if (Nvpl.vpl_npages > 0) { - VPageDescr *vpleft, *vpright, vpsave; - - /* re-sort Nvpl.vpl_pgdesc */ - for (vpleft = Nvpl.vpl_pgdesc, - vpright = Nvpl.vpl_pgdesc + Nvpl.vpl_npages - 1; - vpleft < vpright; vpleft++, vpright--) - { - vpsave = *vpleft; *vpleft = *vpright; *vpright = vpsave; - } - for (i = 0; i < nindices; i++) - vc_vaconeind (&Nvpl, Irel[i], vacrelstats->ntups); + /* vacuum indices again if needed */ + if (Irel != (Relation *) NULL) + { + VPageDescr *vpleft, + *vpright, + vpsave; + + /* re-sort Nvpl.vpl_pgdesc */ + for (vpleft = Nvpl.vpl_pgdesc, + vpright = Nvpl.vpl_pgdesc + Nvpl.vpl_npages - 1; + vpleft < vpright; vpleft++, vpright--) + { + vpsave = *vpleft; + *vpleft = *vpright; + *vpright = vpsave; + } + for (i = 0; i < nindices; i++) + vc_vaconeind(&Nvpl, Irel[i], vacrelstats->ntups); + } + + /* + * clean moved tuples from last page in Nvpl list if some tuples + * left there + */ + if (vpc->vpd_noff > 0 && offnum <= maxoff) + { + Assert(vpc->vpd_blkno == blkno - 1); + buf = ReadBuffer(onerel, vpc->vpd_blkno); + page = BufferGetPage(buf); + ntups = 0; + maxoff = offnum; + for (offnum = FirstOffsetNumber; + offnum < maxoff; + offnum = OffsetNumberNext(offnum)) + { + itemid = PageGetItemId(page, offnum); + if (!ItemIdIsUsed(itemid)) + continue; + htup = (HeapTuple) PageGetItem(page, itemid); + Assert(TransactionIdEquals((TransactionId) htup->t_xmax, myXID)); + itemid->lp_flags &= ~LP_USED; + ntups++; + } + Assert(vpc->vpd_noff == ntups); + PageRepairFragmentation(page); + WriteBuffer(buf); + } + + /* now - free new list of reapped pages */ + vpp = Nvpl.vpl_pgdesc; + for (i = 0; i < Nvpl.vpl_npages; i++, vpp++) + pfree(*vpp); + pfree(Nvpl.vpl_pgdesc); } - /* - * clean moved tuples from last page in Nvpl list - * if some tuples left there - */ - if ( vpc->vpd_noff > 0 && offnum <= maxoff ) + /* truncate relation */ + if (blkno < nblocks) { - Assert (vpc->vpd_blkno == blkno - 1); - buf = ReadBuffer(onerel, vpc->vpd_blkno); - page = BufferGetPage (buf); - ntups = 0; - maxoff = offnum; - for (offnum = FirstOffsetNumber; - offnum < maxoff; - offnum = OffsetNumberNext(offnum)) - { - itemid = PageGetItemId(page, offnum); - if (!ItemIdIsUsed(itemid)) - continue; - htup = (HeapTuple) PageGetItem(page, itemid); - Assert ( TransactionIdEquals((TransactionId)htup->t_xmax, myXID) ); - itemid->lp_flags &= ~LP_USED; - ntups++; - } - Assert ( vpc->vpd_noff == ntups ); - PageRepairFragmentation(page); - WriteBuffer (buf); + blkno = smgrtruncate(onerel->rd_rel->relsmgr, onerel, blkno); + Assert(blkno >= 0); + vacrelstats->npages = blkno; /* set new number of blocks */ + } + + if (archrel != (Relation) NULL) + heap_close(archrel); + + if (Irel != (Relation *) NULL) /* pfree index' allocations */ + { + pfree(Idesc); + pfree(idatum); + pfree(inulls); + vc_clsindices(nindices, Irel); } - /* now - free new list of reapped pages */ - vpp = Nvpl.vpl_pgdesc; - for (i = 0; i < Nvpl.vpl_npages; i++, vpp++) - pfree(*vpp); - pfree (Nvpl.vpl_pgdesc); - } - - /* truncate relation */ - if ( blkno < nblocks ) - { - blkno = smgrtruncate (onerel->rd_rel->relsmgr, onerel, blkno); - Assert ( blkno >= 0 ); - vacrelstats->npages = blkno; /* set new number of blocks */ - } - - if ( archrel != (Relation) NULL ) - heap_close(archrel); - - if ( Irel != (Relation*) NULL ) /* pfree index' allocations */ - { - pfree (Idesc); - pfree (idatum); - pfree (inulls); - vc_clsindices (nindices, Irel); - } - - pfree (vpc); - -} /* vc_rpfheap */ + pfree(vpc); + +} /* vc_rpfheap */ /* - * vc_vacheap() -- free dead tuples + * vc_vacheap() -- free dead tuples * - * This routine marks dead tuples as unused and truncates relation - * if there are "empty" end-blocks. + * This routine marks dead tuples as unused and truncates relation + * if there are "empty" end-blocks. */ static void -vc_vacheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl) +vc_vacheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl) { - Buffer buf; - Page page; - VPageDescr *vpp; - Relation archrel; - int nblocks; - int i; - - nblocks = Vvpl->vpl_npages; - /* if the relation has an archive, open it */ - if (onerel->rd_rel->relarch != 'n') - archrel = vc_getarchrel(onerel); - else - { - archrel = (Relation) NULL; - nblocks -= Vvpl->vpl_nemend; /* nothing to do with them */ - } - - for (i = 0, vpp = Vvpl->vpl_pgdesc; i < nblocks; i++, vpp++) - { - if ( (*vpp)->vpd_noff > 0 ) + Buffer buf; + Page page; + VPageDescr *vpp; + Relation archrel; + int nblocks; + int i; + + nblocks = Vvpl->vpl_npages; + /* if the relation has an archive, open it */ + if (onerel->rd_rel->relarch != 'n') + archrel = vc_getarchrel(onerel); + else { - buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); - page = BufferGetPage (buf); - vc_vacpage (page, *vpp, archrel); - WriteBuffer (buf); + archrel = (Relation) NULL; + nblocks -= Vvpl->vpl_nemend; /* nothing to do with them */ + } + + for (i = 0, vpp = Vvpl->vpl_pgdesc; i < nblocks; i++, vpp++) + { + if ((*vpp)->vpd_noff > 0) + { + buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); + page = BufferGetPage(buf); + vc_vacpage(page, *vpp, archrel); + WriteBuffer(buf); + } } - } - - /* truncate relation if there are some empty end-pages */ - if ( Vvpl->vpl_nemend > 0 ) - { - Assert ( vacrelstats->npages >= Vvpl->vpl_nemend ); - nblocks = vacrelstats->npages - Vvpl->vpl_nemend; - elog (MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u.", - (RelationGetRelationName(onerel))->data, - vacrelstats->npages, nblocks); - - /* - * we have to flush "empty" end-pages (if changed, but who knows it) - * before truncation - */ - FlushBufferPool(!TransactionFlushEnabled()); - nblocks = smgrtruncate (onerel->rd_rel->relsmgr, onerel, nblocks); - Assert ( nblocks >= 0 ); - vacrelstats->npages = nblocks; /* set new number of blocks */ - } + /* truncate relation if there are some empty end-pages */ + if (Vvpl->vpl_nemend > 0) + { + Assert(vacrelstats->npages >= Vvpl->vpl_nemend); + nblocks = vacrelstats->npages - Vvpl->vpl_nemend; + elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u.", + (RelationGetRelationName(onerel))->data, + vacrelstats->npages, nblocks); + + /* + * we have to flush "empty" end-pages (if changed, but who knows + * it) before truncation + */ + FlushBufferPool(!TransactionFlushEnabled()); + + nblocks = smgrtruncate(onerel->rd_rel->relsmgr, onerel, nblocks); + Assert(nblocks >= 0); + vacrelstats->npages = nblocks; /* set new number of blocks */ + } - if ( archrel != (Relation) NULL ) - heap_close(archrel); + if (archrel != (Relation) NULL) + heap_close(archrel); -} /* vc_vacheap */ +} /* vc_vacheap */ /* - * vc_vacpage() -- free (and archive if needed) dead tuples on a page - * and repaire its fragmentation. + * vc_vacpage() -- free (and archive if needed) dead tuples on a page + * and repaire its fragmentation. */ static void -vc_vacpage (Page page, VPageDescr vpd, Relation archrel) +vc_vacpage(Page page, VPageDescr vpd, Relation archrel) { - ItemId itemid; - HeapTuple htup; - int i; - - Assert ( vpd->vpd_nusd == 0 ); - for (i=0; i < vpd->vpd_noff; i++) - { - itemid = &(((PageHeader) page)->pd_linp[vpd->vpd_voff[i] - 1]); - if ( archrel != (Relation) NULL && ItemIdIsUsed(itemid) ) + ItemId itemid; + HeapTuple htup; + int i; + + Assert(vpd->vpd_nusd == 0); + for (i = 0; i < vpd->vpd_noff; i++) { - htup = (HeapTuple) PageGetItem (page, itemid); - vc_archive (archrel, htup); + itemid = &(((PageHeader) page)->pd_linp[vpd->vpd_voff[i] - 1]); + if (archrel != (Relation) NULL && ItemIdIsUsed(itemid)) + { + htup = (HeapTuple) PageGetItem(page, itemid); + vc_archive(archrel, htup); + } + itemid->lp_flags &= ~LP_USED; } - itemid->lp_flags &= ~LP_USED; - } - PageRepairFragmentation(page); + PageRepairFragmentation(page); -} /* vc_vacpage */ +} /* vc_vacpage */ /* - * _vc_scanoneind() -- scan one index relation to update statistic. + * _vc_scanoneind() -- scan one index relation to update statistic. * */ static void -vc_scanoneind (Relation indrel, int nhtups) +vc_scanoneind(Relation indrel, int nhtups) { - RetrieveIndexResult res; - IndexScanDesc iscan; - int nitups; - int nipages; - struct rusage ru0, ru1; + RetrieveIndexResult res; + IndexScanDesc iscan; + int nitups; + int nipages; + struct rusage ru0, + ru1; - getrusage(RUSAGE_SELF, &ru0); + getrusage(RUSAGE_SELF, &ru0); - /* walk through the entire index */ - iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); - nitups = 0; + /* walk through the entire index */ + iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); + nitups = 0; - while ((res = index_getnext(iscan, ForwardScanDirection)) - != (RetrieveIndexResult) NULL) - { - nitups++; - pfree(res); - } + while ((res = index_getnext(iscan, ForwardScanDirection)) + != (RetrieveIndexResult) NULL) + { + nitups++; + pfree(res); + } - index_endscan(iscan); + index_endscan(iscan); - /* now update statistics in pg_class */ - nipages = RelationGetNumberOfBlocks(indrel); - vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); + /* now update statistics in pg_class */ + nipages = RelationGetNumberOfBlocks(indrel); + vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); - getrusage(RUSAGE_SELF, &ru1); + getrusage(RUSAGE_SELF, &ru1); - elog (MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u. Elapsed %u/%u sec.", - indrel->rd_rel->relname.data, nipages, nitups, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + elog(MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u. Elapsed %u/%u sec.", + indrel->rd_rel->relname.data, nipages, nitups, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); - if ( nitups != nhtups ) - elog (NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", - indrel->rd_rel->relname.data, nitups, nhtups); + if (nitups != nhtups) + elog(NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", + indrel->rd_rel->relname.data, nitups, nhtups); -} /* vc_scanoneind */ +} /* vc_scanoneind */ /* - * vc_vaconeind() -- vacuum one index relation. + * vc_vaconeind() -- vacuum one index relation. * - * Vpl is the VPageList of the heap we're currently vacuuming. - * It's locked. Indrel is an index relation on the vacuumed heap. - * We don't set locks on the index relation here, since the indexed - * access methods support locking at different granularities. - * We let them handle it. + * Vpl is the VPageList of the heap we're currently vacuuming. + * It's locked. Indrel is an index relation on the vacuumed heap. + * We don't set locks on the index relation here, since the indexed + * access methods support locking at different granularities. + * We let them handle it. * - * Finally, we arrange to update the index relation's statistics in - * pg_class. + * Finally, we arrange to update the index relation's statistics in + * pg_class. */ static void vc_vaconeind(VPageList vpl, Relation indrel, int nhtups) { - RetrieveIndexResult res; - IndexScanDesc iscan; - ItemPointer heapptr; - int nvac; - int nitups; - int nipages; - VPageDescr vp; - struct rusage ru0, ru1; - - getrusage(RUSAGE_SELF, &ru0); - - /* walk through the entire index */ - iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); - nvac = 0; - nitups = 0; - - while ((res = index_getnext(iscan, ForwardScanDirection)) - != (RetrieveIndexResult) NULL) { - heapptr = &res->heap_iptr; - - if ( (vp = vc_tidreapped (heapptr, vpl)) != (VPageDescr) NULL) + RetrieveIndexResult res; + IndexScanDesc iscan; + ItemPointer heapptr; + int nvac; + int nitups; + int nipages; + VPageDescr vp; + struct rusage ru0, + ru1; + + getrusage(RUSAGE_SELF, &ru0); + + /* walk through the entire index */ + iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); + nvac = 0; + nitups = 0; + + while ((res = index_getnext(iscan, ForwardScanDirection)) + != (RetrieveIndexResult) NULL) { + heapptr = &res->heap_iptr; + + if ((vp = vc_tidreapped(heapptr, vpl)) != (VPageDescr) NULL) + { #if 0 - elog(DEBUG, "<%x,%x> -> <%x,%x>", - ItemPointerGetBlockNumber(&(res->index_iptr)), - ItemPointerGetOffsetNumber(&(res->index_iptr)), - ItemPointerGetBlockNumber(&(res->heap_iptr)), - ItemPointerGetOffsetNumber(&(res->heap_iptr))); + elog(DEBUG, "<%x,%x> -> <%x,%x>", + ItemPointerGetBlockNumber(&(res->index_iptr)), + ItemPointerGetOffsetNumber(&(res->index_iptr)), + ItemPointerGetBlockNumber(&(res->heap_iptr)), + ItemPointerGetOffsetNumber(&(res->heap_iptr))); #endif - if ( vp->vpd_noff == 0 ) - { /* this is EmptyPage !!! */ - elog (NOTICE, "Ind %s: pointer to EmptyPage (blk %u off %u) - fixing", - indrel->rd_rel->relname.data, - vp->vpd_blkno, ItemPointerGetOffsetNumber(heapptr)); - } - ++nvac; - index_delete(indrel, &res->index_iptr); - } else { - nitups++; - } + if (vp->vpd_noff == 0) + { /* this is EmptyPage !!! */ + elog(NOTICE, "Ind %s: pointer to EmptyPage (blk %u off %u) - fixing", + indrel->rd_rel->relname.data, + vp->vpd_blkno, ItemPointerGetOffsetNumber(heapptr)); + } + ++nvac; + index_delete(indrel, &res->index_iptr); + } + else + { + nitups++; + } - /* be tidy */ - pfree(res); - } + /* be tidy */ + pfree(res); + } - index_endscan(iscan); + index_endscan(iscan); - /* now update statistics in pg_class */ - nipages = RelationGetNumberOfBlocks(indrel); - vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); + /* now update statistics in pg_class */ + nipages = RelationGetNumberOfBlocks(indrel); + vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); - getrusage(RUSAGE_SELF, &ru1); + getrusage(RUSAGE_SELF, &ru1); - elog (MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.", - indrel->rd_rel->relname.data, nipages, nitups, nvac, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + elog(MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.", + indrel->rd_rel->relname.data, nipages, nitups, nvac, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); - if ( nitups != nhtups ) - elog (NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", - indrel->rd_rel->relname.data, nitups, nhtups); + if (nitups != nhtups) + elog(NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", + indrel->rd_rel->relname.data, nitups, nhtups); -} /* vc_vaconeind */ +} /* vc_vaconeind */ /* - * vc_tidreapped() -- is a particular tid reapped? + * vc_tidreapped() -- is a particular tid reapped? * - * vpl->VPageDescr_array is sorted in right order. + * vpl->VPageDescr_array is sorted in right order. */ -static VPageDescr +static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl) { - OffsetNumber ioffno; - OffsetNumber *voff; - VPageDescr vp, *vpp; - VPageDescrData vpd; - - vpd.vpd_blkno = ItemPointerGetBlockNumber(itemptr); - ioffno = ItemPointerGetOffsetNumber(itemptr); - - vp = &vpd; - vpp = (VPageDescr*) vc_find_eq ((char*)(vpl->vpl_pgdesc), - vpl->vpl_npages, sizeof (VPageDescr), (char*)&vp, - vc_cmp_blk); - - if ( vpp == (VPageDescr*) NULL ) - return ((VPageDescr)NULL); - vp = *vpp; - - /* ok - we are on true page */ - - if ( vp->vpd_noff == 0 ) { /* this is EmptyPage !!! */ - return (vp); - } - - voff = (OffsetNumber*) vc_find_eq ((char*)(vp->vpd_voff), - vp->vpd_noff, sizeof (OffsetNumber), (char*)&ioffno, - vc_cmp_offno); + OffsetNumber ioffno; + OffsetNumber *voff; + VPageDescr vp, + *vpp; + VPageDescrData vpd; - if ( voff == (OffsetNumber*) NULL ) - return ((VPageDescr)NULL); + vpd.vpd_blkno = ItemPointerGetBlockNumber(itemptr); + ioffno = ItemPointerGetOffsetNumber(itemptr); - return (vp); + vp = &vpd; + vpp = (VPageDescr *) vc_find_eq((char *) (vpl->vpl_pgdesc), + vpl->vpl_npages, sizeof(VPageDescr), (char *) &vp, + vc_cmp_blk); -} /* vc_tidreapped */ + if (vpp == (VPageDescr *) NULL) + return ((VPageDescr) NULL); + vp = *vpp; + + /* ok - we are on true page */ + + if (vp->vpd_noff == 0) + { /* this is EmptyPage !!! */ + return (vp); + } + + voff = (OffsetNumber *) vc_find_eq((char *) (vp->vpd_voff), + vp->vpd_noff, sizeof(OffsetNumber), (char *) &ioffno, + vc_cmp_offno); + + if (voff == (OffsetNumber *) NULL) + return ((VPageDescr) NULL); + + return (vp); + +} /* vc_tidreapped */ /* - * vc_attrstats() -- compute column statistics used by the optimzer + * vc_attrstats() -- compute column statistics used by the optimzer * - * We compute the column min, max, null and non-null counts. - * Plus we attempt to find the count of the value that occurs most - * frequently in each column - * These figures are used to compute the selectivity of the column + * We compute the column min, max, null and non-null counts. + * Plus we attempt to find the count of the value that occurs most + * frequently in each column + * These figures are used to compute the selectivity of the column * - * We use a three-bucked cache to get the most frequent item - * The 'guess' buckets count hits. A cache miss causes guess1 - * to get the most hit 'guess' item in the most recent cycle, and - * the new item goes into guess2. Whenever the total count of hits - * of a 'guess' entry is larger than 'best', 'guess' becomes 'best'. + * We use a three-bucked cache to get the most frequent item + * The 'guess' buckets count hits. A cache miss causes guess1 + * to get the most hit 'guess' item in the most recent cycle, and + * the new item goes into guess2. Whenever the total count of hits + * of a 'guess' entry is larger than 'best', 'guess' becomes 'best'. * - * This method works perfectly for columns with unique values, and columns - * with only two unique values, plus nulls. + * This method works perfectly for columns with unique values, and columns + * with only two unique values, plus nulls. * - * It becomes less perfect as the number of unique values increases and - * their distribution in the table becomes more random. + * It becomes less perfect as the number of unique values increases and + * their distribution in the table becomes more random. * */ static void -vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple htup) +vc_attrstats(Relation onerel, VRelStats * vacrelstats, HeapTuple htup) { - int i, attr_cnt = vacrelstats->va_natts; - VacAttrStats *vacattrstats = vacrelstats->vacattrstats; - TupleDesc tupDesc = onerel->rd_att; - Datum value; - bool isnull; - - for (i = 0; i < attr_cnt; i++) { - VacAttrStats *stats = &vacattrstats[i]; - bool value_hit = true; - - value = (Datum) heap_getattr (htup, InvalidBuffer, - stats->attr->attnum, tupDesc, &isnull); - - if (!VacAttrStatsEqValid(stats)) - continue; - - if (isnull) - stats->null_cnt++; - else { - stats->nonnull_cnt++; - if (stats->initialized == false) { - vc_bucketcpy(stats->attr, value, &stats->best, &stats->best_len); - /* best_cnt gets incremented later */ - vc_bucketcpy(stats->attr, value, &stats->guess1, &stats->guess1_len); - stats->guess1_cnt = stats->guess1_hits = 1; - vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); - stats->guess2_hits = 1; - if (VacAttrStatsLtGtValid(stats)) { - vc_bucketcpy(stats->attr, value, &stats->max , &stats->max_len); - vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); + int i, + attr_cnt = vacrelstats->va_natts; + VacAttrStats *vacattrstats = vacrelstats->vacattrstats; + TupleDesc tupDesc = onerel->rd_att; + Datum value; + bool isnull; + + for (i = 0; i < attr_cnt; i++) + { + VacAttrStats *stats = &vacattrstats[i]; + bool value_hit = true; + + value = (Datum) heap_getattr(htup, InvalidBuffer, + stats->attr->attnum, tupDesc, &isnull); + + if (!VacAttrStatsEqValid(stats)) + continue; + + if (isnull) + stats->null_cnt++; + else + { + stats->nonnull_cnt++; + if (stats->initialized == false) + { + vc_bucketcpy(stats->attr, value, &stats->best, &stats->best_len); + /* best_cnt gets incremented later */ + vc_bucketcpy(stats->attr, value, &stats->guess1, &stats->guess1_len); + stats->guess1_cnt = stats->guess1_hits = 1; + vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); + stats->guess2_hits = 1; + if (VacAttrStatsLtGtValid(stats)) + { + vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len); + vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); + } + stats->initialized = true; + } + if (VacAttrStatsLtGtValid(stats)) + { + if ((*(stats->f_cmplt)) (value, stats->min)) + { + vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); + stats->min_cnt = 0; + } + if ((*(stats->f_cmpgt)) (value, stats->max)) + { + vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len); + stats->max_cnt = 0; + } + if ((*(stats->f_cmpeq)) (value, stats->min)) + stats->min_cnt++; + else if ((*(stats->f_cmpeq)) (value, stats->max)) + stats->max_cnt++; + } + if ((*(stats->f_cmpeq)) (value, stats->best)) + stats->best_cnt++; + else if ((*(stats->f_cmpeq)) (value, stats->guess1)) + { + stats->guess1_cnt++; + stats->guess1_hits++; + } + else if ((*(stats->f_cmpeq)) (value, stats->guess2)) + stats->guess2_hits++; + else + value_hit = false; + + if (stats->guess2_hits > stats->guess1_hits) + { + swapDatum(stats->guess1, stats->guess2); + swapInt(stats->guess1_len, stats->guess2_len); + stats->guess1_cnt = stats->guess2_hits; + swapLong(stats->guess1_hits, stats->guess2_hits); + } + if (stats->guess1_cnt > stats->best_cnt) + { + swapDatum(stats->best, stats->guess1); + swapInt(stats->best_len, stats->guess1_len); + swapLong(stats->best_cnt, stats->guess1_cnt); + stats->guess1_hits = 1; + stats->guess2_hits = 1; + } + if (!value_hit) + { + vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); + stats->guess1_hits = 1; + stats->guess2_hits = 1; + } } - stats->initialized = true; - } - if (VacAttrStatsLtGtValid(stats)) { - if ( (*(stats->f_cmplt)) (value,stats->min) ) { - vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); - stats->min_cnt = 0; - } - if ( (*(stats->f_cmpgt)) (value,stats->max) ) { - vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len); - stats->max_cnt = 0; - } - if ( (*(stats->f_cmpeq)) (value,stats->min) ) - stats->min_cnt++; - else if ( (*(stats->f_cmpeq)) (value,stats->max) ) - stats->max_cnt++; - } - if ( (*(stats->f_cmpeq)) (value,stats->best) ) - stats->best_cnt++; - else if ( (*(stats->f_cmpeq)) (value,stats->guess1) ) { - stats->guess1_cnt++; - stats->guess1_hits++; - } - else if ( (*(stats->f_cmpeq)) (value,stats->guess2) ) - stats->guess2_hits++; - else value_hit = false; - - if (stats->guess2_hits > stats->guess1_hits) { - swapDatum(stats->guess1,stats->guess2); - swapInt(stats->guess1_len,stats->guess2_len); - stats->guess1_cnt = stats->guess2_hits; - swapLong(stats->guess1_hits, stats->guess2_hits); - } - if (stats->guess1_cnt > stats->best_cnt) { - swapDatum(stats->best,stats->guess1); - swapInt(stats->best_len,stats->guess1_len); - swapLong(stats->best_cnt,stats->guess1_cnt); - stats->guess1_hits = 1; - stats->guess2_hits = 1; - } - if (!value_hit) { - vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); - stats->guess1_hits = 1; - stats->guess2_hits = 1; - } } - } - return; + return; } /* - * vc_bucketcpy() -- update pg_class statistics for one relation + * vc_bucketcpy() -- update pg_class statistics for one relation * */ static void -vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum *bucket, int16 *bucket_len) +vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum * bucket, int16 * bucket_len) { - if (attr->attbyval && attr->attlen != -1) - *bucket = value; - else { - int len = (attr->attlen != -1 ? attr->attlen : VARSIZE(value)); - - if (len > *bucket_len) - { - if (*bucket_len != 0) - pfree(DatumGetPointer(*bucket)); - *bucket = PointerGetDatum(palloc(len)); - *bucket_len = len; + if (attr->attbyval && attr->attlen != -1) + *bucket = value; + else + { + int len = (attr->attlen != -1 ? attr->attlen : VARSIZE(value)); + + if (len > *bucket_len) + { + if (*bucket_len != 0) + pfree(DatumGetPointer(*bucket)); + *bucket = PointerGetDatum(palloc(len)); + *bucket_len = len; + } + memmove(DatumGetPointer(*bucket), DatumGetPointer(value), len); } - memmove(DatumGetPointer(*bucket), DatumGetPointer(value), len); - } } /* - * vc_updstats() -- update pg_class statistics for one relation + * vc_updstats() -- update pg_class statistics for one relation * - * This routine works for both index and heap relation entries in - * pg_class. We violate no-overwrite semantics here by storing new - * values for ntups, npages, and hasindex directly in the pg_class - * tuple that's already on the page. The reason for this is that if - * we updated these tuples in the usual way, then every tuple in pg_class - * would be replaced every day. This would make planning and executing - * historical queries very expensive. + * This routine works for both index and heap relation entries in + * pg_class. We violate no-overwrite semantics here by storing new + * values for ntups, npages, and hasindex directly in the pg_class + * tuple that's already on the page. The reason for this is that if + * we updated these tuples in the usual way, then every tuple in pg_class + * would be replaced every day. This would make planning and executing + * historical queries very expensive. */ static void -vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VRelStats *vacrelstats) +vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VRelStats * vacrelstats) { - Relation rd, ad, sd; - HeapScanDesc rsdesc, asdesc; - TupleDesc sdesc; - HeapTuple rtup, atup, stup; - Buffer rbuf, abuf; - Form_pg_class pgcform; - ScanKeyData rskey, askey; - AttributeTupleForm attp; - - /* - * update number of tuples and number of pages in pg_class - */ - ScanKeyEntryInitialize(&rskey, 0x0, ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relid)); - - rd = heap_openr(RelationRelationName); - rsdesc = heap_beginscan(rd, false, NowTimeQual, 1, &rskey); - - if (!HeapTupleIsValid(rtup = heap_getnext(rsdesc, 0, &rbuf))) - elog(WARN, "pg_class entry for relid %d vanished during vacuuming", - relid); - - /* overwrite the existing statistics in the tuple */ - vc_setpagelock(rd, BufferGetBlockNumber(rbuf)); - pgcform = (Form_pg_class) GETSTRUCT(rtup); - pgcform->reltuples = ntups; - pgcform->relpages = npages; - pgcform->relhasindex = hasindex; - - if ( vacrelstats != NULL && vacrelstats->va_natts > 0 ) - { - VacAttrStats *vacattrstats = vacrelstats->vacattrstats; - int natts = vacrelstats->va_natts; - - ad = heap_openr(AttributeRelationName); - sd = heap_openr(StatisticRelationName); - ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, - F_INT4EQ, relid); - - asdesc = heap_beginscan(ad, false, NowTimeQual, 1, &askey); - - while (HeapTupleIsValid(atup = heap_getnext(asdesc, 0, &abuf))) + Relation rd, + ad, + sd; + HeapScanDesc rsdesc, + asdesc; + TupleDesc sdesc; + HeapTuple rtup, + atup, + stup; + Buffer rbuf, + abuf; + Form_pg_class pgcform; + ScanKeyData rskey, + askey; + AttributeTupleForm attp; + + /* + * update number of tuples and number of pages in pg_class + */ + ScanKeyEntryInitialize(&rskey, 0x0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + + rd = heap_openr(RelationRelationName); + rsdesc = heap_beginscan(rd, false, NowTimeQual, 1, &rskey); + + if (!HeapTupleIsValid(rtup = heap_getnext(rsdesc, 0, &rbuf))) + elog(WARN, "pg_class entry for relid %d vanished during vacuuming", + relid); + + /* overwrite the existing statistics in the tuple */ + vc_setpagelock(rd, BufferGetBlockNumber(rbuf)); + pgcform = (Form_pg_class) GETSTRUCT(rtup); + pgcform->reltuples = ntups; + pgcform->relpages = npages; + pgcform->relhasindex = hasindex; + + if (vacrelstats != NULL && vacrelstats->va_natts > 0) { - int i; - float32data selratio; /* average ratio of rows selected for a random constant */ - VacAttrStats *stats; - Datum values[ Natts_pg_statistic ]; - char nulls[ Natts_pg_statistic ]; - - attp = (AttributeTupleForm) GETSTRUCT(atup); - if ( attp->attnum <= 0) /* skip system attributes for now, */ - /* they are unique anyway */ - continue; - - for (i = 0; i < natts; i++) - { - if ( attp->attnum == vacattrstats[i].attr->attnum ) - break; - } - if ( i >= natts ) - continue; - stats = &(vacattrstats[i]); - - /* overwrite the existing statistics in the tuple */ - if (VacAttrStatsEqValid(stats)) { - - vc_setpagelock(ad, BufferGetBlockNumber(abuf)); - - if (stats->nonnull_cnt + stats->null_cnt == 0 || - (stats->null_cnt <= 1 && stats->best_cnt == 1)) - selratio = 0; - else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) + VacAttrStats *vacattrstats = vacrelstats->vacattrstats; + int natts = vacrelstats->va_natts; + + ad = heap_openr(AttributeRelationName); + sd = heap_openr(StatisticRelationName); + ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, + F_INT4EQ, relid); + + asdesc = heap_beginscan(ad, false, NowTimeQual, 1, &askey); + + while (HeapTupleIsValid(atup = heap_getnext(asdesc, 0, &abuf))) { - double min_cnt_d = stats->min_cnt, - max_cnt_d = stats->max_cnt, - null_cnt_d = stats->null_cnt, - nonnullcnt_d = stats->nonnull_cnt; /* prevent overflow */ - selratio = (min_cnt_d*min_cnt_d+max_cnt_d*max_cnt_d+null_cnt_d*null_cnt_d)/ - (nonnullcnt_d+null_cnt_d)/(nonnullcnt_d+null_cnt_d); + int i; + float32data selratio; /* average ratio of rows selected + * for a random constant */ + VacAttrStats *stats; + Datum values[Natts_pg_statistic]; + char nulls[Natts_pg_statistic]; + + attp = (AttributeTupleForm) GETSTRUCT(atup); + if (attp->attnum <= 0) /* skip system attributes for now, */ + /* they are unique anyway */ + continue; + + for (i = 0; i < natts; i++) + { + if (attp->attnum == vacattrstats[i].attr->attnum) + break; + } + if (i >= natts) + continue; + stats = &(vacattrstats[i]); + + /* overwrite the existing statistics in the tuple */ + if (VacAttrStatsEqValid(stats)) + { + + vc_setpagelock(ad, BufferGetBlockNumber(abuf)); + + if (stats->nonnull_cnt + stats->null_cnt == 0 || + (stats->null_cnt <= 1 && stats->best_cnt == 1)) + selratio = 0; + else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) + { + double min_cnt_d = stats->min_cnt, + max_cnt_d = stats->max_cnt, + null_cnt_d = stats->null_cnt, + nonnullcnt_d = stats->nonnull_cnt; /* prevent overflow */ + + selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) / + (nonnullcnt_d + null_cnt_d) / (nonnullcnt_d + null_cnt_d); + } + else + { + double most = (double) (stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); + double total = ((double) stats->nonnull_cnt) + ((double) stats->null_cnt); + + /* + * we assume count of other values are 20% of best + * count in table + */ + selratio = (most * most + 0.20 * most * (total - most)) / total / total; + } + if (selratio > 1.0) + selratio = 1.0; + attp->attdisbursion = selratio; + WriteNoReleaseBuffer(abuf); + + /* DO PG_STATISTIC INSERTS */ + + /* + * doing system relations, especially pg_statistic is a + * problem + */ + if (VacAttrStatsLtGtValid(stats) && stats->initialized /* && + * !IsSystemRelationName( + * pgcform->relname.data) + */ ) + { + func_ptr out_function; + char *out_string; + int dummy; + + for (i = 0; i < Natts_pg_statistic; ++i) + nulls[i] = ' '; + + /* ---------------- + * initialize values[] + * ---------------- + */ + i = 0; + values[i++] = (Datum) relid; /* 1 */ + values[i++] = (Datum) attp->attnum; /* 2 */ + values[i++] = (Datum) InvalidOid; /* 3 */ + fmgr_info(stats->outfunc, &out_function, &dummy); + out_string = (*out_function) (stats->min, stats->attr->atttypid); + values[i++] = (Datum) fmgr(TextInRegProcedure, out_string); + pfree(out_string); + out_string = (char *) (*out_function) (stats->max, stats->attr->atttypid); + values[i++] = (Datum) fmgr(TextInRegProcedure, out_string); + pfree(out_string); + + sdesc = sd->rd_att; + + stup = heap_formtuple(sdesc, values, nulls); + + /* ---------------- + * insert the tuple in the relation and get the tuple's oid. + * ---------------- + */ + heap_insert(sd, stup); + pfree(DatumGetPointer(values[3])); + pfree(DatumGetPointer(values[4])); + pfree(stup); + } + } } - else { - double most = (double)(stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); - double total = ((double)stats->nonnull_cnt)+((double)stats->null_cnt); - /* we assume count of other values are 20% - of best count in table */ - selratio = (most*most + 0.20*most*(total-most))/total/total; - } - if (selratio > 1.0) - selratio = 1.0; - attp->attdisbursion = selratio; - WriteNoReleaseBuffer(abuf); - - /* DO PG_STATISTIC INSERTS */ - - /* doing system relations, especially pg_statistic is a problem */ - if (VacAttrStatsLtGtValid(stats) && stats->initialized /* && - !IsSystemRelationName(pgcform->relname.data)*/) { - func_ptr out_function; - char *out_string; - int dummy; - - for (i = 0; i < Natts_pg_statistic; ++i) nulls[i] = ' '; - - /* ---------------- - * initialize values[] - * ---------------- - */ - i = 0; - values[i++] = (Datum) relid; /* 1 */ - values[i++] = (Datum) attp->attnum; /* 2 */ - values[i++] = (Datum) InvalidOid; /* 3 */ - fmgr_info(stats->outfunc, &out_function, &dummy); - out_string = (*out_function)(stats->min, stats->attr->atttypid); - values[i++] = (Datum) fmgr(TextInRegProcedure,out_string); - pfree(out_string); - out_string = (char *)(*out_function)(stats->max, stats->attr->atttypid); - values[i++] = (Datum) fmgr(TextInRegProcedure,out_string); - pfree(out_string); - - sdesc = sd->rd_att; - - stup = heap_formtuple(sdesc, values, nulls); - - /* ---------------- - * insert the tuple in the relation and get the tuple's oid. - * ---------------- - */ - heap_insert(sd, stup); - pfree(DatumGetPointer(values[3])); - pfree(DatumGetPointer(values[4])); - pfree(stup); - } - } + heap_endscan(asdesc); + heap_close(ad); + heap_close(sd); } - heap_endscan(asdesc); - heap_close(ad); - heap_close(sd); - } - - /* XXX -- after write, should invalidate relcache in other backends */ - WriteNoReleaseBuffer(rbuf); /* heap_endscan release scan' buffers ? */ - - /* invalidating system relations confuses the function cache - of pg_operator and pg_opclass */ - if ( !IsSystemRelationName(pgcform->relname.data)) - RelationInvalidateHeapTuple(rd, rtup); - - /* that's all, folks */ - heap_endscan(rsdesc); - heap_close(rd); + + /* XXX -- after write, should invalidate relcache in other backends */ + WriteNoReleaseBuffer(rbuf); /* heap_endscan release scan' buffers ? */ + + /* + * invalidating system relations confuses the function cache of + * pg_operator and pg_opclass + */ + if (!IsSystemRelationName(pgcform->relname.data)) + RelationInvalidateHeapTuple(rd, rtup); + + /* that's all, folks */ + heap_endscan(rsdesc); + heap_close(rd); } /* - * vc_delhilowstats() -- delete pg_statistics rows + * vc_delhilowstats() -- delete pg_statistics rows * */ static void vc_delhilowstats(Oid relid, int attcnt, int *attnums) { - Relation pgstatistic; - HeapScanDesc pgsscan; - HeapTuple pgstup; - ScanKeyData pgskey; - - pgstatistic = heap_openr(StatisticRelationName); - - if (relid != InvalidOid ) { - ScanKeyEntryInitialize(&pgskey, 0x0, Anum_pg_statistic_starelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relid)); - pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 1, &pgskey); - } - else - pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 0, NULL); - - while (HeapTupleIsValid(pgstup = heap_getnext(pgsscan, 0, NULL))) - { - if ( attcnt > 0 ) - { - Form_pg_statistic pgs = (Form_pg_statistic) GETSTRUCT (pgstup); - int i; - - for (i = 0; i < attcnt; i++) - { - if ( pgs->staattnum == attnums[i] + 1 ) - break; - } - if ( i >= attcnt ) - continue; /* don't delete it */ - } - heap_delete(pgstatistic, &pgstup->t_ctid); - } - - heap_endscan(pgsscan); - heap_close(pgstatistic); + Relation pgstatistic; + HeapScanDesc pgsscan; + HeapTuple pgstup; + ScanKeyData pgskey; + + pgstatistic = heap_openr(StatisticRelationName); + + if (relid != InvalidOid) + { + ScanKeyEntryInitialize(&pgskey, 0x0, Anum_pg_statistic_starelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 1, &pgskey); + } + else + pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 0, NULL); + + while (HeapTupleIsValid(pgstup = heap_getnext(pgsscan, 0, NULL))) + { + if (attcnt > 0) + { + Form_pg_statistic pgs = (Form_pg_statistic) GETSTRUCT(pgstup); + int i; + + for (i = 0; i < attcnt; i++) + { + if (pgs->staattnum == attnums[i] + 1) + break; + } + if (i >= attcnt) + continue; /* don't delete it */ + } + heap_delete(pgstatistic, &pgstup->t_ctid); + } + + heap_endscan(pgsscan); + heap_close(pgstatistic); } -static void vc_setpagelock(Relation rel, BlockNumber blkno) +static void +vc_setpagelock(Relation rel, BlockNumber blkno) { - ItemPointerData itm; + ItemPointerData itm; - ItemPointerSet(&itm, blkno, 1); + ItemPointerSet(&itm, blkno, 1); - RelationSetLockForWritePage(rel, &itm); + RelationSetLockForWritePage(rel, &itm); } /* - * vc_reappage() -- save a page on the array of reapped pages. + * vc_reappage() -- save a page on the array of reapped pages. * - * As a side effect of the way that the vacuuming loop for a given - * relation works, higher pages come after lower pages in the array - * (and highest tid on a page is last). + * As a side effect of the way that the vacuuming loop for a given + * relation works, higher pages come after lower pages in the array + * (and highest tid on a page is last). */ -static void +static void vc_reappage(VPageList vpl, VPageDescr vpc) { - VPageDescr newvpd; + VPageDescr newvpd; - /* allocate a VPageDescrData entry */ - newvpd = (VPageDescr) palloc(sizeof(VPageDescrData) + vpc->vpd_noff*sizeof(OffsetNumber)); + /* allocate a VPageDescrData entry */ + newvpd = (VPageDescr) palloc(sizeof(VPageDescrData) + vpc->vpd_noff * sizeof(OffsetNumber)); - /* fill it in */ - if ( vpc->vpd_noff > 0 ) - memmove (newvpd->vpd_voff, vpc->vpd_voff, vpc->vpd_noff*sizeof(OffsetNumber)); - newvpd->vpd_blkno = vpc->vpd_blkno; - newvpd->vpd_free = vpc->vpd_free; - newvpd->vpd_nusd = vpc->vpd_nusd; - newvpd->vpd_noff = vpc->vpd_noff; + /* fill it in */ + if (vpc->vpd_noff > 0) + memmove(newvpd->vpd_voff, vpc->vpd_voff, vpc->vpd_noff * sizeof(OffsetNumber)); + newvpd->vpd_blkno = vpc->vpd_blkno; + newvpd->vpd_free = vpc->vpd_free; + newvpd->vpd_nusd = vpc->vpd_nusd; + newvpd->vpd_noff = vpc->vpd_noff; - /* insert this page into vpl list */ - vc_vpinsert (vpl, newvpd); - -} /* vc_reappage */ + /* insert this page into vpl list */ + vc_vpinsert(vpl, newvpd); + +} /* vc_reappage */ static void -vc_vpinsert (VPageList vpl, VPageDescr vpnew) +vc_vpinsert(VPageList vpl, VPageDescr vpnew) { - /* allocate a VPageDescr entry if needed */ - if ( vpl->vpl_npages == 0 ) - vpl->vpl_pgdesc = (VPageDescr*) palloc(100*sizeof(VPageDescr)); - else if ( vpl->vpl_npages % 100 == 0 ) - vpl->vpl_pgdesc = (VPageDescr*) repalloc(vpl->vpl_pgdesc, (vpl->vpl_npages+100)*sizeof(VPageDescr)); - vpl->vpl_pgdesc[vpl->vpl_npages] = vpnew; - (vpl->vpl_npages)++; - + /* allocate a VPageDescr entry if needed */ + if (vpl->vpl_npages == 0) + vpl->vpl_pgdesc = (VPageDescr *) palloc(100 * sizeof(VPageDescr)); + else if (vpl->vpl_npages % 100 == 0) + vpl->vpl_pgdesc = (VPageDescr *) repalloc(vpl->vpl_pgdesc, (vpl->vpl_npages + 100) * sizeof(VPageDescr)); + vpl->vpl_pgdesc[vpl->vpl_npages] = vpnew; + (vpl->vpl_npages)++; + } static void vc_free(VRelList vrl) { - VRelList p_vrl; - MemoryContext old; - PortalVariableMemory pmem; + VRelList p_vrl; + MemoryContext old; + PortalVariableMemory pmem; - pmem = PortalGetVariableMemory(vc_portal); - old = MemoryContextSwitchTo((MemoryContext)pmem); + pmem = PortalGetVariableMemory(vc_portal); + old = MemoryContextSwitchTo((MemoryContext) pmem); - while (vrl != (VRelList) NULL) { + while (vrl != (VRelList) NULL) + { - /* free rel list entry */ - p_vrl = vrl; - vrl = vrl->vrl_next; - pfree(p_vrl); - } + /* free rel list entry */ + p_vrl = vrl; + vrl = vrl->vrl_next; + pfree(p_vrl); + } - MemoryContextSwitchTo(old); + MemoryContextSwitchTo(old); } /* - * vc_getarchrel() -- open the archive relation for a heap relation + * vc_getarchrel() -- open the archive relation for a heap relation * - * The archive relation is named 'a,XXXXX' for the heap relation - * whose relid is XXXXX. + * The archive relation is named 'a,XXXXX' for the heap relation + * whose relid is XXXXX. */ #define ARCHIVE_PREFIX "a," -static Relation +static Relation vc_getarchrel(Relation heaprel) { - Relation archrel; - char *archrelname; + Relation archrel; + char *archrelname; - archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */ - sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id); + archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */ + sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id); - archrel = heap_openr(archrelname); + archrel = heap_openr(archrelname); - pfree(archrelname); - return (archrel); + pfree(archrelname); + return (archrel); } /* - * vc_archive() -- write a tuple to an archive relation + * vc_archive() -- write a tuple to an archive relation * - * In the future, this will invoke the archived accessd method. For - * now, archive relations are on mag disk. + * In the future, this will invoke the archived accessd method. For + * now, archive relations are on mag disk. */ static void vc_archive(Relation archrel, HeapTuple htup) { - doinsert(archrel, htup); + doinsert(archrel, htup); } -static bool +static bool vc_isarchrel(char *rname) { - if (strncmp(ARCHIVE_PREFIX, rname,strlen(ARCHIVE_PREFIX)) == 0) - return (true); + if (strncmp(ARCHIVE_PREFIX, rname, strlen(ARCHIVE_PREFIX)) == 0) + return (true); - return (false); + return (false); } -static char * -vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *)) +static char * +vc_find_eq(char *bot, int nelem, int size, char *elm, int (*compar) (char *, char *)) { - int res; - int last = nelem - 1; - int celm = nelem / 2; - bool last_move, first_move; - - last_move = first_move = true; - for ( ; ; ) - { - if ( first_move == true ) + int res; + int last = nelem - 1; + int celm = nelem / 2; + bool last_move, + first_move; + + last_move = first_move = true; + for (;;) { - res = compar (bot, elm); - if ( res > 0 ) - return (NULL); - if ( res == 0 ) - return (bot); - first_move = false; - } - if ( last_move == true ) - { - res = compar (elm, bot + last*size); - if ( res > 0 ) - return (NULL); - if ( res == 0 ) - return (bot + last*size); - last_move = false; - } - res = compar (elm, bot + celm*size); - if ( res == 0 ) - return (bot + celm*size); - if ( res < 0 ) - { - if ( celm == 0 ) - return (NULL); - last = celm - 1; - celm = celm / 2; - last_move = true; - continue; + if (first_move == true) + { + res = compar(bot, elm); + if (res > 0) + return (NULL); + if (res == 0) + return (bot); + first_move = false; + } + if (last_move == true) + { + res = compar(elm, bot + last * size); + if (res > 0) + return (NULL); + if (res == 0) + return (bot + last * size); + last_move = false; + } + res = compar(elm, bot + celm * size); + if (res == 0) + return (bot + celm * size); + if (res < 0) + { + if (celm == 0) + return (NULL); + last = celm - 1; + celm = celm / 2; + last_move = true; + continue; + } + + if (celm == last) + return (NULL); + + last = last - celm - 1; + bot = bot + (celm + 1) * size; + celm = (last + 1) / 2; + first_move = true; } - - if ( celm == last ) - return (NULL); - - last = last - celm - 1; - bot = bot + (celm+1)*size; - celm = (last + 1) / 2; - first_move = true; - } - -} /* vc_find_eq */ - -static int -vc_cmp_blk (char *left, char *right) + +} /* vc_find_eq */ + +static int +vc_cmp_blk(char *left, char *right) { - BlockNumber lblk, rblk; + BlockNumber lblk, + rblk; - lblk = (*((VPageDescr*)left))->vpd_blkno; - rblk = (*((VPageDescr*)right))->vpd_blkno; + lblk = (*((VPageDescr *) left))->vpd_blkno; + rblk = (*((VPageDescr *) right))->vpd_blkno; - if ( lblk < rblk ) - return (-1); - if ( lblk == rblk ) - return (0); - return (1); + if (lblk < rblk) + return (-1); + if (lblk == rblk) + return (0); + return (1); -} /* vc_cmp_blk */ +} /* vc_cmp_blk */ -static int -vc_cmp_offno (char *left, char *right) +static int +vc_cmp_offno(char *left, char *right) { - if ( *(OffsetNumber*)left < *(OffsetNumber*)right ) - return (-1); - if ( *(OffsetNumber*)left == *(OffsetNumber*)right ) - return (0); - return (1); + if (*(OffsetNumber *) left < *(OffsetNumber *) right) + return (-1); + if (*(OffsetNumber *) left == *(OffsetNumber *) right) + return (0); + return (1); -} /* vc_cmp_offno */ +} /* vc_cmp_offno */ static void -vc_getindices (Oid relid, int *nindices, Relation **Irel) +vc_getindices(Oid relid, int *nindices, Relation ** Irel) { - Relation pgindex; - Relation irel; - TupleDesc pgidesc; - HeapTuple pgitup; - HeapScanDesc pgiscan; - Datum d; - int i, k; - bool n; - ScanKeyData pgikey; - Oid *ioid; - - *nindices = i = 0; - - ioid = (Oid *) palloc(10*sizeof(Oid)); - - /* prepare a heap scan on the pg_index relation */ - pgindex = heap_openr(IndexRelationName); - pgidesc = RelationGetTupleDescriptor(pgindex); - - ScanKeyEntryInitialize(&pgikey, 0x0, Anum_pg_index_indrelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relid)); - - pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey); - - while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, NULL))) { - d = (Datum) heap_getattr(pgitup, InvalidBuffer, Anum_pg_index_indexrelid, - pgidesc, &n); - i++; - if ( i % 10 == 0 ) - ioid = (Oid *) repalloc(ioid, (i+10)*sizeof(Oid)); - ioid[i-1] = DatumGetObjectId(d); - } - - heap_endscan(pgiscan); - heap_close(pgindex); - - if ( i == 0 ) { /* No one index found */ - pfree(ioid); - return; - } - - if ( Irel != (Relation **) NULL ) - *Irel = (Relation *) palloc(i * sizeof(Relation)); - - for (k = 0; i > 0; ) - { - irel = index_open(ioid[--i]); - if ( irel != (Relation) NULL ) + Relation pgindex; + Relation irel; + TupleDesc pgidesc; + HeapTuple pgitup; + HeapScanDesc pgiscan; + Datum d; + int i, + k; + bool n; + ScanKeyData pgikey; + Oid *ioid; + + *nindices = i = 0; + + ioid = (Oid *) palloc(10 * sizeof(Oid)); + + /* prepare a heap scan on the pg_index relation */ + pgindex = heap_openr(IndexRelationName); + pgidesc = RelationGetTupleDescriptor(pgindex); + + ScanKeyEntryInitialize(&pgikey, 0x0, Anum_pg_index_indrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + + pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey); + + while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, NULL))) { - if ( Irel != (Relation **) NULL ) - (*Irel)[k] = irel; - else - index_close (irel); - k++; + d = (Datum) heap_getattr(pgitup, InvalidBuffer, Anum_pg_index_indexrelid, + pgidesc, &n); + i++; + if (i % 10 == 0) + ioid = (Oid *) repalloc(ioid, (i + 10) * sizeof(Oid)); + ioid[i - 1] = DatumGetObjectId(d); } - else - elog (NOTICE, "CAN't OPEN INDEX %u - SKIP IT", ioid[i]); - } - *nindices = k; - pfree(ioid); - if ( Irel != (Relation **) NULL && *nindices == 0 ) - { - pfree (*Irel); - *Irel = (Relation *) NULL; - } + heap_endscan(pgiscan); + heap_close(pgindex); + + if (i == 0) + { /* No one index found */ + pfree(ioid); + return; + } + + if (Irel != (Relation **) NULL) + *Irel = (Relation *) palloc(i * sizeof(Relation)); + + for (k = 0; i > 0;) + { + irel = index_open(ioid[--i]); + if (irel != (Relation) NULL) + { + if (Irel != (Relation **) NULL) + (*Irel)[k] = irel; + else + index_close(irel); + k++; + } + else + elog(NOTICE, "CAN't OPEN INDEX %u - SKIP IT", ioid[i]); + } + *nindices = k; + pfree(ioid); -} /* vc_getindices */ + if (Irel != (Relation **) NULL && *nindices == 0) + { + pfree(*Irel); + *Irel = (Relation *) NULL; + } + +} /* vc_getindices */ static void -vc_clsindices (int nindices, Relation *Irel) +vc_clsindices(int nindices, Relation * Irel) { - if ( Irel == (Relation*) NULL ) - return; + if (Irel == (Relation *) NULL) + return; - while (nindices--) { - index_close (Irel[nindices]); - } - pfree (Irel); + while (nindices--) + { + index_close(Irel[nindices]); + } + pfree(Irel); -} /* vc_clsindices */ +} /* vc_clsindices */ static void -vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc) +vc_mkindesc(Relation onerel, int nindices, Relation * Irel, IndDesc ** Idesc) { - IndDesc *idcur; - HeapTuple pgIndexTup; - AttrNumber *attnumP; - int natts; - int i; - - *Idesc = (IndDesc *) palloc (nindices * sizeof (IndDesc)); - - for (i = 0, idcur = *Idesc; i < nindices; i++, idcur++) { - pgIndexTup = - SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(Irel[i]->rd_id), - 0,0,0); - Assert(pgIndexTup); - idcur->tform = (IndexTupleForm)GETSTRUCT(pgIndexTup); - for (attnumP = &(idcur->tform->indkey[0]), natts = 0; - *attnumP != InvalidAttrNumber && natts != INDEX_MAX_KEYS; - attnumP++, natts++); - if (idcur->tform->indproc != InvalidOid) { - idcur->finfoP = &(idcur->finfo); - FIgetnArgs(idcur->finfoP) = natts; - natts = 1; - FIgetProcOid(idcur->finfoP) = idcur->tform->indproc; - *(FIgetname(idcur->finfoP)) = '\0'; - } else - idcur->finfoP = (FuncIndexInfo *) NULL; - - idcur->natts = natts; - } - -} /* vc_mkindesc */ - - -static bool -vc_enough_space (VPageDescr vpd, Size len) + IndDesc *idcur; + HeapTuple pgIndexTup; + AttrNumber *attnumP; + int natts; + int i; + + *Idesc = (IndDesc *) palloc(nindices * sizeof(IndDesc)); + + for (i = 0, idcur = *Idesc; i < nindices; i++, idcur++) + { + pgIndexTup = + SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(Irel[i]->rd_id), + 0, 0, 0); + Assert(pgIndexTup); + idcur->tform = (IndexTupleForm) GETSTRUCT(pgIndexTup); + for (attnumP = &(idcur->tform->indkey[0]), natts = 0; + *attnumP != InvalidAttrNumber && natts != INDEX_MAX_KEYS; + attnumP++, natts++); + if (idcur->tform->indproc != InvalidOid) + { + idcur->finfoP = &(idcur->finfo); + FIgetnArgs(idcur->finfoP) = natts; + natts = 1; + FIgetProcOid(idcur->finfoP) = idcur->tform->indproc; + *(FIgetname(idcur->finfoP)) = '\0'; + } + else + idcur->finfoP = (FuncIndexInfo *) NULL; + + idcur->natts = natts; + } + +} /* vc_mkindesc */ + + +static bool +vc_enough_space(VPageDescr vpd, Size len) { - len = DOUBLEALIGN(len); - - if ( len > vpd->vpd_free ) - return (false); - - if ( vpd->vpd_nusd < vpd->vpd_noff ) /* there are free itemid(s) */ - return (true); /* and len <= free_space */ - - /* ok. noff_usd >= noff_free and so we'll have to allocate new itemid */ - if ( len <= vpd->vpd_free - sizeof (ItemIdData) ) - return (true); - - return (false); - -} /* vc_enough_space */ + len = DOUBLEALIGN(len); + + if (len > vpd->vpd_free) + return (false); + + if (vpd->vpd_nusd < vpd->vpd_noff) /* there are free itemid(s) */ + return (true); /* and len <= free_space */ + + /* ok. noff_usd >= noff_free and so we'll have to allocate new itemid */ + if (len <= vpd->vpd_free - sizeof(ItemIdData)) + return (true); + + return (false); + +} /* vc_enough_space */ diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 3cd011ace25..99439de9ce3 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * view.c-- - * use rewrite rules to construct views + * use rewrite rules to construct views * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.8 1997/08/22 14:22:14 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.9 1997/09/07 04:41:06 momjian Exp $ * *------------------------------------------------------------------------- */ -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include <string.h> #include <postgres.h> @@ -42,69 +42,74 @@ *--------------------------------------------------------------------- */ static void -DefineVirtualRelation(char *relname, List *tlist) +DefineVirtualRelation(char *relname, List * tlist) { - CreateStmt createStmt; - List *attrList, *t; - TargetEntry *entry; - Resdom *res; - char *resname; - char *restypename; - - /* - * create a list with one entry per attribute of this relation. - * Each entry is a two element list. The first element is the - * name of the attribute (a string) and the second the name of the type - * (NOTE: a string, not a type id!). - */ - attrList = NIL; - if (tlist!=NIL) { - foreach (t, tlist ) { - ColumnDef *def = makeNode(ColumnDef); - TypeName *typename; - - /* - * find the names of the attribute & its type - */ - entry = lfirst(t); - res = entry->resdom; - resname = res->resname; - restypename = tname(get_id_type(res->restype)); - - typename = makeNode(TypeName); - - typename->name = pstrdup(restypename); - def->colname = pstrdup(resname); - - def->typename = typename; - - def->is_not_null = false; - def->defval = (char*) NULL; - - attrList = lappend(attrList, def); + CreateStmt createStmt; + List *attrList, + *t; + TargetEntry *entry; + Resdom *res; + char *resname; + char *restypename; + + /* + * create a list with one entry per attribute of this relation. Each + * entry is a two element list. The first element is the name of the + * attribute (a string) and the second the name of the type (NOTE: a + * string, not a type id!). + */ + attrList = NIL; + if (tlist != NIL) + { + foreach(t, tlist) + { + ColumnDef *def = makeNode(ColumnDef); + TypeName *typename; + + /* + * find the names of the attribute & its type + */ + entry = lfirst(t); + res = entry->resdom; + resname = res->resname; + restypename = tname(get_id_type(res->restype)); + + typename = makeNode(TypeName); + + typename->name = pstrdup(restypename); + def->colname = pstrdup(resname); + + def->typename = typename; + + def->is_not_null = false; + def->defval = (char *) NULL; + + attrList = lappend(attrList, def); + } } - } else { - elog ( WARN, "attempted to define virtual relation with no attrs"); - } - - /* - * now create the parametesr for keys/inheritance etc. - * All of them are nil... - */ - createStmt.relname = relname; - createStmt.tableElts = attrList; -/* createStmt.tableType = NULL;*/ - createStmt.inhRelnames = NIL; - createStmt.archiveType = ARCH_NONE; - createStmt.location = -1; - createStmt.archiveLoc = -1; - createStmt.constraints = NIL; - - /* - * finally create the relation... - */ - DefineRelation(&createStmt); -} + else + { + elog(WARN, "attempted to define virtual relation with no attrs"); + } + + /* + * now create the parametesr for keys/inheritance etc. All of them are + * nil... + */ + createStmt.relname = relname; + createStmt.tableElts = attrList; +/* createStmt.tableType = NULL;*/ + createStmt.inhRelnames = NIL; + createStmt.archiveType = ARCH_NONE; + createStmt.location = -1; + createStmt.archiveLoc = -1; + createStmt.constraints = NIL; + + /* + * finally create the relation... + */ + DefineRelation(&createStmt); +} /*------------------------------------------------------------------ * makeViewRetrieveRuleName @@ -118,84 +123,87 @@ DefineVirtualRelation(char *relname, List *tlist) * XXX it also means viewName cannot be 16 chars long! - ay 11/94 *------------------------------------------------------------------ */ -char * +char * MakeRetrieveViewRuleName(char *viewName) { /* - char buf[100]; + char buf[100]; - memset(buf, 0, sizeof(buf)); - sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data); - buf[15] = '\0'; - namestrcpy(rule_name, buf); + memset(buf, 0, sizeof(buf)); + sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data); + buf[15] = '\0'; + namestrcpy(rule_name, buf); */ - char *buf; - buf = palloc(strlen(viewName) + 5); - sprintf(buf, "_RET%s",viewName); - return buf; + char *buf; + + buf = palloc(strlen(viewName) + 5); + sprintf(buf, "_RET%s", viewName); + return buf; } static RuleStmt * -FormViewRetrieveRule(char *viewName, Query *viewParse) +FormViewRetrieveRule(char *viewName, Query * viewParse) { - RuleStmt *rule; - char *rname; - Attr *attr; - - /* - * Create a RuleStmt that corresponds to the suitable - * rewrite rule args for DefineQueryRewrite(); - */ - rule = makeNode(RuleStmt); - rname = MakeRetrieveViewRuleName(viewName); - - attr = makeNode(Attr); - attr->relname = pstrdup(viewName); -/* attr->refname = pstrdup(viewName);*/ - rule->rulename = pstrdup(rname); - rule->whereClause = NULL; - rule->event = CMD_SELECT; - rule->object = attr; - rule->instead = true; - rule->actions = lcons(viewParse, NIL); - - return rule; + RuleStmt *rule; + char *rname; + Attr *attr; + + /* + * Create a RuleStmt that corresponds to the suitable rewrite rule + * args for DefineQueryRewrite(); + */ + rule = makeNode(RuleStmt); + rname = MakeRetrieveViewRuleName(viewName); + + attr = makeNode(Attr); + attr->relname = pstrdup(viewName); +/* attr->refname = pstrdup(viewName);*/ + rule->rulename = pstrdup(rname); + rule->whereClause = NULL; + rule->event = CMD_SELECT; + rule->object = attr; + rule->instead = true; + rule->actions = lcons(viewParse, NIL); + + return rule; } static void -DefineViewRules(char *viewName, Query *viewParse) +DefineViewRules(char *viewName, Query * viewParse) { - RuleStmt *retrieve_rule = NULL; + RuleStmt *retrieve_rule = NULL; + #ifdef NOTYET - RuleStmt *replace_rule = NULL; - RuleStmt *append_rule = NULL; - RuleStmt *delete_rule = NULL; + RuleStmt *replace_rule = NULL; + RuleStmt *append_rule = NULL; + RuleStmt *delete_rule = NULL; + #endif - - retrieve_rule = - FormViewRetrieveRule(viewName, viewParse); - + + retrieve_rule = + FormViewRetrieveRule(viewName, viewParse); + #ifdef NOTYET - - replace_rule = - FormViewReplaceRule(viewName, viewParse); - append_rule = - FormViewAppendRule(viewName, viewParse); - delete_rule = - FormViewDeleteRule(viewName, viewParse); - + + replace_rule = + FormViewReplaceRule(viewName, viewParse); + append_rule = + FormViewAppendRule(viewName, viewParse); + delete_rule = + FormViewDeleteRule(viewName, viewParse); + #endif - - DefineQueryRewrite(retrieve_rule); + + DefineQueryRewrite(retrieve_rule); #ifdef NOTYET - DefineQueryRewrite(replace_rule); - DefineQueryRewrite(append_rule); - DefineQueryRewrite(delete_rule); + DefineQueryRewrite(replace_rule); + DefineQueryRewrite(append_rule); + DefineQueryRewrite(delete_rule); #endif - -} + +} /*--------------------------------------------------------------- * UpdateRangeTableOfViewParse @@ -216,88 +224,84 @@ DefineViewRules(char *viewName, Query *viewParse) *--------------------------------------------------------------- */ static void -UpdateRangeTableOfViewParse(char *viewName, Query *viewParse) +UpdateRangeTableOfViewParse(char *viewName, Query * viewParse) { - List *old_rt; - List *new_rt; - RangeTblEntry *rt_entry1, *rt_entry2; - - /* - * first offset all var nodes by 2 - */ - OffsetVarNodes((Node*)viewParse->targetList, 2); - OffsetVarNodes(viewParse->qual, 2); - - /* - * find the old range table... - */ - old_rt = viewParse->rtable; - - /* - * create the 2 new range table entries and form the new - * range table... - * CURRENT first, then NEW.... - */ - rt_entry1 = - addRangeTableEntry(NULL, (char*)viewName, "*CURRENT*", - FALSE, FALSE, NULL); - rt_entry2 = - addRangeTableEntry(NULL, (char*)viewName, "*NEW*", - FALSE, FALSE, NULL); - new_rt = lcons(rt_entry2, old_rt); - new_rt = lcons(rt_entry1, new_rt); - - /* - * Now the tricky part.... - * Update the range table in place... Be careful here, or - * hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE! - */ - viewParse->rtable = new_rt; + List *old_rt; + List *new_rt; + RangeTblEntry *rt_entry1, + *rt_entry2; + + /* + * first offset all var nodes by 2 + */ + OffsetVarNodes((Node *) viewParse->targetList, 2); + OffsetVarNodes(viewParse->qual, 2); + + /* + * find the old range table... + */ + old_rt = viewParse->rtable; + + /* + * create the 2 new range table entries and form the new range + * table... CURRENT first, then NEW.... + */ + rt_entry1 = + addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*", + FALSE, FALSE, NULL); + rt_entry2 = + addRangeTableEntry(NULL, (char *) viewName, "*NEW*", + FALSE, FALSE, NULL); + new_rt = lcons(rt_entry2, old_rt); + new_rt = lcons(rt_entry1, new_rt); + + /* + * Now the tricky part.... Update the range table in place... Be + * careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE! + */ + viewParse->rtable = new_rt; } /*------------------------------------------------------------------- * DefineView * - * - takes a "viewname", "parsetree" pair and then - * 1) construct the "virtual" relation - * 2) commit the command but NOT the transaction, - * so that the relation exists - * before the rules are defined. - * 2) define the "n" rules specified in the PRS2 paper - * over the "virtual" relation + * - takes a "viewname", "parsetree" pair and then + * 1) construct the "virtual" relation + * 2) commit the command but NOT the transaction, + * so that the relation exists + * before the rules are defined. + * 2) define the "n" rules specified in the PRS2 paper + * over the "virtual" relation *------------------------------------------------------------------- */ void -DefineView(char *viewName, Query *viewParse) +DefineView(char *viewName, Query * viewParse) { - List *viewTlist; - - viewTlist = viewParse->targetList; - - /* - * Create the "view" relation - * NOTE: if it already exists, the xaxt will be aborted. - */ - DefineVirtualRelation(viewName, viewTlist); - - /* - * The relation we have just created is not visible - * to any other commands running with the same transaction & - * command id. - * So, increment the command id counter (but do NOT pfree any - * memory!!!!) - */ - CommandCounterIncrement(); - - /* - * The range table of 'viewParse' does not contain entries - * for the "CURRENT" and "NEW" relations. - * So... add them! - * NOTE: we make the update in place! After this call 'viewParse' - * will never be what it used to be... - */ - UpdateRangeTableOfViewParse(viewName, viewParse); - DefineViewRules(viewName, viewParse); + List *viewTlist; + + viewTlist = viewParse->targetList; + + /* + * Create the "view" relation NOTE: if it already exists, the xaxt + * will be aborted. + */ + DefineVirtualRelation(viewName, viewTlist); + + /* + * The relation we have just created is not visible to any other + * commands running with the same transaction & command id. So, + * increment the command id counter (but do NOT pfree any memory!!!!) + */ + CommandCounterIncrement(); + + /* + * The range table of 'viewParse' does not contain entries for the + * "CURRENT" and "NEW" relations. So... add them! NOTE: we make the + * update in place! After this call 'viewParse' will never be what it + * used to be... + */ + UpdateRangeTableOfViewParse(viewName, viewParse); + DefineViewRules(viewName, viewParse); } /*------------------------------------------------------------------ @@ -309,23 +313,22 @@ DefineView(char *viewName, Query *viewParse) void RemoveView(char *viewName) { - char* rname; - - /* - * first remove all the "view" rules... - * Currently we only have one! - */ - rname = MakeRetrieveViewRuleName(viewName); - RemoveRewriteRule(rname); - - /* - * we don't really need that, but just in case... - */ - CommandCounterIncrement(); - - /* - * now remove the relation. - */ - heap_destroy(viewName); - pfree(rname); + char *rname; + + /* + * first remove all the "view" rules... Currently we only have one! + */ + rname = MakeRetrieveViewRuleName(viewName); + RemoveRewriteRule(rname); + + /* + * we don't really need that, but just in case... + */ + CommandCounterIncrement(); + + /* + * now remove the relation. + */ + heap_destroy(viewName); + pfree(rname); } diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index 84b33d4f1e1..401924485e0 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -1,32 +1,32 @@ /*------------------------------------------------------------------------- * * execAmi.c-- - * miscellanious executor access method routines + * miscellanious executor access method routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.5 1997/08/19 21:30:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.6 1997/09/07 04:41:09 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES + * INTERFACE ROUTINES * - * ExecOpenScanR \ / amopen - * ExecBeginScan \ / ambeginscan - * ExecCloseR \ / amclose - * ExecInsert \ executor interface / aminsert - * ExecReScanNode / to access methods \ amrescan - * ExecReScanR / \ amrescan - * ExecMarkPos / \ ammarkpos - * ExecRestrPos / \ amrestpos + * ExecOpenScanR \ / amopen + * ExecBeginScan \ / ambeginscan + * ExecCloseR \ / amclose + * ExecInsert \ executor interface / aminsert + * ExecReScanNode / to access methods \ amrescan + * ExecReScanR / \ amrescan + * ExecMarkPos / \ ammarkpos + * ExecRestrPos / \ amrestpos * - * ExecCreatR function to create temporary relations + * ExecCreatR function to create temporary relations * */ -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include "postgres.h" @@ -43,409 +43,430 @@ #include "access/heapam.h" #include "catalog/heap.h" -static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys, - bool isindex, ScanDirection dir, TimeQual time_range); +static Pointer +ExecBeginScan(Relation relation, int nkeys, ScanKey skeys, + bool isindex, ScanDirection dir, TimeQual time_range); static Relation ExecOpenR(Oid relationOid, bool isindex); /* ---------------------------------------------------------------- - * ExecOpenScanR + * ExecOpenScanR * * old comments: - * Parameters: - * relation -- relation to be opened and scanned. - * nkeys -- number of keys - * skeys -- keys to restrict scanning - * isindex -- if this is true, the relation is the relid of - * an index relation, else it is an index into the - * range table. - * Returns the relation as(relDesc scanDesc) - * If this structure is changed, need to modify the access macros - * defined in execInt.h. + * Parameters: + * relation -- relation to be opened and scanned. + * nkeys -- number of keys + * skeys -- keys to restrict scanning + * isindex -- if this is true, the relation is the relid of + * an index relation, else it is an index into the + * range table. + * Returns the relation as(relDesc scanDesc) + * If this structure is changed, need to modify the access macros + * defined in execInt.h. * ---------------------------------------------------------------- */ void ExecOpenScanR(Oid relOid, - int nkeys, - ScanKey skeys, - bool isindex, - ScanDirection dir, - TimeQual timeRange, - Relation *returnRelation, /* return */ - Pointer *returnScanDesc) /* return */ + int nkeys, + ScanKey skeys, + bool isindex, + ScanDirection dir, + TimeQual timeRange, + Relation * returnRelation, /* return */ + Pointer * returnScanDesc) /* return */ { - Relation relation; - Pointer scanDesc; - - /* ---------------- - * note: scanDesc returned by ExecBeginScan can be either - * a HeapScanDesc or an IndexScanDesc so for now we - * make it a Pointer. There should be a better scan - * abstraction someday -cim 9/9/89 - * ---------------- - */ - relation = ExecOpenR(relOid, isindex); - scanDesc = ExecBeginScan(relation, - nkeys, - skeys, - isindex, - dir, - timeRange); - - if (returnRelation != NULL) - *returnRelation = relation; - if (scanDesc != NULL) - *returnScanDesc = scanDesc; + Relation relation; + Pointer scanDesc; + + /* ---------------- + * note: scanDesc returned by ExecBeginScan can be either + * a HeapScanDesc or an IndexScanDesc so for now we + * make it a Pointer. There should be a better scan + * abstraction someday -cim 9/9/89 + * ---------------- + */ + relation = ExecOpenR(relOid, isindex); + scanDesc = ExecBeginScan(relation, + nkeys, + skeys, + isindex, + dir, + timeRange); + + if (returnRelation != NULL) + *returnRelation = relation; + if (scanDesc != NULL) + *returnScanDesc = scanDesc; } - + /* ---------------------------------------------------------------- - * ExecOpenR + * ExecOpenR * - * returns a relation descriptor given an object id. + * returns a relation descriptor given an object id. * ---------------------------------------------------------------- */ -static Relation +static Relation ExecOpenR(Oid relationOid, bool isindex) { - Relation relation; - relation = (Relation) NULL; - - /* ---------------- - * open the relation with the correct call depending - * on whether this is a heap relation or an index relation. - * ---------------- - */ - if (isindex) { - relation = index_open(relationOid); - } else - relation = heap_open(relationOid); - - if (relation == NULL) - elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed."); - - return relation; + Relation relation; + + relation = (Relation) NULL; + + /* ---------------- + * open the relation with the correct call depending + * on whether this is a heap relation or an index relation. + * ---------------- + */ + if (isindex) + { + relation = index_open(relationOid); + } + else + relation = heap_open(relationOid); + + if (relation == NULL) + elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed."); + + return relation; } - + /* ---------------------------------------------------------------- - * ExecBeginScan + * ExecBeginScan * - * beginscans a relation in current direction. + * beginscans a relation in current direction. * - * XXX fix parameters to AMbeginscan (and btbeginscan) - * currently we need to pass a flag stating whether - * or not the scan should begin at an endpoint of - * the relation.. Right now we always pass false - * -cim 9/14/89 + * XXX fix parameters to AMbeginscan (and btbeginscan) + * currently we need to pass a flag stating whether + * or not the scan should begin at an endpoint of + * the relation.. Right now we always pass false + * -cim 9/14/89 * ---------------------------------------------------------------- */ -static Pointer +static Pointer ExecBeginScan(Relation relation, - int nkeys, - ScanKey skeys, - bool isindex, - ScanDirection dir, - TimeQual time_range) + int nkeys, + ScanKey skeys, + bool isindex, + ScanDirection dir, + TimeQual time_range) { - Pointer scanDesc; - - scanDesc = NULL; - - /* ---------------- - * open the appropriate type of scan. - * - * Note: ambeginscan()'s second arg is a boolean indicating - * that the scan should be done in reverse.. That is, - * if you pass it true, then the scan is backward. - * ---------------- - */ - if (isindex) { - scanDesc = (Pointer) index_beginscan(relation, - false, /* see above comment */ - nkeys, - skeys); - } else { - scanDesc = (Pointer) heap_beginscan(relation, - ScanDirectionIsBackward(dir), - time_range, - nkeys, - skeys); - } - - if (scanDesc == NULL) - elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed."); - - - return scanDesc; + Pointer scanDesc; + + scanDesc = NULL; + + /* ---------------- + * open the appropriate type of scan. + * + * Note: ambeginscan()'s second arg is a boolean indicating + * that the scan should be done in reverse.. That is, + * if you pass it true, then the scan is backward. + * ---------------- + */ + if (isindex) + { + scanDesc = (Pointer) index_beginscan(relation, + false, /* see above comment */ + nkeys, + skeys); + } + else + { + scanDesc = (Pointer) heap_beginscan(relation, + ScanDirectionIsBackward(dir), + time_range, + nkeys, + skeys); + } + + if (scanDesc == NULL) + elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed."); + + + return scanDesc; } - + /* ---------------------------------------------------------------- - * ExecCloseR + * ExecCloseR * - * closes the relation and scan descriptor for a scan or sort - * node. Also closes index relations and scans for index scans. + * closes the relation and scan descriptor for a scan or sort + * node. Also closes index relations and scans for index scans. * * old comments - * closes the relation indicated in 'relID' + * closes the relation indicated in 'relID' * ---------------------------------------------------------------- */ void -ExecCloseR(Plan *node) +ExecCloseR(Plan * node) { - CommonScanState *state; - Relation relation; - HeapScanDesc scanDesc; - - /* ---------------- - * shut down the heap scan and close the heap relation - * ---------------- - */ - switch (nodeTag(node)) { - - case T_SeqScan: - state = ((SeqScan *)node)->scanstate; - break; - - case T_IndexScan: - state = ((IndexScan *)node)->scan.scanstate; - break; - - case T_Material: - state = &(((Material *)node)->matstate->csstate); - break; - - case T_Sort: - state = &(((Sort *)node)->sortstate->csstate); - break; - - case T_Agg: - state = &(((Agg *)node)->aggstate->csstate); - break; - - default: - elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!"); - return; - } - - relation = state->css_currentRelation; - scanDesc = state->css_currentScanDesc; - - if (scanDesc != NULL) - heap_endscan(scanDesc); - - if (relation != NULL) - heap_close(relation); - - /* ---------------- - * if this is an index scan then we have to take care - * of the index relations as well.. - * ---------------- - */ - if (nodeTag(node) == T_IndexScan) { - IndexScan *iscan= (IndexScan *)node; - IndexScanState *indexstate; - int numIndices; - RelationPtr indexRelationDescs; - IndexScanDescPtr indexScanDescs; - int i; - - indexstate = iscan->indxstate; - numIndices = indexstate->iss_NumIndices; - indexRelationDescs = indexstate->iss_RelationDescs; - indexScanDescs = indexstate->iss_ScanDescs; - - for (i = 0; i<numIndices; i++) { - /* ---------------- - * shut down each of the scans and - * close each of the index relations - * ---------------- - */ - if (indexScanDescs[i] != NULL) - index_endscan(indexScanDescs[i]); - - if (indexRelationDescs[i] != NULL) - index_close(indexRelationDescs[i]); + CommonScanState *state; + Relation relation; + HeapScanDesc scanDesc; + + /* ---------------- + * shut down the heap scan and close the heap relation + * ---------------- + */ + switch (nodeTag(node)) + { + + case T_SeqScan: + state = ((SeqScan *) node)->scanstate; + break; + + case T_IndexScan: + state = ((IndexScan *) node)->scan.scanstate; + break; + + case T_Material: + state = &(((Material *) node)->matstate->csstate); + break; + + case T_Sort: + state = &(((Sort *) node)->sortstate->csstate); + break; + + case T_Agg: + state = &(((Agg *) node)->aggstate->csstate); + break; + + default: + elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!"); + return; + } + + relation = state->css_currentRelation; + scanDesc = state->css_currentScanDesc; + + if (scanDesc != NULL) + heap_endscan(scanDesc); + + if (relation != NULL) + heap_close(relation); + + /* ---------------- + * if this is an index scan then we have to take care + * of the index relations as well.. + * ---------------- + */ + if (nodeTag(node) == T_IndexScan) + { + IndexScan *iscan = (IndexScan *) node; + IndexScanState *indexstate; + int numIndices; + RelationPtr indexRelationDescs; + IndexScanDescPtr indexScanDescs; + int i; + + indexstate = iscan->indxstate; + numIndices = indexstate->iss_NumIndices; + indexRelationDescs = indexstate->iss_RelationDescs; + indexScanDescs = indexstate->iss_ScanDescs; + + for (i = 0; i < numIndices; i++) + { + /* ---------------- + * shut down each of the scans and + * close each of the index relations + * ---------------- + */ + if (indexScanDescs[i] != NULL) + index_endscan(indexScanDescs[i]); + + if (indexRelationDescs[i] != NULL) + index_close(indexRelationDescs[i]); + } } - } } - + /* ---------------------------------------------------------------- - * ExecReScan + * ExecReScan * - * XXX this should be extended to cope with all the node types.. + * XXX this should be extended to cope with all the node types.. * - * takes the new expression context as an argument, so that - * index scans needn't have their scan keys updated separately - * - marcel 09/20/94 + * takes the new expression context as an argument, so that + * index scans needn't have their scan keys updated separately + * - marcel 09/20/94 * ---------------------------------------------------------------- */ void -ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent) +ExecReScan(Plan * node, ExprContext * exprCtxt, Plan * parent) { - switch(nodeTag(node)) { - case T_SeqScan: - ExecSeqReScan((SeqScan *) node, exprCtxt, parent); - return; - - case T_IndexScan: - ExecIndexReScan((IndexScan *) node, exprCtxt, parent); - return; + switch (nodeTag(node)) + { + case T_SeqScan: + ExecSeqReScan((SeqScan *) node, exprCtxt, parent); + return; - case T_Material: - /* the first call to ExecReScan should have no effect because - * everything is initialized properly already. the following - * calls will be handled by ExecSeqReScan() because the nodes - * below the Material node have already been materialized into - * a temp relation. - */ - return; + case T_IndexScan: + ExecIndexReScan((IndexScan *) node, exprCtxt, parent); + return; - case T_Tee: - ExecTeeReScan((Tee*) node, exprCtxt, parent); - break; + case T_Material: - default: - elog(WARN, "ExecReScan: not a seqscan or indexscan node."); - return; - } + /* + * the first call to ExecReScan should have no effect because + * everything is initialized properly already. the following + * calls will be handled by ExecSeqReScan() because the nodes + * below the Material node have already been materialized into a + * temp relation. + */ + return; + + case T_Tee: + ExecTeeReScan((Tee *) node, exprCtxt, parent); + break; + + default: + elog(WARN, "ExecReScan: not a seqscan or indexscan node."); + return; + } } - + /* ---------------------------------------------------------------- - * ExecReScanR + * ExecReScanR * - * XXX this does not do the right thing with indices yet. + * XXX this does not do the right thing with indices yet. * ---------------------------------------------------------------- */ HeapScanDesc -ExecReScanR(Relation relDesc, /* LLL relDesc unused */ - HeapScanDesc scanDesc, - ScanDirection direction, - int nkeys, /* LLL nkeys unused */ - ScanKey skeys) +ExecReScanR(Relation relDesc, /* LLL relDesc unused */ + HeapScanDesc scanDesc, + ScanDirection direction, + int nkeys, /* LLL nkeys unused */ + ScanKey skeys) { - if (scanDesc != NULL) - heap_rescan(scanDesc, /* scan desc */ - ScanDirectionIsBackward(direction), /* backward flag */ - skeys); /* scan keys */ - - return scanDesc; + if (scanDesc != NULL) + heap_rescan(scanDesc, /* scan desc */ + ScanDirectionIsBackward(direction), /* backward flag */ + skeys); /* scan keys */ + + return scanDesc; } - + /* ---------------------------------------------------------------- - * ExecMarkPos + * ExecMarkPos * - * Marks the current scan position. + * Marks the current scan position. * - * XXX Needs to be extended to include all the node types. + * XXX Needs to be extended to include all the node types. * ---------------------------------------------------------------- */ void -ExecMarkPos(Plan *node) +ExecMarkPos(Plan * node) { - switch(nodeTag(node)) { - case T_SeqScan: - ExecSeqMarkPos((SeqScan *) node); - break; - - case T_IndexScan: - ExecIndexMarkPos((IndexScan *) node); - break; - - case T_Sort: - ExecSortMarkPos((Sort *) node); - break; - - default: - /* elog(DEBUG, "ExecMarkPos: unsupported node type"); */ - break; - } - return; + switch (nodeTag(node)) + { + case T_SeqScan: + ExecSeqMarkPos((SeqScan *) node); + break; + + case T_IndexScan: + ExecIndexMarkPos((IndexScan *) node); + break; + + case T_Sort: + ExecSortMarkPos((Sort *) node); + break; + + default: + /* elog(DEBUG, "ExecMarkPos: unsupported node type"); */ + break; + } + return; } - + /* ---------------------------------------------------------------- - * ExecRestrPos + * ExecRestrPos * - * restores the scan position previously saved with ExecMarkPos() + * restores the scan position previously saved with ExecMarkPos() * ---------------------------------------------------------------- */ void -ExecRestrPos(Plan *node) +ExecRestrPos(Plan * node) { - switch(nodeTag(node)) { - case T_SeqScan: - ExecSeqRestrPos((SeqScan *) node); - return; - - case T_IndexScan: - ExecIndexRestrPos((IndexScan *) node); - return; - - case T_Sort: - ExecSortRestrPos((Sort *) node); - return; + switch (nodeTag(node)) + { + case T_SeqScan: + ExecSeqRestrPos((SeqScan *) node); + return; - default: - /* elog(DEBUG, "ExecRestrPos: node type not supported"); */ - return; - } + case T_IndexScan: + ExecIndexRestrPos((IndexScan *) node); + return; + + case T_Sort: + ExecSortRestrPos((Sort *) node); + return; + + default: + /* elog(DEBUG, "ExecRestrPos: node type not supported"); */ + return; + } } - + /* ---------------------------------------------------------------- - * ExecCreatR + * ExecCreatR * * old comments - * Creates a relation. + * Creates a relation. * - * Parameters: - * attrType -- type information on the attributes. - * accessMtd -- access methods used to access the created relation. - * relation -- optional. Either an index to the range table or - * negative number indicating a temporary relation. - * A temporary relation is assume is this field is absent. + * Parameters: + * attrType -- type information on the attributes. + * accessMtd -- access methods used to access the created relation. + * relation -- optional. Either an index to the range table or + * negative number indicating a temporary relation. + * A temporary relation is assume is this field is absent. * ---------------------------------------------------------------- */ Relation ExecCreatR(TupleDesc tupType, - Oid relationOid) + Oid relationOid) { - Relation relDesc; - - EU3_printf("ExecCreatR: %s type=%d oid=%d\n", - "entering: ", tupType, relationOid); - CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext); - - relDesc = NULL; - - if (relationOid == _TEMP_RELATION_ID_ ) { - /* ---------------- - * create a temporary relation - * (currently the planner always puts a _TEMP_RELATION_ID - * in the relation argument so we expect this to be the case although - * it's possible that someday we'll get the name from - * from the range table.. -cim 10/12/89) - * ---------------- - */ + Relation relDesc; + + EU3_printf("ExecCreatR: %s type=%d oid=%d\n", + "entering: ", tupType, relationOid); + CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext); + + relDesc = NULL; + + if (relationOid == _TEMP_RELATION_ID_) + { + /* ---------------- + * create a temporary relation + * (currently the planner always puts a _TEMP_RELATION_ID + * in the relation argument so we expect this to be the case although + * it's possible that someday we'll get the name from + * from the range table.. -cim 10/12/89) + * ---------------- + */ /* - sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++); - EU1_printf("ExecCreatR: attempting to create %s\n", tempname); + sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++); + EU1_printf("ExecCreatR: attempting to create %s\n", tempname); */ - /* heap_creatr creates a name if the argument to heap_creatr is '\0 ' */ - relDesc = heap_creatr("", - DEFAULT_SMGR, - tupType); - } else { - /* ---------------- - * use a relation from the range table - * ---------------- - */ - elog(DEBUG, "ExecCreatR: %s", - "stuff using range table id's is not functional"); - } - - if (relDesc == NULL) - elog(DEBUG, "ExecCreatR: failed to create relation."); - - EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc); - - return relDesc; + + /* + * heap_creatr creates a name if the argument to heap_creatr is + * '\0 ' + */ + relDesc = heap_creatr("", + DEFAULT_SMGR, + tupType); + } + else + { + /* ---------------- + * use a relation from the range table + * ---------------- + */ + elog(DEBUG, "ExecCreatR: %s", + "stuff using range table id's is not functional"); + } + + if (relDesc == NULL) + elog(DEBUG, "ExecCreatR: failed to create relation."); + + EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc); + + return relDesc; } - diff --git a/src/backend/executor/execFlatten.c b/src/backend/executor/execFlatten.c index c9bde2ff663..43d616712fa 100644 --- a/src/backend/executor/execFlatten.c +++ b/src/backend/executor/execFlatten.c @@ -1,29 +1,29 @@ /*------------------------------------------------------------------------- * * execFlatten.c-- - * This file handles the nodes associated with flattening sets in the - * target list of queries containing functions returning sets. + * This file handles the nodes associated with flattening sets in the + * target list of queries containing functions returning sets. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.2 1997/08/19 21:30:56 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.3 1997/09/07 04:41:12 momjian Exp $ * *------------------------------------------------------------------------- */ /* * ExecEvalIter() - - * Iterate through the all return tuples/base types from a function one - * at time (i.e. one per ExecEvalIter call). Not really needed for - * postquel functions, but for reasons of orthogonality, these nodes - * exist above pq functions as well as c functions. + * Iterate through the all return tuples/base types from a function one + * at time (i.e. one per ExecEvalIter call). Not really needed for + * postquel functions, but for reasons of orthogonality, these nodes + * exist above pq functions as well as c functions. * * ExecEvalFjoin() - - * Given N Iter nodes return a vector of all combinations of results - * one at a time (i.e. one result vector per ExecEvalFjoin call). This - * node does the actual flattening work. + * Given N Iter nodes return a vector of all combinations of results + * one at a time (i.e. one result vector per ExecEvalFjoin call). This + * node does the actual flattening work. */ #include "postgres.h" #include "nodes/primnodes.h" @@ -33,208 +33,216 @@ #include "executor/execFlatten.h" #ifdef SETS_FIXED -static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext, - DatumPtr results, char *nulls); +static bool +FjoinBumpOuterNodes(TargetEntry * tlist, ExprContext * econtext, + DatumPtr results, char *nulls); + #endif Datum -ExecEvalIter(Iter *iterNode, - ExprContext *econtext, - bool *resultIsNull, - bool *iterIsDone) +ExecEvalIter(Iter * iterNode, + ExprContext * econtext, + bool * resultIsNull, + bool * iterIsDone) { - Node *expression; - - expression = iterNode->iterexpr; - - /* - * Really Iter nodes are only needed for C functions, postquel function - * by their nature return 1 result at a time. For now we are only worrying - * about postquel functions, c functions will come later. - */ - return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone); + Node *expression; + + expression = iterNode->iterexpr; + + /* + * Really Iter nodes are only needed for C functions, postquel + * function by their nature return 1 result at a time. For now we are + * only worrying about postquel functions, c functions will come + * later. + */ + return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone); } void -ExecEvalFjoin(TargetEntry *tlist, - ExprContext *econtext, - bool *isNullVect, - bool *fj_isDone) +ExecEvalFjoin(TargetEntry * tlist, + ExprContext * econtext, + bool * isNullVect, + bool * fj_isDone) { #ifdef SETS_FIXED - bool isDone; - int curNode; - List *tlistP; - - Fjoin *fjNode = tlist->fjoin; - DatumPtr resVect = fjNode->fj_results; - BoolPtr alwaysDone = fjNode->fj_alwaysDone; - - if (fj_isDone) *fj_isDone = false; - /* - * For the next tuple produced by the plan, we need to re-initialize - * the Fjoin node. - */ - if (!fjNode->fj_initialized) + bool isDone; + int curNode; + List *tlistP; + + Fjoin *fjNode = tlist->fjoin; + DatumPtr resVect = fjNode->fj_results; + BoolPtr alwaysDone = fjNode->fj_alwaysDone; + + if (fj_isDone) + *fj_isDone = false; + + /* + * For the next tuple produced by the plan, we need to re-initialize + * the Fjoin node. + */ + if (!fjNode->fj_initialized) { - /* - * Initialize all of the Outer nodes - */ - curNode = 1; - foreach(tlistP, lnext(tlist)) + + /* + * Initialize all of the Outer nodes + */ + curNode = 1; + foreach(tlistP, lnext(tlist)) { - TargetEntry *tle = lfirst(tlistP); - - resVect[curNode] = ExecEvalIter((Iter*)tle->expr, - econtext, - &isNullVect[curNode], - &isDone); - if (isDone) - isNullVect[curNode] = alwaysDone[curNode] = true; - else - alwaysDone[curNode] = false; - - curNode++; + TargetEntry *tle = lfirst(tlistP); + + resVect[curNode] = ExecEvalIter((Iter *) tle->expr, + econtext, + &isNullVect[curNode], + &isDone); + if (isDone) + isNullVect[curNode] = alwaysDone[curNode] = true; + else + alwaysDone[curNode] = false; + + curNode++; } - - /* - * Initialize the inner node - */ - resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, - econtext, - &isNullVect[0], - &isDone); - if (isDone) - isNullVect[0] = alwaysDone[0] = true; - else - alwaysDone[0] = false; - - /* - * Mark the Fjoin as initialized now. - */ - fjNode->fj_initialized = TRUE; - - /* - * If the inner node is always done, then we are done for now - */ - if (isDone) - return; + + /* + * Initialize the inner node + */ + resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr, + econtext, + &isNullVect[0], + &isDone); + if (isDone) + isNullVect[0] = alwaysDone[0] = true; + else + alwaysDone[0] = false; + + /* + * Mark the Fjoin as initialized now. + */ + fjNode->fj_initialized = TRUE; + + /* + * If the inner node is always done, then we are done for now + */ + if (isDone) + return; } - else + else { - /* - * If we're already initialized, all we need to do is get the - * next inner result and pair it up with the existing outer node - * result vector. Watch out for the degenerate case, where the - * inner node never returns results. - */ - - /* - * Fill in nulls for every function that is always done. - */ - for (curNode=fjNode->fj_nNodes-1; curNode >= 0; curNode--) - isNullVect[curNode] = alwaysDone[curNode]; - - if (alwaysDone[0] == true) + + /* + * If we're already initialized, all we need to do is get the next + * inner result and pair it up with the existing outer node result + * vector. Watch out for the degenerate case, where the inner + * node never returns results. + */ + + /* + * Fill in nulls for every function that is always done. + */ + for (curNode = fjNode->fj_nNodes - 1; curNode >= 0; curNode--) + isNullVect[curNode] = alwaysDone[curNode]; + + if (alwaysDone[0] == true) { - *fj_isDone = FjoinBumpOuterNodes(tlist, - econtext, - resVect, - isNullVect); - return; + *fj_isDone = FjoinBumpOuterNodes(tlist, + econtext, + resVect, + isNullVect); + return; } - else - resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, - econtext, - &isNullVect[0], - &isDone); + else + resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr, + econtext, + &isNullVect[0], + &isDone); } - - /* - * if the inner node is done - */ - if (isDone) + + /* + * if the inner node is done + */ + if (isDone) { - *fj_isDone = FjoinBumpOuterNodes(tlist, - econtext, - resVect, - isNullVect); - if (*fj_isDone) - return; - - resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr, - econtext, - &isNullVect[0], - &isDone); - + *fj_isDone = FjoinBumpOuterNodes(tlist, + econtext, + resVect, + isNullVect); + if (*fj_isDone) + return; + + resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr, + econtext, + &isNullVect[0], + &isDone); + } #endif - return; + return; } #ifdef SETS_FIXED -static bool -FjoinBumpOuterNodes(TargetEntry *tlist, - ExprContext *econtext, - DatumPtr results, - char *nulls) +static bool +FjoinBumpOuterNodes(TargetEntry * tlist, + ExprContext * econtext, + DatumPtr results, + char *nulls) { - bool funcIsDone = true; - Fjoin *fjNode = tlist->fjoin; - char *alwaysDone = fjNode->fj_alwaysDone; - List *outerList = lnext(tlist); - List *trailers = lnext(tlist); - int trailNode = 1; - int curNode = 1; - - /* - * Run through list of functions until we get to one that isn't yet - * done returning values. Watch out for funcs that are always done. - */ - while ((funcIsDone == true) && (outerList != NIL)) + bool funcIsDone = true; + Fjoin *fjNode = tlist->fjoin; + char *alwaysDone = fjNode->fj_alwaysDone; + List *outerList = lnext(tlist); + List *trailers = lnext(tlist); + int trailNode = 1; + int curNode = 1; + + /* + * Run through list of functions until we get to one that isn't yet + * done returning values. Watch out for funcs that are always done. + */ + while ((funcIsDone == true) && (outerList != NIL)) { - TargetEntry *tle = lfirst(outerList); - - if (alwaysDone[curNode] == true) - nulls[curNode] = 'n'; - else - results[curNode] = ExecEvalIter((Iter)tle->expr, - econtext, - &nulls[curNode], - &funcIsDone); - curNode++; - outerList = lnext(outerList); + TargetEntry *tle = lfirst(outerList); + + if (alwaysDone[curNode] == true) + nulls[curNode] = 'n'; + else + results[curNode] = ExecEvalIter((Iter) tle->expr, + econtext, + &nulls[curNode], + &funcIsDone); + curNode++; + outerList = lnext(outerList); } - - /* - * If every function is done, then we are done flattening. - * Mark the Fjoin node unitialized, it is time to get the - * next tuple from the plan and redo all of the flattening. - */ - if (funcIsDone) + + /* + * If every function is done, then we are done flattening. Mark the + * Fjoin node unitialized, it is time to get the next tuple from the + * plan and redo all of the flattening. + */ + if (funcIsDone) { - set_fj_initialized(fjNode, false); - return (true); + set_fj_initialized(fjNode, false); + return (true); } - - /* - * We found a function that wasn't done. Now re-run every function - * before it. As usual watch out for functions that are always done. - */ - trailNode = 1; - while (trailNode != curNode-1) + + /* + * We found a function that wasn't done. Now re-run every function + * before it. As usual watch out for functions that are always done. + */ + trailNode = 1; + while (trailNode != curNode - 1) { - TargetEntry *tle = lfirst(trailers); - - if (alwaysDone[trailNode] != true) - results[trailNode] = ExecEvalIter((Iter)tle->expr, - econtext, - &nulls[trailNode], - &funcIsDone); - trailNode++; - trailers = lnext(trailers); + TargetEntry *tle = lfirst(trailers); + + if (alwaysDone[trailNode] != true) + results[trailNode] = ExecEvalIter((Iter) tle->expr, + econtext, + &nulls[trailNode], + &funcIsDone); + trailNode++; + trailers = lnext(trailers); } - return false; + return false; } + #endif diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c index 8779647a113..3ad41bd393f 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * junk.c-- - * Junk attribute support stuff.... + * Junk attribute support stuff.... * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.5 1997/08/26 23:31:37 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.6 1997/09/07 04:41:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,37 +20,37 @@ #include "access/heapam.h" #include "executor/executor.h" #include "nodes/relation.h" -#include "optimizer/tlist.h" /* for MakeTLE */ +#include "optimizer/tlist.h" /* for MakeTLE */ /*------------------------------------------------------------------------- - * XXX this stuff should be rewritten to take advantage - * of ExecProject() and the ProjectionInfo node. - * -cim 6/3/91 - * + * XXX this stuff should be rewritten to take advantage + * of ExecProject() and the ProjectionInfo node. + * -cim 6/3/91 + * * An attribute of a tuple living inside the executor, can be * either a normal attribute or a "junk" attribute. "junk" attributes * never make it out of the executor, i.e. they are never printed, * returned or stored in disk. Their only purpose in life is to * store some information useful only to the executor, mainly the values * of some system attributes like "ctid" or rule locks. - * + * * The general idea is the following: A target list consists of a list of * Resdom nodes & expression pairs. Each Resdom node has an attribute * called 'resjunk'. If the value of this attribute is 1 then the * corresponding attribute is a "junk" attribute. - * + * * When we initialize a plan we call 'ExecInitJunkFilter' to create * and store the appropriate information in the 'es_junkFilter' attribute of * EState. - * + * * We then execute the plan ignoring the "resjunk" attributes. - * + * * Finally, when at the top level we get back a tuple, we can call * 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we * are interested in, and 'ExecRemoveJunk' to remove all the junk attributes * from a tuple. This new "clean" tuple is then printed, replaced, deleted * or inserted. - * + * *------------------------------------------------------------------------- */ @@ -60,174 +60,196 @@ * Initialize the Junk filter. *------------------------------------------------------------------------- */ -JunkFilter * -ExecInitJunkFilter(List *targetList) +JunkFilter * +ExecInitJunkFilter(List * targetList) { - JunkFilter *junkfilter; - List *cleanTargetList; - int len, cleanLength; - TupleDesc tupType, cleanTupType; - List *t; - TargetEntry *tle; - Resdom *resdom, *cleanResdom; - int resjunk; - AttrNumber cleanResno; - AttrNumber *cleanMap; - Size size; - Node *expr; - - /* --------------------- - * First find the "clean" target list, i.e. all the entries - * in the original target list which have a zero 'resjunk' - * NOTE: make copy of the Resdom nodes, because we have - * to change the 'resno's... - * --------------------- - */ - cleanTargetList = NIL; - cleanResno = 1; - - foreach (t, targetList) { - TargetEntry *rtarget = lfirst(t); - if (rtarget->resdom != NULL) { - resdom = rtarget->resdom; - expr = rtarget->expr; - resjunk = resdom->resjunk; - if (resjunk == 0) { - /* - * make a copy of the resdom node, changing its resno. - */ - cleanResdom = (Resdom *) copyObject(resdom); - cleanResdom->resno = cleanResno; - cleanResno ++; - /* - * create a new target list entry - */ - tle = makeNode(TargetEntry); - tle->resdom = cleanResdom; - tle->expr = expr; - cleanTargetList = lappend(cleanTargetList, tle); - } - } - else { + JunkFilter *junkfilter; + List *cleanTargetList; + int len, + cleanLength; + TupleDesc tupType, + cleanTupType; + List *t; + TargetEntry *tle; + Resdom *resdom, + *cleanResdom; + int resjunk; + AttrNumber cleanResno; + AttrNumber *cleanMap; + Size size; + Node *expr; + + /* --------------------- + * First find the "clean" target list, i.e. all the entries + * in the original target list which have a zero 'resjunk' + * NOTE: make copy of the Resdom nodes, because we have + * to change the 'resno's... + * --------------------- + */ + cleanTargetList = NIL; + cleanResno = 1; + + foreach(t, targetList) + { + TargetEntry *rtarget = lfirst(t); + + if (rtarget->resdom != NULL) + { + resdom = rtarget->resdom; + expr = rtarget->expr; + resjunk = resdom->resjunk; + if (resjunk == 0) + { + + /* + * make a copy of the resdom node, changing its resno. + */ + cleanResdom = (Resdom *) copyObject(resdom); + cleanResdom->resno = cleanResno; + cleanResno++; + + /* + * create a new target list entry + */ + tle = makeNode(TargetEntry); + tle->resdom = cleanResdom; + tle->expr = expr; + cleanTargetList = lappend(cleanTargetList, tle); + } + } + else + { #ifdef SETS_FIXED - List *fjListP; - Fjoin *cleanFjoin; - List *cleanFjList; - List *fjList = lfirst(t); - Fjoin *fjNode = (Fjoin *)tl_node(fjList); - - cleanFjoin = (Fjoin)copyObject((Node) fjNode); - cleanFjList = lcons(cleanFjoin, NIL); - - resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); - expr = lsecond(get_fj_innerNode(fjNode)); - cleanResdom = (Resdom) copyObject((Node) resdom); - set_resno(cleanResdom, cleanResno); - cleanResno++; - tle = (List) MakeTLE(cleanResdom, (Expr) expr); - set_fj_innerNode(cleanFjoin, tle); - - foreach(fjListP, lnext(fjList)) { - TargetEntry *tle = lfirst(fjListP); - - resdom = tle->resdom; - expr = tle->expr; - cleanResdom = (Resdom*) copyObject((Node) resdom); - cleanResno++; - cleanResdom->Resno = cleanResno; - /* - * create a new target list entry - */ - tle = (List) MakeTLE(cleanResdom, (Expr) expr); - cleanFjList = lappend(cleanFjList, tle); - } - lappend(cleanTargetList, cleanFjList); + List *fjListP; + Fjoin *cleanFjoin; + List *cleanFjList; + List *fjList = lfirst(t); + Fjoin *fjNode = (Fjoin *) tl_node(fjList); + + cleanFjoin = (Fjoin) copyObject((Node) fjNode); + cleanFjList = lcons(cleanFjoin, NIL); + + resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); + expr = lsecond(get_fj_innerNode(fjNode)); + cleanResdom = (Resdom) copyObject((Node) resdom); + set_resno(cleanResdom, cleanResno); + cleanResno++; + tle = (List) MakeTLE(cleanResdom, (Expr) expr); + set_fj_innerNode(cleanFjoin, tle); + + foreach(fjListP, lnext(fjList)) + { + TargetEntry *tle = lfirst(fjListP); + + resdom = tle->resdom; + expr = tle->expr; + cleanResdom = (Resdom *) copyObject((Node) resdom); + cleanResno++; + cleanResdom->Resno = cleanResno; + + /* + * create a new target list entry + */ + tle = (List) MakeTLE(cleanResdom, (Expr) expr); + cleanFjList = lappend(cleanFjList, tle); + } + lappend(cleanTargetList, cleanFjList); #endif - } - } - - /* --------------------- - * Now calculate the tuple types for the original and the clean tuple - * - * XXX ExecTypeFromTL should be used sparingly. Don't we already - * have the tupType corresponding to the targetlist we are passed? - * -cim 5/31/91 - * --------------------- - */ - tupType = (TupleDesc) ExecTypeFromTL(targetList); - cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList); - - len = ExecTargetListLength(targetList); - cleanLength = ExecTargetListLength(cleanTargetList); - - /* --------------------- - * Now calculate the "map" between the original tuples attributes - * and the "clean" tuple's attributes. - * - * The "map" is an array of "cleanLength" attribute numbers, i.e. - * one entry for every attribute of the "clean" tuple. - * The value of this entry is the attribute number of the corresponding - * attribute of the "original" tuple. - * --------------------- - */ - if (cleanLength > 0) { - size = cleanLength * sizeof(AttrNumber); - cleanMap = (AttrNumber*) palloc(size); - cleanResno = 1; - foreach (t, targetList) { - TargetEntry *tle = lfirst(t); - if (tle->resdom != NULL) { - resdom = tle->resdom; - expr = tle->expr; - resjunk = resdom->resjunk; - if (resjunk == 0) { - cleanMap[cleanResno-1] = resdom->resno; - cleanResno ++; } - } else { + } + + /* --------------------- + * Now calculate the tuple types for the original and the clean tuple + * + * XXX ExecTypeFromTL should be used sparingly. Don't we already + * have the tupType corresponding to the targetlist we are passed? + * -cim 5/31/91 + * --------------------- + */ + tupType = (TupleDesc) ExecTypeFromTL(targetList); + cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList); + + len = ExecTargetListLength(targetList); + cleanLength = ExecTargetListLength(cleanTargetList); + + /* --------------------- + * Now calculate the "map" between the original tuples attributes + * and the "clean" tuple's attributes. + * + * The "map" is an array of "cleanLength" attribute numbers, i.e. + * one entry for every attribute of the "clean" tuple. + * The value of this entry is the attribute number of the corresponding + * attribute of the "original" tuple. + * --------------------- + */ + if (cleanLength > 0) + { + size = cleanLength * sizeof(AttrNumber); + cleanMap = (AttrNumber *) palloc(size); + cleanResno = 1; + foreach(t, targetList) + { + TargetEntry *tle = lfirst(t); + + if (tle->resdom != NULL) + { + resdom = tle->resdom; + expr = tle->expr; + resjunk = resdom->resjunk; + if (resjunk == 0) + { + cleanMap[cleanResno - 1] = resdom->resno; + cleanResno++; + } + } + else + { #ifdef SETS_FIXED - List fjListP; - List fjList = lfirst(t); - Fjoin fjNode = (Fjoin)lfirst(fjList); + List fjListP; + List fjList = lfirst(t); + Fjoin fjNode = (Fjoin) lfirst(fjList); - /* what the hell is this????? */ - resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); + /* what the hell is this????? */ + resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); #endif - cleanMap[cleanResno-1] = tle->resdom->resno; - cleanResno++; + cleanMap[cleanResno - 1] = tle->resdom->resno; + cleanResno++; #ifdef SETS_FIXED - foreach(fjListP, lnext(fjList)) { - TargetEntry *tle = lfirst(fjListP); + foreach(fjListP, lnext(fjList)) + { + TargetEntry *tle = lfirst(fjListP); - resdom = tle->resdom; - cleanMap[cleanResno-1] = resdom->resno; - cleanResno++; - } + resdom = tle->resdom; + cleanMap[cleanResno - 1] = resdom->resno; + cleanResno++; + } #endif - } + } + } + } + else + { + cleanMap = NULL; } - } else { - cleanMap = NULL; - } - - /* --------------------- - * Finally create and initialize the JunkFilter. - * --------------------- - */ - junkfilter = makeNode(JunkFilter); - - junkfilter->jf_targetList = targetList; - junkfilter->jf_length = len; - junkfilter->jf_tupType = tupType; - junkfilter->jf_cleanTargetList = cleanTargetList; - junkfilter->jf_cleanLength = cleanLength; - junkfilter->jf_cleanTupType = cleanTupType; - junkfilter->jf_cleanMap = cleanMap; - - return(junkfilter); - + + /* --------------------- + * Finally create and initialize the JunkFilter. + * --------------------- + */ + junkfilter = makeNode(JunkFilter); + + junkfilter->jf_targetList = targetList; + junkfilter->jf_length = len; + junkfilter->jf_tupType = tupType; + junkfilter->jf_cleanTargetList = cleanTargetList; + junkfilter->jf_cleanLength = cleanLength; + junkfilter->jf_cleanTupType = cleanTupType; + junkfilter->jf_cleanMap = cleanMap; + + return (junkfilter); + } /*------------------------------------------------------------------------- @@ -242,57 +264,61 @@ ExecInitJunkFilter(List *targetList) *------------------------------------------------------------------------- */ bool -ExecGetJunkAttribute(JunkFilter *junkfilter, - TupleTableSlot *slot, - char *attrName, - Datum *value, - bool *isNull) +ExecGetJunkAttribute(JunkFilter * junkfilter, + TupleTableSlot * slot, + char *attrName, + Datum * value, + bool * isNull) { - List *targetList; - List *t; - Resdom *resdom; - AttrNumber resno; - char *resname; - int resjunk; - TupleDesc tupType; - HeapTuple tuple; - - /* --------------------- - * first look in the junkfilter's target list for - * an attribute with the given name - * --------------------- - */ - resno = InvalidAttrNumber; - targetList = junkfilter->jf_targetList; - - foreach (t, targetList) { - TargetEntry *tle = lfirst(t); - resdom = tle->resdom; - resname = resdom->resname; - resjunk = resdom->resjunk; - if (resjunk != 0 && (strcmp(resname, attrName) == 0)) { - /* We found it ! */ - resno = resdom->resno; - break; + List *targetList; + List *t; + Resdom *resdom; + AttrNumber resno; + char *resname; + int resjunk; + TupleDesc tupType; + HeapTuple tuple; + + /* --------------------- + * first look in the junkfilter's target list for + * an attribute with the given name + * --------------------- + */ + resno = InvalidAttrNumber; + targetList = junkfilter->jf_targetList; + + foreach(t, targetList) + { + TargetEntry *tle = lfirst(t); + + resdom = tle->resdom; + resname = resdom->resname; + resjunk = resdom->resjunk; + if (resjunk != 0 && (strcmp(resname, attrName) == 0)) + { + /* We found it ! */ + resno = resdom->resno; + break; + } + } + + if (resno == InvalidAttrNumber) + { + /* Ooops! We couldn't find this attribute... */ + return (false); } - } - - if (resno == InvalidAttrNumber) { - /* Ooops! We couldn't find this attribute... */ - return(false); - } - - /* --------------------- - * Now extract the attribute value from the tuple. - * --------------------- - */ - tuple = slot->val; - tupType = (TupleDesc) junkfilter->jf_tupType; - - *value = (Datum) - heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull); - - return true; + + /* --------------------- + * Now extract the attribute value from the tuple. + * --------------------- + */ + tuple = slot->val; + tupType = (TupleDesc) junkfilter->jf_tupType; + + *value = (Datum) + heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull); + + return true; } /*------------------------------------------------------------------------- @@ -302,94 +328,98 @@ ExecGetJunkAttribute(JunkFilter *junkfilter, *------------------------------------------------------------------------- */ HeapTuple -ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) +ExecRemoveJunk(JunkFilter * junkfilter, TupleTableSlot * slot) { - HeapTuple tuple; - HeapTuple cleanTuple; - AttrNumber *cleanMap; - TupleDesc cleanTupType; - TupleDesc tupType; - int cleanLength; - bool isNull; - int i; - Size size; - Datum *values; - char *nulls; - Datum values_array[64]; - char nulls_array[64]; - - /* ---------------- - * get info from the slot and the junk filter - * ---------------- - */ - tuple = slot->val; - - tupType = (TupleDesc) junkfilter->jf_tupType; - cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType; - cleanLength = junkfilter->jf_cleanLength; - cleanMap = junkfilter->jf_cleanMap; - - /* --------------------- - * Handle the trivial case first. - * --------------------- - */ - if (cleanLength == 0) - return (HeapTuple) NULL; - - /* --------------------- - * Create the arrays that will hold the attribute values - * and the null information for the new "clean" tuple. - * - * Note: we use memory on the stack to optimize things when - * we are dealing with a small number of tuples. - * for large tuples we just use palloc. - * --------------------- - */ - if (cleanLength > 64) { - size = cleanLength * sizeof(Datum); - values = (Datum *) palloc(size); - - size = cleanLength * sizeof(char); - nulls = (char *) palloc(size); - } else { - values = values_array; - nulls = nulls_array; - } - - /* --------------------- - * Exctract one by one all the values of the "clean" tuple. - * --------------------- - */ - for (i=0; i<cleanLength; i++) { - Datum d = (Datum) - heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull); - - values[i] = d; - - if (isNull) - nulls[i] = 'n'; + HeapTuple tuple; + HeapTuple cleanTuple; + AttrNumber *cleanMap; + TupleDesc cleanTupType; + TupleDesc tupType; + int cleanLength; + bool isNull; + int i; + Size size; + Datum *values; + char *nulls; + Datum values_array[64]; + char nulls_array[64]; + + /* ---------------- + * get info from the slot and the junk filter + * ---------------- + */ + tuple = slot->val; + + tupType = (TupleDesc) junkfilter->jf_tupType; + cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType; + cleanLength = junkfilter->jf_cleanLength; + cleanMap = junkfilter->jf_cleanMap; + + /* --------------------- + * Handle the trivial case first. + * --------------------- + */ + if (cleanLength == 0) + return (HeapTuple) NULL; + + /* --------------------- + * Create the arrays that will hold the attribute values + * and the null information for the new "clean" tuple. + * + * Note: we use memory on the stack to optimize things when + * we are dealing with a small number of tuples. + * for large tuples we just use palloc. + * --------------------- + */ + if (cleanLength > 64) + { + size = cleanLength * sizeof(Datum); + values = (Datum *) palloc(size); + + size = cleanLength * sizeof(char); + nulls = (char *) palloc(size); + } else - nulls[i] = ' '; - } - - /* --------------------- - * Now form the new tuple. - * --------------------- - */ - cleanTuple = heap_formtuple(cleanTupType, - values, - nulls); - - /* --------------------- - * We are done. Free any space allocated for 'values' and 'nulls' - * and return the new tuple. - * --------------------- - */ - if (cleanLength > 64) { - pfree(values); - pfree(nulls); - } - - return(cleanTuple); -} + { + values = values_array; + nulls = nulls_array; + } + + /* --------------------- + * Exctract one by one all the values of the "clean" tuple. + * --------------------- + */ + for (i = 0; i < cleanLength; i++) + { + Datum d = (Datum) + heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull); + values[i] = d; + + if (isNull) + nulls[i] = 'n'; + else + nulls[i] = ' '; + } + + /* --------------------- + * Now form the new tuple. + * --------------------- + */ + cleanTuple = heap_formtuple(cleanTupType, + values, + nulls); + + /* --------------------- + * We are done. Free any space allocated for 'values' and 'nulls' + * and return the new tuple. + * --------------------- + */ + if (cleanLength > 64) + { + pfree(values); + pfree(nulls); + } + + return (cleanTuple); +} diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 48bf84ba095..2bf0edaf35e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1,32 +1,32 @@ /*------------------------------------------------------------------------- * * execMain.c-- - * top level executor interface routines + * top level executor interface routines * * INTERFACE ROUTINES - * ExecutorStart() - * ExecutorRun() - * ExecutorEnd() + * ExecutorStart() + * ExecutorRun() + * ExecutorEnd() * - * The old ExecutorMain() has been replaced by ExecutorStart(), - * ExecutorRun() and ExecutorEnd() + * The old ExecutorMain() has been replaced by ExecutorStart(), + * ExecutorRun() and ExecutorEnd() + * + * These three procedures are the external interfaces to the executor. + * In each case, the query descriptor and the execution state is required + * as arguments + * + * ExecutorStart() must be called at the beginning of any execution of any + * query plan and ExecutorEnd() should always be called at the end of + * execution of a plan. + * + * ExecutorRun accepts 'feature' and 'count' arguments that specify whether + * the plan is to be executed forwards, backwards, and for how many tuples. * - * These three procedures are the external interfaces to the executor. - * In each case, the query descriptor and the execution state is required - * as arguments - * - * ExecutorStart() must be called at the beginning of any execution of any - * query plan and ExecutorEnd() should always be called at the end of - * execution of a plan. - * - * ExecutorRun accepts 'feature' and 'count' arguments that specify whether - * the plan is to be executed forwards, backwards, and for how many tuples. - * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.22 1997/09/04 13:22:36 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.23 1997/09/07 04:41:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -42,7 +42,7 @@ #include "utils/palloc.h" #include "utils/acl.h" #include "utils/syscache.h" -#include "parser/parsetree.h" /* rt_fetch() */ +#include "parser/parsetree.h" /* rt_fetch() */ #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" @@ -56,28 +56,35 @@ /* decls for local routines only used within this module */ -static void ExecCheckPerms(CmdType operation, int resultRelation, List *rangeTable, - Query *parseTree); -static TupleDesc InitPlan(CmdType operation, Query *parseTree, - Plan *plan, EState *estate); -static void EndPlan(Plan *plan, EState *estate); -static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan, - Query *parseTree, CmdType operation, - int numberTuples, ScanDirection direction, - void (*printfunc)()); -static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc)(), - EState *estate); -static void ExecAppend(TupleTableSlot *slot,ItemPointer tupleid, - EState *estate); -static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid, - EState *estate); -static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid, - EState *estate, Query *parseTree); +static void +ExecCheckPerms(CmdType operation, int resultRelation, List * rangeTable, + Query * parseTree); +static TupleDesc +InitPlan(CmdType operation, Query * parseTree, + Plan * plan, EState * estate); +static void EndPlan(Plan * plan, EState * estate); +static TupleTableSlot * +ExecutePlan(EState * estate, Plan * plan, + Query * parseTree, CmdType operation, + int numberTuples, ScanDirection direction, + void (*printfunc) ()); +static void ExecRetrieve(TupleTableSlot * slot, void (*printfunc) (), + EState * estate); +static void +ExecAppend(TupleTableSlot * slot, ItemPointer tupleid, + EState * estate); +static void +ExecDelete(TupleTableSlot * slot, ItemPointer tupleid, + EState * estate); +static void +ExecReplace(TupleTableSlot * slot, ItemPointer tupleid, + EState * estate, Query * parseTree); /* end of local decls */ #ifdef QUERY_LIMIT -static int queryLimit = ALL_TUPLES; +static int queryLimit = ALL_TUPLES; + #undef ALL_TUPLES #define ALL_TUPLES queryLimit @@ -85,574 +92,605 @@ static int queryLimit = ALL_TUPLES; int ExecutorLimit(int limit) { - return queryLimit = limit; + return queryLimit = limit; } + #endif #endif /* ---------------------------------------------------------------- - * ExecutorStart - * - * This routine must be called at the beginning of any execution of any - * query plan - * - * returns (AttrInfo*) which describes the attributes of the tuples to - * be returned by the query. + * ExecutorStart + * + * This routine must be called at the beginning of any execution of any + * query plan + * + * returns (AttrInfo*) which describes the attributes of the tuples to + * be returned by the query. * * ---------------------------------------------------------------- */ TupleDesc -ExecutorStart(QueryDesc *queryDesc, EState *estate) +ExecutorStart(QueryDesc * queryDesc, EState * estate) { - TupleDesc result; - - /* sanity checks */ - Assert(queryDesc!=NULL); - - result = InitPlan(queryDesc->operation, - queryDesc->parsetree, - queryDesc->plantree, - estate); - - /* reset buffer refcount. the current refcounts - * are saved and will be restored when ExecutorEnd is called - * - * this makes sure that when ExecutorRun's are - * called recursively as for postquel functions, - * the buffers pinned by one ExecutorRun will not be - * unpinned by another ExecutorRun. - */ - BufferRefCountReset(estate->es_refcount); - - return result; + TupleDesc result; + + /* sanity checks */ + Assert(queryDesc != NULL); + + result = InitPlan(queryDesc->operation, + queryDesc->parsetree, + queryDesc->plantree, + estate); + + /* + * reset buffer refcount. the current refcounts are saved and will be + * restored when ExecutorEnd is called + * + * this makes sure that when ExecutorRun's are called recursively as for + * postquel functions, the buffers pinned by one ExecutorRun will not + * be unpinned by another ExecutorRun. + */ + BufferRefCountReset(estate->es_refcount); + + return result; } /* ---------------------------------------------------------------- - * ExecutorRun - * - * This is the main routine of the executor module. It accepts - * the query descriptor from the traffic cop and executes the - * query plan. - * - * ExecutorStart must have been called already. + * ExecutorRun + * + * This is the main routine of the executor module. It accepts + * the query descriptor from the traffic cop and executes the + * query plan. + * + * ExecutorStart must have been called already. * - * the different features supported are: - * EXEC_RUN: retrieve all tuples in the forward direction - * EXEC_FOR: retrieve 'count' number of tuples in the forward dir - * EXEC_BACK: retrieve 'count' number of tuples in the backward dir - * EXEC_RETONE: return one tuple but don't 'retrieve' it - * used in postquel function processing + * the different features supported are: + * EXEC_RUN: retrieve all tuples in the forward direction + * EXEC_FOR: retrieve 'count' number of tuples in the forward dir + * EXEC_BACK: retrieve 'count' number of tuples in the backward dir + * EXEC_RETONE: return one tuple but don't 'retrieve' it + * used in postquel function processing * * * ---------------------------------------------------------------- */ -TupleTableSlot* -ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count) +TupleTableSlot * +ExecutorRun(QueryDesc * queryDesc, EState * estate, int feature, int count) { - CmdType operation; - Query *parseTree; - Plan *plan; - TupleTableSlot *result; - CommandDest dest; - void (*destination)(); - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(queryDesc!=NULL); - - /* ---------------- - * extract information from the query descriptor - * and the query feature. - * ---------------- - */ - operation = queryDesc->operation; - parseTree = queryDesc->parsetree; - plan = queryDesc->plantree; - dest = queryDesc->dest; - destination = (void (*)()) DestToFunction(dest); - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; + CmdType operation; + Query *parseTree; + Plan *plan; + TupleTableSlot *result; + CommandDest dest; + void (*destination) (); -#if 0 - /* - * It doesn't work in common case (i.g. if function has a aggregate). - * Now we store parameter values before ExecutorStart. - vadim 01/22/97 - */ -#ifdef INDEXSCAN_PATCH - /* - * If the plan is an index scan and some of the scan key are - * function arguments rescan the indices after the parameter - * values have been stored in the execution state. DZ - 27-8-1996 - */ - if ((nodeTag(plan) == T_IndexScan) && - (((IndexScan *)plan)->indxstate->iss_RuntimeKeyInfo != NULL)) { - ExprContext *econtext; - econtext = ((IndexScan *)plan)->scan.scanstate->cstate.cs_ExprContext; - ExecIndexReScan((IndexScan *)plan, econtext, plan); - } -#endif -#endif - - switch(feature) { - - case EXEC_RUN: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - ALL_TUPLES, - ForwardScanDirection, - destination); - break; - case EXEC_FOR: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - count, - ForwardScanDirection, - destination); - break; - /* ---------------- - * retrieve next n "backward" tuples + * sanity checks * ---------------- */ - case EXEC_BACK: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - count, - BackwardScanDirection, - destination); - break; - + Assert(queryDesc != NULL); + /* ---------------- - * return one tuple but don't "retrieve" it. - * (this is used by the rule manager..) -cim 9/14/89 + * extract information from the query descriptor + * and the query feature. * ---------------- */ - case EXEC_RETONE: - result = ExecutePlan(estate, - plan, - parseTree, - operation, - ONE_TUPLE, - ForwardScanDirection, - destination); - break; - default: - result = NULL; - elog(DEBUG, "ExecutorRun: Unknown feature %d", feature); - break; - } + operation = queryDesc->operation; + parseTree = queryDesc->parsetree; + plan = queryDesc->plantree; + dest = queryDesc->dest; + destination = (void (*) ()) DestToFunction(dest); + estate->es_processed = 0; + estate->es_lastoid = InvalidOid; + +#if 0 + + /* + * It doesn't work in common case (i.g. if function has a aggregate). + * Now we store parameter values before ExecutorStart. - vadim + * 01/22/97 + */ +#ifdef INDEXSCAN_PATCH + + /* + * If the plan is an index scan and some of the scan key are function + * arguments rescan the indices after the parameter values have been + * stored in the execution state. DZ - 27-8-1996 + */ + if ((nodeTag(plan) == T_IndexScan) && + (((IndexScan *) plan)->indxstate->iss_RuntimeKeyInfo != NULL)) + { + ExprContext *econtext; + + econtext = ((IndexScan *) plan)->scan.scanstate->cstate.cs_ExprContext; + ExecIndexReScan((IndexScan *) plan, econtext, plan); + } +#endif +#endif + + switch (feature) + { + + case EXEC_RUN: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + ALL_TUPLES, + ForwardScanDirection, + destination); + break; + case EXEC_FOR: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + count, + ForwardScanDirection, + destination); + break; - return result; + /* ---------------- + * retrieve next n "backward" tuples + * ---------------- + */ + case EXEC_BACK: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + count, + BackwardScanDirection, + destination); + break; + + /* ---------------- + * return one tuple but don't "retrieve" it. + * (this is used by the rule manager..) -cim 9/14/89 + * ---------------- + */ + case EXEC_RETONE: + result = ExecutePlan(estate, + plan, + parseTree, + operation, + ONE_TUPLE, + ForwardScanDirection, + destination); + break; + default: + result = NULL; + elog(DEBUG, "ExecutorRun: Unknown feature %d", feature); + break; + } + + return result; } /* ---------------------------------------------------------------- - * ExecutorEnd - * - * This routine must be called at the end of any execution of any - * query plan - * - * returns (AttrInfo*) which describes the attributes of the tuples to - * be returned by the query. + * ExecutorEnd + * + * This routine must be called at the end of any execution of any + * query plan + * + * returns (AttrInfo*) which describes the attributes of the tuples to + * be returned by the query. * * ---------------------------------------------------------------- */ void -ExecutorEnd(QueryDesc *queryDesc, EState *estate) +ExecutorEnd(QueryDesc * queryDesc, EState * estate) { - /* sanity checks */ - Assert(queryDesc!=NULL); + /* sanity checks */ + Assert(queryDesc != NULL); - EndPlan(queryDesc->plantree, estate); + EndPlan(queryDesc->plantree, estate); - /* restore saved refcounts. */ - BufferRefCountRestore(estate->es_refcount); + /* restore saved refcounts. */ + BufferRefCountRestore(estate->es_refcount); } /* =============================================================== * =============================================================== - static routines follow + static routines follow * =============================================================== * =============================================================== */ static void ExecCheckPerms(CmdType operation, - int resultRelation, - List *rangeTable, - Query *parseTree) + int resultRelation, + List * rangeTable, + Query * parseTree) { - int i = 1; - Oid relid; - HeapTuple htp; - List *lp; - List *qvars, *tvars; - int32 ok = 1, aclcheck_result = -1; - char *opstr; - NameData rname; - char *userName; - -#define CHECK(MODE) pg_aclcheck(rname.data, userName, MODE) - - userName = GetPgUserName(); - - foreach (lp, rangeTable) { - RangeTblEntry *rte = lfirst(lp); - - relid = rte->relid; - htp = SearchSysCacheTuple(RELOID, - ObjectIdGetDatum(relid), - 0,0,0); - if (!HeapTupleIsValid(htp)) - elog(WARN, "ExecCheckPerms: bogus RT relid: %d", - relid); - strNcpy(rname.data, - ((Form_pg_class) GETSTRUCT(htp))->relname.data, - NAMEDATALEN-1); - if (i == resultRelation) { /* this is the result relation */ - qvars = pull_varnos(parseTree->qual); - tvars = pull_varnos((Node*)parseTree->targetList); - if (intMember(resultRelation, qvars) || - intMember(resultRelation, tvars)) { - /* result relation is scanned */ - ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); - opstr = "read"; + int i = 1; + Oid relid; + HeapTuple htp; + List *lp; + List *qvars, + *tvars; + int32 ok = 1, + aclcheck_result = -1; + char *opstr; + NameData rname; + char *userName; + +#define CHECK(MODE) pg_aclcheck(rname.data, userName, MODE) + + userName = GetPgUserName(); + + foreach(lp, rangeTable) + { + RangeTblEntry *rte = lfirst(lp); + + relid = rte->relid; + htp = SearchSysCacheTuple(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(htp)) + elog(WARN, "ExecCheckPerms: bogus RT relid: %d", + relid); + strNcpy(rname.data, + ((Form_pg_class) GETSTRUCT(htp))->relname.data, + NAMEDATALEN - 1); + if (i == resultRelation) + { /* this is the result relation */ + qvars = pull_varnos(parseTree->qual); + tvars = pull_varnos((Node *) parseTree->targetList); + if (intMember(resultRelation, qvars) || + intMember(resultRelation, tvars)) + { + /* result relation is scanned */ + ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); + opstr = "read"; + if (!ok) + break; + } + switch (operation) + { + case CMD_INSERT: + ok = ((aclcheck_result = CHECK(ACL_AP)) == ACLCHECK_OK) || + ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); + opstr = "append"; + break; + case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */ + case CMD_DELETE: + case CMD_UPDATE: + ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); + opstr = "write"; + break; + default: + elog(WARN, "ExecCheckPerms: bogus operation %d", + operation); + } + } + else + { + /* XXX NOTIFY?? */ + ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); + opstr = "read"; + } if (!ok) - break; - } - switch (operation) { - case CMD_INSERT: - ok = ((aclcheck_result = CHECK(ACL_AP)) == ACLCHECK_OK) || - ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); - opstr = "append"; - break; - case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */ - case CMD_DELETE: - case CMD_UPDATE: - ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK); - opstr = "write"; - break; - default: - elog(WARN, "ExecCheckPerms: bogus operation %d", - operation); - } - } else { - /* XXX NOTIFY?? */ - ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK); - opstr = "read"; + break; + ++i; } if (!ok) - break; - ++i; - } - if (!ok) { - elog(WARN, "%s: %s", rname.data, aclcheck_error_strings[aclcheck_result]); - } + { + elog(WARN, "%s: %s", rname.data, aclcheck_error_strings[aclcheck_result]); + } } /* ---------------------------------------------------------------- - * InitPlan - * - * Initializes the query plan: open files, allocate storage - * and start up the rule manager + * InitPlan + * + * Initializes the query plan: open files, allocate storage + * and start up the rule manager * ---------------------------------------------------------------- */ -static TupleDesc -InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) -{ - List *rangeTable; - int resultRelation; - Relation intoRelationDesc; - - TupleDesc tupType; - List *targetList; - int len; - - /* ---------------- - * get information from query descriptor - * ---------------- - */ - rangeTable = parseTree->rtable; - resultRelation = parseTree->resultRelation; - - /* ---------------- - * initialize the node's execution state - * ---------------- - */ - estate->es_range_table = rangeTable; - - /* ---------------- - * initialize the BaseId counter so node base_id's - * are assigned correctly. Someday baseid's will have to - * be stored someplace other than estate because they - * should be unique per query planned. - * ---------------- - */ - estate->es_BaseId = 1; - - /* ---------------- - * initialize result relation stuff - * ---------------- - */ - - if (resultRelation != 0 && operation != CMD_SELECT) { - /* ---------------- - * if we have a result relation, open it and +static TupleDesc +InitPlan(CmdType operation, Query * parseTree, Plan * plan, EState * estate) +{ + List *rangeTable; + int resultRelation; + Relation intoRelationDesc; + + TupleDesc tupType; + List *targetList; + int len; - * initialize the result relation info stuff. + /* ---------------- + * get information from query descriptor * ---------------- */ - RelationInfo *resultRelationInfo; - Index resultRelationIndex; - RangeTblEntry *rtentry; - Oid resultRelationOid; - Relation resultRelationDesc; - - resultRelationIndex = resultRelation; - rtentry = rt_fetch(resultRelationIndex, rangeTable); - resultRelationOid = rtentry->relid; - resultRelationDesc = heap_open(resultRelationOid); - - if ( resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE ) - elog (WARN, "You can't change sequence relation %s", - resultRelationDesc->rd_rel->relname.data); - - /* Write-lock the result relation right away: if the relation - is used in a subsequent scan, we won't have to elevate the - read-lock set by heap_beginscan to a write-lock (needed by - heap_insert, heap_delete and heap_replace). - This will hopefully prevent some deadlocks. - 01/24/94 */ - RelationSetLockForWrite(resultRelationDesc); - - resultRelationInfo = makeNode(RelationInfo); - resultRelationInfo->ri_RangeTableIndex = resultRelationIndex; - resultRelationInfo->ri_RelationDesc = resultRelationDesc; - resultRelationInfo->ri_NumIndices = 0; - resultRelationInfo->ri_IndexRelationDescs = NULL; - resultRelationInfo->ri_IndexRelationInfo = NULL; + rangeTable = parseTree->rtable; + resultRelation = parseTree->resultRelation; /* ---------------- - * open indices on result relation and save descriptors - * in the result relation information.. + * initialize the node's execution state * ---------------- */ - ExecOpenIndices(resultRelationOid, resultRelationInfo); - - estate->es_result_relation_info = resultRelationInfo; - } else { + estate->es_range_table = rangeTable; + /* ---------------- - * if no result relation, then set state appropriately + * initialize the BaseId counter so node base_id's + * are assigned correctly. Someday baseid's will have to + * be stored someplace other than estate because they + * should be unique per query planned. * ---------------- */ - estate->es_result_relation_info = NULL; - } + estate->es_BaseId = 1; -#ifndef NO_SECURITY - ExecCheckPerms(operation, resultRelation, rangeTable, parseTree); -#endif + /* ---------------- + * initialize result relation stuff + * ---------------- + */ - /* ---------------- - * initialize the executor "tuple" table. - * ---------------- - */ - { - int nSlots = ExecCountSlotsNode(plan); - TupleTable tupleTable = ExecCreateTupleTable(nSlots+10); /* why add ten? - jolly */ - - estate->es_tupleTable = tupleTable; - } - - /* ---------------- - * initialize the private state information for - * all the nodes in the query tree. This opens - * files, allocates storage and leaves us ready - * to start processing tuples.. - * ---------------- - */ - ExecInitNode(plan, estate, NULL); - - /* ---------------- - * get the tuple descriptor describing the type - * of tuples to return.. (this is especially important - * if we are creating a relation with "retrieve into") - * ---------------- - */ - tupType = ExecGetTupType(plan); /* tuple descriptor */ - targetList = plan->targetlist; - len = ExecTargetListLength(targetList); /* number of attributes */ - - /* ---------------- - * now that we have the target list, initialize the junk filter - * if this is a REPLACE or a DELETE query. - * We also init the junk filter if this is an append query - * (there might be some rule lock info there...) - * NOTE: in the future we might want to initialize the junk - * filter for all queries. - * ---------------- - */ - if (operation == CMD_UPDATE || operation == CMD_DELETE || - operation == CMD_INSERT) { - - JunkFilter *j = (JunkFilter*) ExecInitJunkFilter(targetList); - estate->es_junkFilter = j; - } else - estate->es_junkFilter = NULL; - - /* ---------------- - * initialize the "into" relation - * ---------------- - */ - intoRelationDesc = (Relation) NULL; - - if (operation == CMD_SELECT) { - char *intoName; - char archiveMode; - Oid intoRelationId; - TupleDesc tupdesc; - - if (!parseTree->isPortal) { - /* - * a select into table - */ - if (parseTree->into != NULL) { + if (resultRelation != 0 && operation != CMD_SELECT) + { /* ---------------- - * create the "into" relation - * - * note: there is currently no way for the user to - * specify the desired archive mode of the - * "into" relation... + * if we have a result relation, open it and + + * initialize the result relation info stuff. * ---------------- */ - intoName = parseTree->into; - archiveMode = 'n'; - + RelationInfo *resultRelationInfo; + Index resultRelationIndex; + RangeTblEntry *rtentry; + Oid resultRelationOid; + Relation resultRelationDesc; + + resultRelationIndex = resultRelation; + rtentry = rt_fetch(resultRelationIndex, rangeTable); + resultRelationOid = rtentry->relid; + resultRelationDesc = heap_open(resultRelationOid); + + if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE) + elog(WARN, "You can't change sequence relation %s", + resultRelationDesc->rd_rel->relname.data); + /* - * have to copy tupType to get rid of constraints + * Write-lock the result relation right away: if the relation is + * used in a subsequent scan, we won't have to elevate the + * read-lock set by heap_beginscan to a write-lock (needed by + * heap_insert, heap_delete and heap_replace). This will hopefully + * prevent some deadlocks. - 01/24/94 */ - tupdesc = CreateTupleDescCopy (tupType); - - /* fixup to prevent zero-length columns in create */ - setVarAttrLenForCreateTable(tupdesc, targetList, rangeTable); - - intoRelationId = heap_create(intoName, - intoName, /* not used */ - archiveMode, - DEFAULT_SMGR, - tupdesc); -#ifdef NOT_USED /* it's copy ... */ - resetVarAttrLenForCreateTable(tupdesc); -#endif - FreeTupleDesc (tupdesc); + RelationSetLockForWrite(resultRelationDesc); + + resultRelationInfo = makeNode(RelationInfo); + resultRelationInfo->ri_RangeTableIndex = resultRelationIndex; + resultRelationInfo->ri_RelationDesc = resultRelationDesc; + resultRelationInfo->ri_NumIndices = 0; + resultRelationInfo->ri_IndexRelationDescs = NULL; + resultRelationInfo->ri_IndexRelationInfo = NULL; /* ---------------- - * XXX rather than having to call setheapoverride(true) - * and then back to false, we should change the - * arguments to heap_open() instead.. + * open indices on result relation and save descriptors + * in the result relation information.. * ---------------- */ - setheapoverride(true); - - intoRelationDesc = heap_open(intoRelationId); - - setheapoverride(false); - } + ExecOpenIndices(resultRelationOid, resultRelationInfo); + + estate->es_result_relation_info = resultRelationInfo; } - } + else + { + /* ---------------- + * if no result relation, then set state appropriately + * ---------------- + */ + estate->es_result_relation_info = NULL; + } + +#ifndef NO_SECURITY + ExecCheckPerms(operation, resultRelation, rangeTable, parseTree); +#endif + + /* ---------------- + * initialize the executor "tuple" table. + * ---------------- + */ + { + int nSlots = ExecCountSlotsNode(plan); + TupleTable tupleTable = ExecCreateTupleTable(nSlots + 10); /* why add ten? - jolly */ - estate->es_into_relation_descriptor = intoRelationDesc; + estate->es_tupleTable = tupleTable; + } - /* ---------------- - * return the type information.. - * ---------------- - */ + /* ---------------- + * initialize the private state information for + * all the nodes in the query tree. This opens + * files, allocates storage and leaves us ready + * to start processing tuples.. + * ---------------- + */ + ExecInitNode(plan, estate, NULL); + + /* ---------------- + * get the tuple descriptor describing the type + * of tuples to return.. (this is especially important + * if we are creating a relation with "retrieve into") + * ---------------- + */ + tupType = ExecGetTupType(plan); /* tuple descriptor */ + targetList = plan->targetlist; + len = ExecTargetListLength(targetList); /* number of attributes */ + + /* ---------------- + * now that we have the target list, initialize the junk filter + * if this is a REPLACE or a DELETE query. + * We also init the junk filter if this is an append query + * (there might be some rule lock info there...) + * NOTE: in the future we might want to initialize the junk + * filter for all queries. + * ---------------- + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE || + operation == CMD_INSERT) + { + + JunkFilter *j = (JunkFilter *) ExecInitJunkFilter(targetList); + + estate->es_junkFilter = j; + } + else + estate->es_junkFilter = NULL; + + /* ---------------- + * initialize the "into" relation + * ---------------- + */ + intoRelationDesc = (Relation) NULL; + + if (operation == CMD_SELECT) + { + char *intoName; + char archiveMode; + Oid intoRelationId; + TupleDesc tupdesc; + + if (!parseTree->isPortal) + { + + /* + * a select into table + */ + if (parseTree->into != NULL) + { + /* ---------------- + * create the "into" relation + * + * note: there is currently no way for the user to + * specify the desired archive mode of the + * "into" relation... + * ---------------- + */ + intoName = parseTree->into; + archiveMode = 'n'; + + /* + * have to copy tupType to get rid of constraints + */ + tupdesc = CreateTupleDescCopy(tupType); + + /* fixup to prevent zero-length columns in create */ + setVarAttrLenForCreateTable(tupdesc, targetList, rangeTable); + + intoRelationId = heap_create(intoName, + intoName, /* not used */ + archiveMode, + DEFAULT_SMGR, + tupdesc); +#ifdef NOT_USED /* it's copy ... */ + resetVarAttrLenForCreateTable(tupdesc); +#endif + FreeTupleDesc(tupdesc); + + /* ---------------- + * XXX rather than having to call setheapoverride(true) + * and then back to false, we should change the + * arguments to heap_open() instead.. + * ---------------- + */ + setheapoverride(true); + + intoRelationDesc = heap_open(intoRelationId); + + setheapoverride(false); + } + } + } + + estate->es_into_relation_descriptor = intoRelationDesc; + + /* ---------------- + * return the type information.. + * ---------------- + */ /* - attinfo = (AttrInfo *)palloc(sizeof(AttrInfo)); - attinfo->numAttr = len; - attinfo->attrs = tupType->attrs; + attinfo = (AttrInfo *)palloc(sizeof(AttrInfo)); + attinfo->numAttr = len; + attinfo->attrs = tupType->attrs; */ - return tupType; + return tupType; } /* ---------------------------------------------------------------- - * EndPlan - * - * Cleans up the query plan -- closes files and free up storages + * EndPlan + * + * Cleans up the query plan -- closes files and free up storages * ---------------------------------------------------------------- */ static void -EndPlan(Plan *plan, EState *estate) +EndPlan(Plan * plan, EState * estate) { - RelationInfo *resultRelationInfo; - Relation intoRelationDesc; - - /* ---------------- - * get information from state - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - intoRelationDesc = estate->es_into_relation_descriptor; - - /* ---------------- - * shut down the query - * ---------------- - */ - ExecEndNode(plan, plan); - - /* ---------------- - * destroy the executor "tuple" table. - * ---------------- - */ - { - TupleTable tupleTable = (TupleTable) estate->es_tupleTable; - ExecDestroyTupleTable(tupleTable,true); /* was missing last arg */ - estate->es_tupleTable = NULL; - } - - /* ---------------- - * close the result relations if necessary - * ---------------- - */ - if (resultRelationInfo != NULL) { - Relation resultRelationDesc; - - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - heap_close(resultRelationDesc); - + RelationInfo *resultRelationInfo; + Relation intoRelationDesc; + /* ---------------- - * close indices on the result relation + * get information from state * ---------------- */ - ExecCloseIndices(resultRelationInfo); - } - - /* ---------------- - * close the "into" relation if necessary - * ---------------- - */ - if (intoRelationDesc != NULL) { - heap_close(intoRelationDesc); - } + resultRelationInfo = estate->es_result_relation_info; + intoRelationDesc = estate->es_into_relation_descriptor; + + /* ---------------- + * shut down the query + * ---------------- + */ + ExecEndNode(plan, plan); + + /* ---------------- + * destroy the executor "tuple" table. + * ---------------- + */ + { + TupleTable tupleTable = (TupleTable) estate->es_tupleTable; + + ExecDestroyTupleTable(tupleTable, true); /* was missing last arg */ + estate->es_tupleTable = NULL; + } + + /* ---------------- + * close the result relations if necessary + * ---------------- + */ + if (resultRelationInfo != NULL) + { + Relation resultRelationDesc; + + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + heap_close(resultRelationDesc); + + /* ---------------- + * close indices on the result relation + * ---------------- + */ + ExecCloseIndices(resultRelationInfo); + } + + /* ---------------- + * close the "into" relation if necessary + * ---------------- + */ + if (intoRelationDesc != NULL) + { + heap_close(intoRelationDesc); + } } /* ---------------------------------------------------------------- - * ExecutePlan - * - * processes the query plan to retrieve 'tupleCount' tuples in the - * direction specified. - * Retrieves all tuples if tupleCount is 0 + * ExecutePlan + * + * processes the query plan to retrieve 'tupleCount' tuples in the + * direction specified. + * Retrieves all tuples if tupleCount is 0 + * + * result is either a slot containing a tuple in the case + * of a RETRIEVE or NULL otherwise. * - * result is either a slot containing a tuple in the case - * of a RETRIEVE or NULL otherwise. - * * ---------------------------------------------------------------- */ @@ -660,688 +698,704 @@ EndPlan(Plan *plan, EState *estate) user can see it*/ static TupleTableSlot * -ExecutePlan(EState *estate, - Plan *plan, - Query *parseTree, - CmdType operation, - int numberTuples, - ScanDirection direction, - void (*printfunc)()) +ExecutePlan(EState * estate, + Plan * plan, + Query * parseTree, + CmdType operation, + int numberTuples, + ScanDirection direction, + void (*printfunc) ()) { - JunkFilter *junkfilter; - - TupleTableSlot *slot; - ItemPointer tupleid = NULL; - ItemPointerData tuple_ctid; - int current_tuple_count; - TupleTableSlot *result; - - /* ---------------- - * initialize local variables - * ---------------- - */ - slot = NULL; - current_tuple_count = 0; - result = NULL; - - /* ---------------- - * Set the direction. - * ---------------- - */ - estate->es_direction = direction; - - /* ---------------- - * Loop until we've processed the proper number - * of tuples from the plan.. - * ---------------- - */ - - for(;;) { - if (operation != CMD_NOTIFY) { - /* ---------------- - * Execute the plan and obtain a tuple - * ---------------- - */ - /* at the top level, the parent of a plan (2nd arg) is itself */ - slot = ExecProcNode(plan,plan); - - /* ---------------- - * if the tuple is null, then we assume - * there is nothing more to process so - * we just return null... - * ---------------- - */ - if (TupIsNull(slot)) { - result = NULL; - break; - } - } - + JunkFilter *junkfilter; + + TupleTableSlot *slot; + ItemPointer tupleid = NULL; + ItemPointerData tuple_ctid; + int current_tuple_count; + TupleTableSlot *result; + /* ---------------- - * if we have a junk filter, then project a new - * tuple with the junk removed. - * - * Store this new "clean" tuple in the place of the - * original tuple. - * - * Also, extract all the junk ifnormation we need. + * initialize local variables * ---------------- */ - if ((junkfilter = estate->es_junkFilter) != (JunkFilter*)NULL) { - Datum datum; -/* NameData attrName; */ - HeapTuple newTuple; - bool isNull; - - /* --------------- - * extract the 'ctid' junk attribute. - * --------------- - */ - if (operation == CMD_UPDATE || operation == CMD_DELETE) { - if (! ExecGetJunkAttribute(junkfilter, - slot, - "ctid", - &datum, - &isNull)) - elog(WARN,"ExecutePlan: NO (junk) `ctid' was found!"); - - if (isNull) - elog(WARN,"ExecutePlan: (junk) `ctid' is NULL!"); - - tupleid = (ItemPointer) DatumGetPointer(datum); - tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */ - tupleid = &tuple_ctid; - } - - /* --------------- - * Finally create a new "clean" tuple with all junk attributes - * removed - * --------------- - */ - newTuple = ExecRemoveJunk(junkfilter, slot); - - slot = ExecStoreTuple(newTuple, /* tuple to store */ - slot, /* destination slot */ - InvalidBuffer,/* this tuple has no buffer */ - true); /* tuple should be pfreed */ - } /* if (junkfilter... */ - + slot = NULL; + current_tuple_count = 0; + result = NULL; + /* ---------------- - * now that we have a tuple, do the appropriate thing - * with it.. either return it to the user, add - * it to a relation someplace, delete it from a - * relation, or modify some of it's attributes. + * Set the direction. * ---------------- */ - - switch(operation) { - case CMD_SELECT: - ExecRetrieve(slot, /* slot containing tuple */ - printfunc, /* print function */ - estate); /* */ - result = slot; - break; - - case CMD_INSERT: - ExecAppend(slot, tupleid, estate); - result = NULL; - break; - - case CMD_DELETE: - ExecDelete(slot, tupleid, estate); - result = NULL; - break; - - case CMD_UPDATE: - ExecReplace(slot, tupleid, estate, parseTree); - result = NULL; - break; - - /* Total hack. I'm ignoring any accessor functions for - Relation, RelationTupleForm, NameData. - Assuming that NameData.data has offset 0. - */ - case CMD_NOTIFY: { - RelationInfo *rInfo = estate->es_result_relation_info; - Relation rDesc = rInfo->ri_RelationDesc; - Async_Notify(rDesc->rd_rel->relname.data); - result = NULL; - current_tuple_count = 0; - numberTuples = 1; - elog(DEBUG, "ExecNotify %s",&rDesc->rd_rel->relname); - } - break; - - default: - elog(DEBUG, "ExecutePlan: unknown operation in queryDesc"); - result = NULL; - break; + estate->es_direction = direction; + + /* ---------------- + * Loop until we've processed the proper number + * of tuples from the plan.. + * ---------------- + */ + + for (;;) + { + if (operation != CMD_NOTIFY) + { + /* ---------------- + * Execute the plan and obtain a tuple + * ---------------- + */ + /* at the top level, the parent of a plan (2nd arg) is itself */ + slot = ExecProcNode(plan, plan); + + /* ---------------- + * if the tuple is null, then we assume + * there is nothing more to process so + * we just return null... + * ---------------- + */ + if (TupIsNull(slot)) + { + result = NULL; + break; + } + } + + /* ---------------- + * if we have a junk filter, then project a new + * tuple with the junk removed. + * + * Store this new "clean" tuple in the place of the + * original tuple. + * + * Also, extract all the junk ifnormation we need. + * ---------------- + */ + if ((junkfilter = estate->es_junkFilter) != (JunkFilter *) NULL) + { + Datum datum; + +/* NameData attrName; */ + HeapTuple newTuple; + bool isNull; + + /* --------------- + * extract the 'ctid' junk attribute. + * --------------- + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE) + { + if (!ExecGetJunkAttribute(junkfilter, + slot, + "ctid", + &datum, + &isNull)) + elog(WARN, "ExecutePlan: NO (junk) `ctid' was found!"); + + if (isNull) + elog(WARN, "ExecutePlan: (junk) `ctid' is NULL!"); + + tupleid = (ItemPointer) DatumGetPointer(datum); + tuple_ctid = *tupleid; /* make sure we don't free the + * ctid!! */ + tupleid = &tuple_ctid; + } + + /* --------------- + * Finally create a new "clean" tuple with all junk attributes + * removed + * --------------- + */ + newTuple = ExecRemoveJunk(junkfilter, slot); + + slot = ExecStoreTuple(newTuple, /* tuple to store */ + slot, /* destination slot */ + InvalidBuffer, /* this tuple has no + * buffer */ + true); /* tuple should be pfreed */ + } /* if (junkfilter... */ + + /* ---------------- + * now that we have a tuple, do the appropriate thing + * with it.. either return it to the user, add + * it to a relation someplace, delete it from a + * relation, or modify some of it's attributes. + * ---------------- + */ + + switch (operation) + { + case CMD_SELECT: + ExecRetrieve(slot, /* slot containing tuple */ + printfunc, /* print function */ + estate); /* */ + result = slot; + break; + + case CMD_INSERT: + ExecAppend(slot, tupleid, estate); + result = NULL; + break; + + case CMD_DELETE: + ExecDelete(slot, tupleid, estate); + result = NULL; + break; + + case CMD_UPDATE: + ExecReplace(slot, tupleid, estate, parseTree); + result = NULL; + break; + + /* + * Total hack. I'm ignoring any accessor functions for + * Relation, RelationTupleForm, NameData. Assuming that + * NameData.data has offset 0. + */ + case CMD_NOTIFY: + { + RelationInfo *rInfo = estate->es_result_relation_info; + Relation rDesc = rInfo->ri_RelationDesc; + + Async_Notify(rDesc->rd_rel->relname.data); + result = NULL; + current_tuple_count = 0; + numberTuples = 1; + elog(DEBUG, "ExecNotify %s", &rDesc->rd_rel->relname); + } + break; + + default: + elog(DEBUG, "ExecutePlan: unknown operation in queryDesc"); + result = NULL; + break; + } + /* ---------------- + * check our tuple count.. if we've returned the + * proper number then return, else loop again and + * process more tuples.. + * ---------------- + */ + current_tuple_count += 1; + if (numberTuples == current_tuple_count) + break; } + /* ---------------- - * check our tuple count.. if we've returned the - * proper number then return, else loop again and - * process more tuples.. + * here, result is either a slot containing a tuple in the case + * of a RETRIEVE or NULL otherwise. * ---------------- */ - current_tuple_count += 1; - if (numberTuples == current_tuple_count) - break; - } - - /* ---------------- - * here, result is either a slot containing a tuple in the case - * of a RETRIEVE or NULL otherwise. - * ---------------- - */ - return result; + return result; } /* ---------------------------------------------------------------- - * ExecRetrieve + * ExecRetrieve * - * RETRIEVEs are easy.. we just pass the tuple to the appropriate - * print function. The only complexity is when we do a - * "retrieve into", in which case we insert the tuple into - * the appropriate relation (note: this is a newly created relation - * so we don't need to worry about indices or locks.) + * RETRIEVEs are easy.. we just pass the tuple to the appropriate + * print function. The only complexity is when we do a + * "retrieve into", in which case we insert the tuple into + * the appropriate relation (note: this is a newly created relation + * so we don't need to worry about indices or locks.) * ---------------------------------------------------------------- */ static void -ExecRetrieve(TupleTableSlot *slot, - void (*printfunc)(), - EState *estate) +ExecRetrieve(TupleTableSlot * slot, + void (*printfunc) (), + EState * estate) { - HeapTuple tuple; - TupleDesc attrtype; - - /* ---------------- - * get the heap tuple out of the tuple table slot - * ---------------- - */ - tuple = slot->val; - attrtype = slot->ttc_tupleDescriptor; - - /* ---------------- - * insert the tuple into the "into relation" - * ---------------- - */ - if ( estate->es_into_relation_descriptor != NULL ) - { - heap_insert (estate->es_into_relation_descriptor, tuple); - IncrAppended(); - } - - /* ---------------- - * send the tuple to the front end (or the screen) - * ---------------- - */ - (*printfunc)(tuple, attrtype); - IncrRetrieved(); - (estate->es_processed)++; + HeapTuple tuple; + TupleDesc attrtype; + + /* ---------------- + * get the heap tuple out of the tuple table slot + * ---------------- + */ + tuple = slot->val; + attrtype = slot->ttc_tupleDescriptor; + + /* ---------------- + * insert the tuple into the "into relation" + * ---------------- + */ + if (estate->es_into_relation_descriptor != NULL) + { + heap_insert(estate->es_into_relation_descriptor, tuple); + IncrAppended(); + } + + /* ---------------- + * send the tuple to the front end (or the screen) + * ---------------- + */ + (*printfunc) (tuple, attrtype); + IncrRetrieved(); + (estate->es_processed)++; } /* ---------------------------------------------------------------- - * ExecAppend + * ExecAppend * - * APPENDs are trickier.. we have to insert the tuple into - * the base relation and insert appropriate tuples into the - * index relations. + * APPENDs are trickier.. we have to insert the tuple into + * the base relation and insert appropriate tuples into the + * index relations. * ---------------------------------------------------------------- */ static void -ExecAppend(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate) +ExecAppend(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate) { - HeapTuple tuple; - RelationInfo *resultRelationInfo; - Relation resultRelationDesc; - int numIndices; - Oid newId; - - /* ---------------- - * get the heap tuple out of the tuple table slot - * ---------------- - */ - tuple = slot->val; - - /* ---------------- - * get information on the result relation - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - - /* ---------------- - * have to add code to preform unique checking here. - * cim -12/1/89 - * ---------------- - */ - - /* BEFORE ROW INSERT Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0 ) - { - HeapTuple newtuple; - - newtuple = ExecBRInsertTriggers (resultRelationDesc, tuple); - - if ( newtuple == NULL ) /* "do nothing" */ - return; - - if ( newtuple != tuple ) /* modified by Trigger(s) */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * Check the constraints of a tuple - * ---------------- - */ - - if ( resultRelationDesc->rd_att->constr ) - { - HeapTuple newtuple; - - newtuple = ExecConstraints ("ExecAppend", resultRelationDesc, tuple); - - if ( newtuple != tuple ) /* modified by DEFAULT */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * insert the tuple - * ---------------- - */ - newId = heap_insert(resultRelationDesc, /* relation desc */ - tuple); /* heap tuple */ - IncrAppended(); - - /* ---------------- - * process indices - * - * Note: heap_insert adds a new tuple to a relation. As a side - * effect, the tupleid of the new tuple is placed in the new - * tuple's t_ctid field. - * ---------------- - */ - numIndices = resultRelationInfo->ri_NumIndices; - if (numIndices > 0) { - ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, false); - } - (estate->es_processed)++; - estate->es_lastoid = newId; - - /* AFTER ROW INSERT Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 ) - ExecARInsertTriggers (resultRelationDesc, tuple); + HeapTuple tuple; + RelationInfo *resultRelationInfo; + Relation resultRelationDesc; + int numIndices; + Oid newId; + + /* ---------------- + * get the heap tuple out of the tuple table slot + * ---------------- + */ + tuple = slot->val; + + /* ---------------- + * get information on the result relation + * ---------------- + */ + resultRelationInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + + /* ---------------- + * have to add code to preform unique checking here. + * cim -12/1/89 + * ---------------- + */ + + /* BEFORE ROW INSERT Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) + { + HeapTuple newtuple; + + newtuple = ExecBRInsertTriggers(resultRelationDesc, tuple); + + if (newtuple == NULL) /* "do nothing" */ + return; + + if (newtuple != tuple) /* modified by Trigger(s) */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * Check the constraints of a tuple + * ---------------- + */ + + if (resultRelationDesc->rd_att->constr) + { + HeapTuple newtuple; + + newtuple = ExecConstraints("ExecAppend", resultRelationDesc, tuple); + + if (newtuple != tuple) /* modified by DEFAULT */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * insert the tuple + * ---------------- + */ + newId = heap_insert(resultRelationDesc, /* relation desc */ + tuple); /* heap tuple */ + IncrAppended(); + + /* ---------------- + * process indices + * + * Note: heap_insert adds a new tuple to a relation. As a side + * effect, the tupleid of the new tuple is placed in the new + * tuple's t_ctid field. + * ---------------- + */ + numIndices = resultRelationInfo->ri_NumIndices; + if (numIndices > 0) + { + ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, false); + } + (estate->es_processed)++; + estate->es_lastoid = newId; + + /* AFTER ROW INSERT Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0) + ExecARInsertTriggers(resultRelationDesc, tuple); } /* ---------------------------------------------------------------- - * ExecDelete + * ExecDelete * - * DELETE is like append, we delete the tuple and its - * index tuples. + * DELETE is like append, we delete the tuple and its + * index tuples. * ---------------------------------------------------------------- */ static void -ExecDelete(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate) +ExecDelete(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate) { - RelationInfo *resultRelationInfo; - Relation resultRelationDesc; - - /* ---------------- - * get the result relation information - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - - /* BEFORE ROW DELETE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0 ) - { - bool dodelete; - - dodelete = ExecBRDeleteTriggers (resultRelationDesc, tupleid); - - if ( !dodelete ) /* "do nothing" */ - return; - } - - /* ---------------- - * delete the tuple - * ---------------- - */ - if ( heap_delete(resultRelationDesc, /* relation desc */ - tupleid) ) /* item pointer to tuple */ - return; - - IncrDeleted(); - (estate->es_processed)++; - - /* ---------------- - * Note: Normally one would think that we have to - * delete index tuples associated with the - * heap tuple now.. - * - * ... but in POSTGRES, we have no need to do this - * because the vacuum daemon automatically - * opens an index scan and deletes index tuples - * when it finds deleted heap tuples. -cim 9/27/89 - * ---------------- - */ - - /* AFTER ROW DELETE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0 ) - ExecARDeleteTriggers (resultRelationDesc, tupleid); + RelationInfo *resultRelationInfo; + Relation resultRelationDesc; + + /* ---------------- + * get the result relation information + * ---------------- + */ + resultRelationInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + + /* BEFORE ROW DELETE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) + { + bool dodelete; + + dodelete = ExecBRDeleteTriggers(resultRelationDesc, tupleid); + + if (!dodelete) /* "do nothing" */ + return; + } + + /* ---------------- + * delete the tuple + * ---------------- + */ + if (heap_delete(resultRelationDesc, /* relation desc */ + tupleid)) /* item pointer to tuple */ + return; + + IncrDeleted(); + (estate->es_processed)++; + + /* ---------------- + * Note: Normally one would think that we have to + * delete index tuples associated with the + * heap tuple now.. + * + * ... but in POSTGRES, we have no need to do this + * because the vacuum daemon automatically + * opens an index scan and deletes index tuples + * when it finds deleted heap tuples. -cim 9/27/89 + * ---------------- + */ + + /* AFTER ROW DELETE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) + ExecARDeleteTriggers(resultRelationDesc, tupleid); } /* ---------------------------------------------------------------- - * ExecReplace + * ExecReplace * - * note: we can't run replace queries with transactions - * off because replaces are actually appends and our - * scan will mistakenly loop forever, replacing the tuple - * it just appended.. This should be fixed but until it - * is, we don't want to get stuck in an infinite loop - * which corrupts your database.. + * note: we can't run replace queries with transactions + * off because replaces are actually appends and our + * scan will mistakenly loop forever, replacing the tuple + * it just appended.. This should be fixed but until it + * is, we don't want to get stuck in an infinite loop + * which corrupts your database.. * ---------------------------------------------------------------- */ static void -ExecReplace(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate, - Query *parseTree) +ExecReplace(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate, + Query * parseTree) { - HeapTuple tuple; - RelationInfo *resultRelationInfo; - Relation resultRelationDesc; - int numIndices; - - /* ---------------- - * abort the operation if not running transactions - * ---------------- - */ - if (IsBootstrapProcessingMode()) { - elog(DEBUG, "ExecReplace: replace can't run without transactions"); - return; - } - - /* ---------------- - * get the heap tuple out of the tuple table slot - * ---------------- - */ - tuple = slot->val; - - /* ---------------- - * get the result relation information - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - - /* ---------------- - * have to add code to preform unique checking here. - * in the event of unique tuples, this becomes a deletion - * of the original tuple affected by the replace. - * cim -12/1/89 - * ---------------- - */ - - /* BEFORE ROW UPDATE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0 ) - { - HeapTuple newtuple; - - newtuple = ExecBRUpdateTriggers (resultRelationDesc, tupleid, tuple); - - if ( newtuple == NULL ) /* "do nothing" */ - return; - - if ( newtuple != tuple ) /* modified by Trigger(s) */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * Check the constraints of a tuple - * ---------------- - */ - - if ( resultRelationDesc->rd_att->constr ) - { - HeapTuple newtuple; - - newtuple = ExecConstraints ("ExecReplace", resultRelationDesc, tuple); - - if ( newtuple != tuple ) /* modified by DEFAULT */ - { - Assert ( slot->ttc_shouldFree ); - pfree (tuple); - slot->val = tuple = newtuple; - } - } - - /* ---------------- - * replace the heap tuple - * - * Don't want to continue if our heap_replace didn't actually - * do a replace. This would be the case if heap_replace - * detected a non-functional update. -kw 12/30/93 - * ---------------- - */ - if (heap_replace(resultRelationDesc, /* relation desc */ - tupleid, /* item ptr of tuple to replace */ - tuple)) { /* replacement heap tuple */ - return; - } - - IncrReplaced(); - (estate->es_processed)++; - - /* ---------------- - * Note: instead of having to update the old index tuples - * associated with the heap tuple, all we do is form - * and insert new index tuples.. This is because - * replaces are actually deletes and inserts and - * index tuple deletion is done automagically by - * the vaccuum deamon.. All we do is insert new - * index tuples. -cim 9/27/89 - * ---------------- - */ - - /* ---------------- - * process indices - * - * heap_replace updates a tuple in the base relation by invalidating - * it and then appending a new tuple to the relation. As a side - * effect, the tupleid of the new tuple is placed in the new - * tuple's t_ctid field. So we now insert index tuples using - * the new tupleid stored there. - * ---------------- - */ - - numIndices = resultRelationInfo->ri_NumIndices; - if (numIndices > 0) { - ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, true); - } - - /* AFTER ROW UPDATE Triggers */ - if ( resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ) - ExecARUpdateTriggers (resultRelationDesc, tupleid, tuple); + HeapTuple tuple; + RelationInfo *resultRelationInfo; + Relation resultRelationDesc; + int numIndices; + + /* ---------------- + * abort the operation if not running transactions + * ---------------- + */ + if (IsBootstrapProcessingMode()) + { + elog(DEBUG, "ExecReplace: replace can't run without transactions"); + return; + } + + /* ---------------- + * get the heap tuple out of the tuple table slot + * ---------------- + */ + tuple = slot->val; + + /* ---------------- + * get the result relation information + * ---------------- + */ + resultRelationInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + + /* ---------------- + * have to add code to preform unique checking here. + * in the event of unique tuples, this becomes a deletion + * of the original tuple affected by the replace. + * cim -12/1/89 + * ---------------- + */ + + /* BEFORE ROW UPDATE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) + { + HeapTuple newtuple; + + newtuple = ExecBRUpdateTriggers(resultRelationDesc, tupleid, tuple); + + if (newtuple == NULL) /* "do nothing" */ + return; + + if (newtuple != tuple) /* modified by Trigger(s) */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * Check the constraints of a tuple + * ---------------- + */ + + if (resultRelationDesc->rd_att->constr) + { + HeapTuple newtuple; + + newtuple = ExecConstraints("ExecReplace", resultRelationDesc, tuple); + + if (newtuple != tuple) /* modified by DEFAULT */ + { + Assert(slot->ttc_shouldFree); + pfree(tuple); + slot->val = tuple = newtuple; + } + } + + /* ---------------- + * replace the heap tuple + * + * Don't want to continue if our heap_replace didn't actually + * do a replace. This would be the case if heap_replace + * detected a non-functional update. -kw 12/30/93 + * ---------------- + */ + if (heap_replace(resultRelationDesc, /* relation desc */ + tupleid, /* item ptr of tuple to replace */ + tuple)) + { /* replacement heap tuple */ + return; + } + + IncrReplaced(); + (estate->es_processed)++; + + /* ---------------- + * Note: instead of having to update the old index tuples + * associated with the heap tuple, all we do is form + * and insert new index tuples.. This is because + * replaces are actually deletes and inserts and + * index tuple deletion is done automagically by + * the vaccuum deamon.. All we do is insert new + * index tuples. -cim 9/27/89 + * ---------------- + */ + + /* ---------------- + * process indices + * + * heap_replace updates a tuple in the base relation by invalidating + * it and then appending a new tuple to the relation. As a side + * effect, the tupleid of the new tuple is placed in the new + * tuple's t_ctid field. So we now insert index tuples using + * the new tupleid stored there. + * ---------------- + */ + + numIndices = resultRelationInfo->ri_NumIndices; + if (numIndices > 0) + { + ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate, true); + } + + /* AFTER ROW UPDATE Triggers */ + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0) + ExecARUpdateTriggers(resultRelationDesc, tupleid, tuple); } -static HeapTuple -ExecAttrDefault (Relation rel, HeapTuple tuple) +static HeapTuple +ExecAttrDefault(Relation rel, HeapTuple tuple) { - int ndef = rel->rd_att->constr->num_defval; - AttrDefault *attrdef = rel->rd_att->constr->defval; - ExprContext *econtext = makeNode (ExprContext); - HeapTuple newtuple; - Node *expr; - bool isnull; - bool isdone; - Datum val; - Datum *replValue = NULL; - char *replNull = NULL; - char *repl = NULL; - int i; - - econtext->ecxt_scantuple = NULL; /* scan tuple slot */ - econtext->ecxt_innertuple = NULL; /* inner tuple slot */ - econtext->ecxt_outertuple = NULL; /* outer tuple slot */ - econtext->ecxt_relation = NULL; /* relation */ - econtext->ecxt_relid = 0; /* relid */ - econtext->ecxt_param_list_info = NULL; /* param list info */ - econtext->ecxt_range_table = NULL; /* range table */ - for (i = 0; i < ndef; i++) - { - if ( !heap_attisnull (tuple, attrdef[i].adnum) ) - continue; - expr = (Node*) stringToNode (attrdef[i].adbin); - - val = ExecEvalExpr (expr, econtext, &isnull, &isdone); - - pfree (expr); - - if ( isnull ) - continue; - - if ( repl == NULL ) - { - repl = (char*) palloc (rel->rd_att->natts * sizeof (char)); - replNull = (char*) palloc (rel->rd_att->natts * sizeof (char)); - replValue = (Datum*) palloc (rel->rd_att->natts * sizeof (Datum)); - memset (repl, ' ', rel->rd_att->natts * sizeof (char)); - } - - repl[attrdef[i].adnum - 1] = 'r'; - replNull[attrdef[i].adnum - 1] = ' '; - replValue[attrdef[i].adnum - 1] = val; - - } - - pfree (econtext); - - if ( repl == NULL ) - return (tuple); - - newtuple = heap_modifytuple (tuple, InvalidBuffer, rel, replValue, replNull, repl); - - pfree (repl); - pfree (replNull); - pfree (replValue); - - return (newtuple); - + int ndef = rel->rd_att->constr->num_defval; + AttrDefault *attrdef = rel->rd_att->constr->defval; + ExprContext *econtext = makeNode(ExprContext); + HeapTuple newtuple; + Node *expr; + bool isnull; + bool isdone; + Datum val; + Datum *replValue = NULL; + char *replNull = NULL; + char *repl = NULL; + int i; + + econtext->ecxt_scantuple = NULL; /* scan tuple slot */ + econtext->ecxt_innertuple = NULL; /* inner tuple slot */ + econtext->ecxt_outertuple = NULL; /* outer tuple slot */ + econtext->ecxt_relation = NULL; /* relation */ + econtext->ecxt_relid = 0; /* relid */ + econtext->ecxt_param_list_info = NULL; /* param list info */ + econtext->ecxt_range_table = NULL; /* range table */ + for (i = 0; i < ndef; i++) + { + if (!heap_attisnull(tuple, attrdef[i].adnum)) + continue; + expr = (Node *) stringToNode(attrdef[i].adbin); + + val = ExecEvalExpr(expr, econtext, &isnull, &isdone); + + pfree(expr); + + if (isnull) + continue; + + if (repl == NULL) + { + repl = (char *) palloc(rel->rd_att->natts * sizeof(char)); + replNull = (char *) palloc(rel->rd_att->natts * sizeof(char)); + replValue = (Datum *) palloc(rel->rd_att->natts * sizeof(Datum)); + memset(repl, ' ', rel->rd_att->natts * sizeof(char)); + } + + repl[attrdef[i].adnum - 1] = 'r'; + replNull[attrdef[i].adnum - 1] = ' '; + replValue[attrdef[i].adnum - 1] = val; + + } + + pfree(econtext); + + if (repl == NULL) + return (tuple); + + newtuple = heap_modifytuple(tuple, InvalidBuffer, rel, replValue, replNull, repl); + + pfree(repl); + pfree(replNull); + pfree(replValue); + + return (newtuple); + } -static char * -ExecRelCheck (Relation rel, HeapTuple tuple) +static char * +ExecRelCheck(Relation rel, HeapTuple tuple) { - int ncheck = rel->rd_att->constr->num_check; - ConstrCheck *check = rel->rd_att->constr->check; - ExprContext *econtext = makeNode (ExprContext); - TupleTableSlot *slot = makeNode (TupleTableSlot); - RangeTblEntry *rte = makeNode (RangeTblEntry); - List *rtlist; - List *qual; - bool res; - int i; - - slot->val = tuple; - slot->ttc_shouldFree = false; - slot->ttc_descIsNew = true; - slot->ttc_tupleDescriptor = rel->rd_att; - slot->ttc_buffer = InvalidBuffer; - slot->ttc_whichplan = -1; - rte->relname = nameout (&(rel->rd_rel->relname)); - rte->timeRange = NULL; - rte->refname = rte->relname; - rte->relid = rel->rd_id; - rte->inh = false; - rte->archive = false; - rte->inFromCl = true; - rte->timeQual = NULL; - rtlist = lcons (rte, NIL); - econtext->ecxt_scantuple = slot; /* scan tuple slot */ - econtext->ecxt_innertuple = NULL; /* inner tuple slot */ - econtext->ecxt_outertuple = NULL; /* outer tuple slot */ - econtext->ecxt_relation = rel; /* relation */ - econtext->ecxt_relid = 0; /* relid */ - econtext->ecxt_param_list_info = NULL; /* param list info */ - econtext->ecxt_range_table = rtlist; /* range table */ - - for (i = 0; i < ncheck; i++) - { - qual = (List*) stringToNode (check[i].ccbin); - - res = ExecQual (qual, econtext); - - pfree (qual); - - if ( !res ) - return (check[i].ccname); - } - - pfree (slot); - pfree (rte->relname); - pfree (rte); - pfree (rtlist); - pfree (econtext); - - return ((char *) NULL); - + int ncheck = rel->rd_att->constr->num_check; + ConstrCheck *check = rel->rd_att->constr->check; + ExprContext *econtext = makeNode(ExprContext); + TupleTableSlot *slot = makeNode(TupleTableSlot); + RangeTblEntry *rte = makeNode(RangeTblEntry); + List *rtlist; + List *qual; + bool res; + int i; + + slot->val = tuple; + slot->ttc_shouldFree = false; + slot->ttc_descIsNew = true; + slot->ttc_tupleDescriptor = rel->rd_att; + slot->ttc_buffer = InvalidBuffer; + slot->ttc_whichplan = -1; + rte->relname = nameout(&(rel->rd_rel->relname)); + rte->timeRange = NULL; + rte->refname = rte->relname; + rte->relid = rel->rd_id; + rte->inh = false; + rte->archive = false; + rte->inFromCl = true; + rte->timeQual = NULL; + rtlist = lcons(rte, NIL); + econtext->ecxt_scantuple = slot; /* scan tuple slot */ + econtext->ecxt_innertuple = NULL; /* inner tuple slot */ + econtext->ecxt_outertuple = NULL; /* outer tuple slot */ + econtext->ecxt_relation = rel; /* relation */ + econtext->ecxt_relid = 0; /* relid */ + econtext->ecxt_param_list_info = NULL; /* param list info */ + econtext->ecxt_range_table = rtlist; /* range table */ + + for (i = 0; i < ncheck; i++) + { + qual = (List *) stringToNode(check[i].ccbin); + + res = ExecQual(qual, econtext); + + pfree(qual); + + if (!res) + return (check[i].ccname); + } + + pfree(slot); + pfree(rte->relname); + pfree(rte); + pfree(rtlist); + pfree(econtext); + + return ((char *) NULL); + } HeapTuple -ExecConstraints (char *caller, Relation rel, HeapTuple tuple) +ExecConstraints(char *caller, Relation rel, HeapTuple tuple) { - HeapTuple newtuple = tuple; - - Assert ( rel->rd_att->constr ); - - if ( rel->rd_att->constr->num_defval > 0 ) - newtuple = tuple = ExecAttrDefault (rel, tuple); - - if ( rel->rd_att->constr->has_not_null ) - { - int attrChk; - - for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++) + HeapTuple newtuple = tuple; + + Assert(rel->rd_att->constr); + + if (rel->rd_att->constr->num_defval > 0) + newtuple = tuple = ExecAttrDefault(rel, tuple); + + if (rel->rd_att->constr->has_not_null) { - if (rel->rd_att->attrs[attrChk-1]->attnotnull && heap_attisnull (tuple,attrChk)) - elog(WARN,"%s: Fail to add null value in not null attribute %s", - caller, rel->rd_att->attrs[attrChk-1]->attname.data); - } - } - - if ( rel->rd_att->constr->num_check > 0 ) - { - char *failed; - - if ( ( failed = ExecRelCheck (rel, tuple) ) != NULL ) - elog(WARN,"%s: rejected due to CHECK constraint %s", caller, failed); - } - - return (newtuple); + int attrChk; + + for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++) + { + if (rel->rd_att->attrs[attrChk - 1]->attnotnull && heap_attisnull(tuple, attrChk)) + elog(WARN, "%s: Fail to add null value in not null attribute %s", + caller, rel->rd_att->attrs[attrChk - 1]->attname.data); + } + } + + if (rel->rd_att->constr->num_check > 0) + { + char *failed; + + if ((failed = ExecRelCheck(rel, tuple)) != NULL) + elog(WARN, "%s: rejected due to CHECK constraint %s", caller, failed); + } + + return (newtuple); } diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c index f9e18d948f0..89caefd162e 100644 --- a/src/backend/executor/execProcnode.c +++ b/src/backend/executor/execProcnode.c @@ -1,75 +1,75 @@ /*------------------------------------------------------------------------- * * execProcnode.c-- - * contains dispatch functions which call the appropriate "initialize", - * "get a tuple", and "cleanup" routines for the given node type. - * If the node has children, then it will presumably call ExecInitNode, - * ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate - * processing.. + * contains dispatch functions which call the appropriate "initialize", + * "get a tuple", and "cleanup" routines for the given node type. + * If the node has children, then it will presumably call ExecInitNode, + * ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate + * processing.. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.2 1996/10/31 10:11:27 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.3 1997/09/07 04:41:19 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES - * ExecInitNode - initialize a plan node and it's subplans - * ExecProcNode - get a tuple by executing the plan node - * ExecEndNode - shut down a plan node and it's subplans + * INTERFACE ROUTINES + * ExecInitNode - initialize a plan node and it's subplans + * ExecProcNode - get a tuple by executing the plan node + * ExecEndNode - shut down a plan node and it's subplans * - * NOTES - * This used to be three files. It is now all combined into - * one file so that it is easier to keep ExecInitNode, ExecProcNode, - * and ExecEndNode in sync when new nodes are added. - * - * EXAMPLE - * suppose we want the age of the manager of the shoe department and - * the number of employees in that department. so we have the query: + * NOTES + * This used to be three files. It is now all combined into + * one file so that it is easier to keep ExecInitNode, ExecProcNode, + * and ExecEndNode in sync when new nodes are added. * - * retrieve (DEPT.no_emps, EMP.age) - * where EMP.name = DEPT.mgr and - * DEPT.name = "shoe" - * - * Suppose the planner gives us the following plan: - * - * Nest Loop (DEPT.mgr = EMP.name) - * / \ - * / \ - * Seq Scan Seq Scan - * DEPT EMP - * (name = "shoe") - * - * ExecStart() is called first. - * It calls InitPlan() which calls ExecInitNode() on - * the root of the plan -- the nest loop node. + * EXAMPLE + * suppose we want the age of the manager of the shoe department and + * the number of employees in that department. so we have the query: * - * * ExecInitNode() notices that it is looking at a nest loop and - * as the code below demonstrates, it calls ExecInitNestLoop(). - * Eventually this calls ExecInitNode() on the right and left subplans - * and so forth until the entire plan is initialized. - * - * * Then when ExecRun() is called, it calls ExecutePlan() which - * calls ExecProcNode() repeatedly on the top node of the plan. - * Each time this happens, ExecProcNode() will end up calling - * ExecNestLoop(), which calls ExecProcNode() on its subplans. - * Each of these subplans is a sequential scan so ExecSeqScan() is - * called. The slots returned by ExecSeqScan() may contain - * tuples which contain the attributes ExecNestLoop() uses to - * form the tuples it returns. + * retrieve (DEPT.no_emps, EMP.age) + * where EMP.name = DEPT.mgr and + * DEPT.name = "shoe" * - * * Eventually ExecSeqScan() stops returning tuples and the nest - * loop join ends. Lastly, ExecEnd() calls ExecEndNode() which - * calls ExecEndNestLoop() which in turn calls ExecEndNode() on - * its subplans which result in ExecEndSeqScan(). + * Suppose the planner gives us the following plan: * - * This should show how the executor works by having - * ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch - * their work to the appopriate node support routines which may - * in turn call these routines themselves on their subplans. + * Nest Loop (DEPT.mgr = EMP.name) + * / \ + * / \ + * Seq Scan Seq Scan + * DEPT EMP + * (name = "shoe") + * + * ExecStart() is called first. + * It calls InitPlan() which calls ExecInitNode() on + * the root of the plan -- the nest loop node. + * + * * ExecInitNode() notices that it is looking at a nest loop and + * as the code below demonstrates, it calls ExecInitNestLoop(). + * Eventually this calls ExecInitNode() on the right and left subplans + * and so forth until the entire plan is initialized. + * + * * Then when ExecRun() is called, it calls ExecutePlan() which + * calls ExecProcNode() repeatedly on the top node of the plan. + * Each time this happens, ExecProcNode() will end up calling + * ExecNestLoop(), which calls ExecProcNode() on its subplans. + * Each of these subplans is a sequential scan so ExecSeqScan() is + * called. The slots returned by ExecSeqScan() may contain + * tuples which contain the attributes ExecNestLoop() uses to + * form the tuples it returns. + * + * * Eventually ExecSeqScan() stops returning tuples and the nest + * loop join ends. Lastly, ExecEnd() calls ExecEndNode() which + * calls ExecEndNestLoop() which in turn calls ExecEndNode() on + * its subplans which result in ExecEndSeqScan(). + * + * This should show how the executor works by having + * ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch + * their work to the appopriate node support routines which may + * in turn call these routines themselves on their subplans. * */ #include "postgres.h" @@ -91,389 +91,393 @@ #include "executor/nodeTee.h" /* ------------------------------------------------------------------------ - * ExecInitNode - * - * Recursively initializes all the nodes in the plan rooted - * at 'node'. - * - * Initial States: - * 'node' is the plan produced by the query planner - * - * returns TRUE/FALSE on whether the plan was successfully initialized + * ExecInitNode + * + * Recursively initializes all the nodes in the plan rooted + * at 'node'. + * + * Initial States: + * 'node' is the plan produced by the query planner + * + * returns TRUE/FALSE on whether the plan was successfully initialized * ------------------------------------------------------------------------ */ bool -ExecInitNode(Plan *node, EState *estate, Plan *parent) +ExecInitNode(Plan * node, EState * estate, Plan * parent) { - bool result; - - /* ---------------- - * do nothing when we get to the end - * of a leaf on tree. - * ---------------- - */ - if (node == NULL) - return FALSE; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - result = ExecInitResult((Result *)node, estate, parent); - break; - - case T_Append: - result = ExecInitAppend((Append *)node, estate, parent); - break; - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - result = ExecInitSeqScan((SeqScan *)node, estate, parent); - break; - - case T_IndexScan: - result = ExecInitIndexScan((IndexScan *)node, estate, parent); - break; - - /* ---------------- - * join nodes - * ---------------- - */ - case T_NestLoop: - result = ExecInitNestLoop((NestLoop *)node, estate, parent); - break; - - case T_MergeJoin: - result = ExecInitMergeJoin((MergeJoin *)node, estate, parent); - break; - + bool result; + /* ---------------- - * materialization nodes + * do nothing when we get to the end + * of a leaf on tree. * ---------------- */ - case T_Material: - result = ExecInitMaterial((Material *)node, estate, parent); - break; - - case T_Sort: - result = ExecInitSort((Sort *)node, estate, parent); - break; - - case T_Unique: - result = ExecInitUnique((Unique *)node, estate, parent); - break; - - case T_Group: - result = ExecInitGroup((Group *)node, estate, parent); - break; - - case T_Agg: - result = ExecInitAgg((Agg *)node, estate, parent); - break; - - case T_Hash: - result = ExecInitHash((Hash *)node, estate, parent); - break; - - case T_HashJoin: - result = ExecInitHashJoin((HashJoin *)node, estate, parent); - break; - - case T_Tee: - result = ExecInitTee((Tee*)node, estate, parent); - break; - - default: - elog(DEBUG, "ExecInitNode: node not yet supported: %d", - nodeTag(node)); - result = FALSE; - } - - return result; + if (node == NULL) + return FALSE; + + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + result = ExecInitResult((Result *) node, estate, parent); + break; + + case T_Append: + result = ExecInitAppend((Append *) node, estate, parent); + break; + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + result = ExecInitSeqScan((SeqScan *) node, estate, parent); + break; + + case T_IndexScan: + result = ExecInitIndexScan((IndexScan *) node, estate, parent); + break; + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + result = ExecInitNestLoop((NestLoop *) node, estate, parent); + break; + + case T_MergeJoin: + result = ExecInitMergeJoin((MergeJoin *) node, estate, parent); + break; + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + result = ExecInitMaterial((Material *) node, estate, parent); + break; + + case T_Sort: + result = ExecInitSort((Sort *) node, estate, parent); + break; + + case T_Unique: + result = ExecInitUnique((Unique *) node, estate, parent); + break; + + case T_Group: + result = ExecInitGroup((Group *) node, estate, parent); + break; + + case T_Agg: + result = ExecInitAgg((Agg *) node, estate, parent); + break; + + case T_Hash: + result = ExecInitHash((Hash *) node, estate, parent); + break; + + case T_HashJoin: + result = ExecInitHashJoin((HashJoin *) node, estate, parent); + break; + + case T_Tee: + result = ExecInitTee((Tee *) node, estate, parent); + break; + + default: + elog(DEBUG, "ExecInitNode: node not yet supported: %d", + nodeTag(node)); + result = FALSE; + } + + return result; } /* ---------------------------------------------------------------- - * ExecProcNode - * - * Initial States: - * the query tree must be initialized once by calling ExecInit. + * ExecProcNode + * + * Initial States: + * the query tree must be initialized once by calling ExecInit. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecProcNode(Plan *node, Plan *parent) +ExecProcNode(Plan * node, Plan * parent) { - TupleTableSlot *result; - - /* ---------------- - * deal with NULL nodes.. - * ---------------- - */ - if (node == NULL) - return NULL; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - result = ExecResult((Result *)node); - break; - - case T_Append: - result = ExecProcAppend((Append *)node); - break; - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - result = ExecSeqScan((SeqScan *)node); - break; - - case T_IndexScan: - result = ExecIndexScan((IndexScan *)node); - break; - - /* ---------------- - * join nodes - * ---------------- - */ - case T_NestLoop: - result = ExecNestLoop((NestLoop *)node, parent); - break; - - case T_MergeJoin: - result = ExecMergeJoin((MergeJoin *)node); - break; - + TupleTableSlot *result; + /* ---------------- - * materialization nodes + * deal with NULL nodes.. * ---------------- */ - case T_Material: - result = ExecMaterial((Material *)node); - break; - - case T_Sort: - result = ExecSort((Sort *)node); - break; - - case T_Unique: - result = ExecUnique((Unique *)node); - break; - - case T_Group: - result = ExecGroup((Group *)node); - break; - - case T_Agg: - result = ExecAgg((Agg *)node); - break; - - case T_Hash: - result = ExecHash((Hash *)node); - break; - - case T_HashJoin: - result = ExecHashJoin((HashJoin *)node); - break; - - case T_Tee: - result = ExecTee((Tee*)node, parent); - break; - - default: - elog(DEBUG, "ExecProcNode: node not yet supported: %d", - nodeTag(node)); - result = FALSE; - } - - return result; + if (node == NULL) + return NULL; + + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + result = ExecResult((Result *) node); + break; + + case T_Append: + result = ExecProcAppend((Append *) node); + break; + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + result = ExecSeqScan((SeqScan *) node); + break; + + case T_IndexScan: + result = ExecIndexScan((IndexScan *) node); + break; + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + result = ExecNestLoop((NestLoop *) node, parent); + break; + + case T_MergeJoin: + result = ExecMergeJoin((MergeJoin *) node); + break; + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + result = ExecMaterial((Material *) node); + break; + + case T_Sort: + result = ExecSort((Sort *) node); + break; + + case T_Unique: + result = ExecUnique((Unique *) node); + break; + + case T_Group: + result = ExecGroup((Group *) node); + break; + + case T_Agg: + result = ExecAgg((Agg *) node); + break; + + case T_Hash: + result = ExecHash((Hash *) node); + break; + + case T_HashJoin: + result = ExecHashJoin((HashJoin *) node); + break; + + case T_Tee: + result = ExecTee((Tee *) node, parent); + break; + + default: + elog(DEBUG, "ExecProcNode: node not yet supported: %d", + nodeTag(node)); + result = FALSE; + } + + return result; } int -ExecCountSlotsNode(Plan *node) +ExecCountSlotsNode(Plan * node) { - if (node == (Plan *)NULL) - return 0; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - return ExecCountSlotsResult((Result *)node); - - case T_Append: - return ExecCountSlotsAppend((Append *)node); - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - return ExecCountSlotsSeqScan((SeqScan *)node); + if (node == (Plan *) NULL) + return 0; - case T_IndexScan: - return ExecCountSlotsIndexScan((IndexScan *)node); - - /* ---------------- - * join nodes - * ---------------- - */ - case T_NestLoop: - return ExecCountSlotsNestLoop((NestLoop *)node); - - case T_MergeJoin: - return ExecCountSlotsMergeJoin((MergeJoin *)node); - - /* ---------------- - * materialization nodes - * ---------------- - */ - case T_Material: - return ExecCountSlotsMaterial((Material *)node); - - case T_Sort: - return ExecCountSlotsSort((Sort *)node); - - case T_Unique: - return ExecCountSlotsUnique((Unique *)node); - - case T_Group: - return ExecCountSlotsGroup((Group *)node); - - case T_Agg: - return ExecCountSlotsAgg((Agg *)node); - - case T_Hash: - return ExecCountSlotsHash((Hash *)node); - - case T_HashJoin: - return ExecCountSlotsHashJoin((HashJoin *)node); - - case T_Tee: - return ExecCountSlotsTee((Tee*)node); - - default: - elog(WARN, "ExecCountSlotsNode: node not yet supported: %d", - nodeTag(node)); - break; - } - return 0; + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + return ExecCountSlotsResult((Result *) node); + + case T_Append: + return ExecCountSlotsAppend((Append *) node); + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + return ExecCountSlotsSeqScan((SeqScan *) node); + + case T_IndexScan: + return ExecCountSlotsIndexScan((IndexScan *) node); + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + return ExecCountSlotsNestLoop((NestLoop *) node); + + case T_MergeJoin: + return ExecCountSlotsMergeJoin((MergeJoin *) node); + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + return ExecCountSlotsMaterial((Material *) node); + + case T_Sort: + return ExecCountSlotsSort((Sort *) node); + + case T_Unique: + return ExecCountSlotsUnique((Unique *) node); + + case T_Group: + return ExecCountSlotsGroup((Group *) node); + + case T_Agg: + return ExecCountSlotsAgg((Agg *) node); + + case T_Hash: + return ExecCountSlotsHash((Hash *) node); + + case T_HashJoin: + return ExecCountSlotsHashJoin((HashJoin *) node); + + case T_Tee: + return ExecCountSlotsTee((Tee *) node); + + default: + elog(WARN, "ExecCountSlotsNode: node not yet supported: %d", + nodeTag(node)); + break; + } + return 0; } -/* ---------------------------------------------------------------- - * ExecEndNode - * - * Recursively cleans up all the nodes in the plan rooted - * at 'node'. +/* ---------------------------------------------------------------- + * ExecEndNode * - * After this operation, the query plan will not be able to - * processed any further. This should be called only after - * the query plan has been fully executed. - * ---------------------------------------------------------------- + * Recursively cleans up all the nodes in the plan rooted + * at 'node'. + * + * After this operation, the query plan will not be able to + * processed any further. This should be called only after + * the query plan has been fully executed. + * ---------------------------------------------------------------- */ void -ExecEndNode(Plan *node, Plan *parent) +ExecEndNode(Plan * node, Plan * parent) { - /* ---------------- - * do nothing when we get to the end - * of a leaf on tree. - * ---------------- - */ - if (node == NULL) - return; - - switch(nodeTag(node)) { - /* ---------------- - * control nodes - * ---------------- - */ - case T_Result: - ExecEndResult((Result *)node); - break; - - case T_Append: - ExecEndAppend((Append *)node); - break; - - /* ---------------- - * scan nodes - * ---------------- - */ - case T_SeqScan: - ExecEndSeqScan((SeqScan *)node); - break; - - case T_IndexScan: - ExecEndIndexScan((IndexScan *)node); - break; - /* ---------------- - * join nodes + * do nothing when we get to the end + * of a leaf on tree. * ---------------- */ - case T_NestLoop: - ExecEndNestLoop((NestLoop *)node); - break; - - case T_MergeJoin: - ExecEndMergeJoin((MergeJoin *)node); - break; - - /* ---------------- - * materialization nodes - * ---------------- - */ - case T_Material: - ExecEndMaterial((Material *)node); - break; - - case T_Sort: - ExecEndSort((Sort *)node); - break; - - case T_Unique: - ExecEndUnique((Unique *)node); - break; - - case T_Group: - ExecEndGroup((Group *)node); - break; - - case T_Agg: - ExecEndAgg((Agg *)node); - break; - - /* ---------------- - * XXX add hooks to these - * ---------------- - */ - case T_Hash: - ExecEndHash((Hash *) node); - break; - - case T_HashJoin: - ExecEndHashJoin((HashJoin *) node); - break; - - case T_Tee: - ExecEndTee((Tee*) node, parent); - break; - - default: - elog(DEBUG, "ExecEndNode: node not yet supported", - nodeTag(node)); - break; - } + if (node == NULL) + return; + + switch (nodeTag(node)) + { + /* ---------------- + * control nodes + * ---------------- + */ + case T_Result: + ExecEndResult((Result *) node); + break; + + case T_Append: + ExecEndAppend((Append *) node); + break; + + /* ---------------- + * scan nodes + * ---------------- + */ + case T_SeqScan: + ExecEndSeqScan((SeqScan *) node); + break; + + case T_IndexScan: + ExecEndIndexScan((IndexScan *) node); + break; + + /* ---------------- + * join nodes + * ---------------- + */ + case T_NestLoop: + ExecEndNestLoop((NestLoop *) node); + break; + + case T_MergeJoin: + ExecEndMergeJoin((MergeJoin *) node); + break; + + /* ---------------- + * materialization nodes + * ---------------- + */ + case T_Material: + ExecEndMaterial((Material *) node); + break; + + case T_Sort: + ExecEndSort((Sort *) node); + break; + + case T_Unique: + ExecEndUnique((Unique *) node); + break; + + case T_Group: + ExecEndGroup((Group *) node); + break; + + case T_Agg: + ExecEndAgg((Agg *) node); + break; + + /* ---------------- + * XXX add hooks to these + * ---------------- + */ + case T_Hash: + ExecEndHash((Hash *) node); + break; + + case T_HashJoin: + ExecEndHashJoin((HashJoin *) node); + break; + + case T_Tee: + ExecEndTee((Tee *) node, parent); + break; + + default: + elog(DEBUG, "ExecEndNode: node not yet supported", + nodeTag(node)); + break; + } } diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 536b0068342..7b8cb18ef25 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -1,32 +1,32 @@ /*------------------------------------------------------------------------- * * execQual.c-- - * Routines to evaluate qualification and targetlist expressions + * Routines to evaluate qualification and targetlist expressions * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.12 1997/08/19 21:31:03 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.13 1997/09/07 04:41:20 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES - * ExecEvalExpr - evaluate an expression and return a datum - * ExecQual - return true/false if qualification is satisified - * ExecTargetList - form a new tuple by projecting the given tuple + * INTERFACE ROUTINES + * ExecEvalExpr - evaluate an expression and return a datum + * ExecQual - return true/false if qualification is satisified + * ExecTargetList - form a new tuple by projecting the given tuple * - * NOTES - * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster - * will speed up the entire system. Unfortunately they are currently - * implemented recursively.. Eliminating the recursion is bound to - * improve the speed of the executor. + * NOTES + * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster + * will speed up the entire system. Unfortunately they are currently + * implemented recursively.. Eliminating the recursion is bound to + * improve the speed of the executor. * - * ExecTargetList() is used to make tuple projections. Rather then - * trying to speed it up, the execution plan should be pre-processed - * to facilitate attribute sharing between nodes wherever possible, - * instead of doing needless copying. -cim 5/31/91 + * ExecTargetList() is used to make tuple projections. Rather then + * trying to speed it up, the execution plan should be pre-processed + * to facilitate attribute sharing between nodes wherever possible, + * instead of doing needless copying. -cim 5/31/91 * */ #include <string.h> @@ -56,7 +56,7 @@ #include "utils/mcxt.h" /* ---------------- - * externs and constants + * externs and constants * ---------------- */ @@ -65,1492 +65,1577 @@ * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst * and by ExecEvalArrayRef. */ -bool execConstByVal; -int execConstLen; +bool execConstByVal; +int execConstLen; /* static functions decls */ -static Datum ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull); -static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext, - bool *isNull, bool *isDone); -static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext, - bool *isNull, bool *isDone); -static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext, - List *argList, Datum argV[], bool *argIsDone); -static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull); -static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext, - bool *isNull); -static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull); -static Datum ExecMakeFunctionResult(Node *node, List *arguments, - ExprContext *econtext, bool *isNull, bool *isDone); -static bool ExecQualClause(Node *clause, ExprContext *econtext); +static Datum ExecEvalAggreg(Aggreg * agg, ExprContext * econtext, bool * isNull); +static Datum +ExecEvalArrayRef(ArrayRef * arrayRef, ExprContext * econtext, + bool * isNull, bool * isDone); +static Datum ExecEvalAnd(Expr * andExpr, ExprContext * econtext, bool * isNull); +static Datum +ExecEvalFunc(Expr * funcClause, ExprContext * econtext, + bool * isNull, bool * isDone); +static void +ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext * econtext, + List * argList, Datum argV[], bool * argIsDone); +static Datum ExecEvalNot(Expr * notclause, ExprContext * econtext, bool * isNull); +static Datum +ExecEvalOper(Expr * opClause, ExprContext * econtext, + bool * isNull); +static Datum ExecEvalOr(Expr * orExpr, ExprContext * econtext, bool * isNull); +static Datum ExecEvalVar(Var * variable, ExprContext * econtext, bool * isNull); +static Datum +ExecMakeFunctionResult(Node * node, List * arguments, + ExprContext * econtext, bool * isNull, bool * isDone); +static bool ExecQualClause(Node * clause, ExprContext * econtext); /* -------------------------------- - * ExecEvalArrayRef + * ExecEvalArrayRef * - * This function takes an ArrayRef and returns a Const Node if it - * is an array reference or returns the changed Array Node if it is - * an array assignment. + * This function takes an ArrayRef and returns a Const Node if it + * is an array reference or returns the changed Array Node if it is + * an array assignment. * * -------------------------------- */ -static Datum -ExecEvalArrayRef(ArrayRef *arrayRef, - ExprContext *econtext, - bool *isNull, - bool *isDone) +static Datum +ExecEvalArrayRef(ArrayRef * arrayRef, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - bool dummy; - int i = 0, j = 0; - ArrayType *array_scanner; - List *upperIndexpr, *lowerIndexpr; - Node *assgnexpr; - List *elt; - IntArray upper, lower; - int *lIndex; - char *dataPtr; - - *isNull = false; - array_scanner = (ArrayType*)ExecEvalExpr(arrayRef->refexpr, - econtext, - isNull, - isDone); - if (*isNull) return (Datum)NULL; - - upperIndexpr = arrayRef->refupperindexpr; - - foreach (elt, upperIndexpr) { - upper.indx[i++] = (int32)ExecEvalExpr((Node*)lfirst(elt), - econtext, - isNull, - &dummy); - if (*isNull) return (Datum)NULL; - } - - lowerIndexpr = arrayRef->reflowerindexpr; - lIndex = NULL; - if (lowerIndexpr != NIL) { - foreach (elt, lowerIndexpr) { - lower.indx[j++] = (int32)ExecEvalExpr((Node*)lfirst(elt), - econtext, - isNull, - &dummy); - if (*isNull) return (Datum)NULL; + bool dummy; + int i = 0, + j = 0; + ArrayType *array_scanner; + List *upperIndexpr, + *lowerIndexpr; + Node *assgnexpr; + List *elt; + IntArray upper, + lower; + int *lIndex; + char *dataPtr; + + *isNull = false; + array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr, + econtext, + isNull, + isDone); + if (*isNull) + return (Datum) NULL; + + upperIndexpr = arrayRef->refupperindexpr; + + foreach(elt, upperIndexpr) + { + upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt), + econtext, + isNull, + &dummy); + if (*isNull) + return (Datum) NULL; + } + + lowerIndexpr = arrayRef->reflowerindexpr; + lIndex = NULL; + if (lowerIndexpr != NIL) + { + foreach(elt, lowerIndexpr) + { + lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt), + econtext, + isNull, + &dummy); + if (*isNull) + return (Datum) NULL; + } + if (i != j) + elog(WARN, + "ExecEvalArrayRef: upper and lower indices mismatch"); + lIndex = lower.indx; } - if (i != j) - elog(WARN, - "ExecEvalArrayRef: upper and lower indices mismatch"); - lIndex = lower.indx; - } - - assgnexpr = arrayRef->refassgnexpr; - if (assgnexpr != NULL) { - dataPtr = (char*)ExecEvalExpr((Node *) - assgnexpr, econtext, - isNull, &dummy); - if (*isNull) return (Datum)NULL; - execConstByVal = arrayRef->refelembyval; - execConstLen = arrayRef->refelemlength; + + assgnexpr = arrayRef->refassgnexpr; + if (assgnexpr != NULL) + { + dataPtr = (char *) ExecEvalExpr((Node *) + assgnexpr, econtext, + isNull, &dummy); + if (*isNull) + return (Datum) NULL; + execConstByVal = arrayRef->refelembyval; + execConstLen = arrayRef->refelemlength; + if (lIndex == NULL) + return (Datum) array_set(array_scanner, i, upper.indx, dataPtr, + arrayRef->refelembyval, + arrayRef->refelemlength, + arrayRef->refattrlength, isNull); + return (Datum) array_assgn(array_scanner, i, upper.indx, + lower.indx, + (ArrayType *) dataPtr, + arrayRef->refelembyval, + arrayRef->refelemlength, isNull); + } + execConstByVal = arrayRef->refelembyval; + execConstLen = arrayRef->refelemlength; if (lIndex == NULL) - return (Datum) array_set(array_scanner, i, upper.indx, dataPtr, - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, isNull); - return (Datum) array_assgn(array_scanner, i, upper.indx, - lower.indx, - (ArrayType*)dataPtr, - arrayRef->refelembyval, - arrayRef->refelemlength, isNull); - } - execConstByVal = arrayRef->refelembyval; - execConstLen = arrayRef->refelemlength; - if (lIndex == NULL) - return (Datum) array_ref(array_scanner, i, upper.indx, - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, isNull); - return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx, - arrayRef->refelembyval, - arrayRef->refelemlength, isNull); + return (Datum) array_ref(array_scanner, i, upper.indx, + arrayRef->refelembyval, + arrayRef->refelemlength, + arrayRef->refattrlength, isNull); + return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx, + arrayRef->refelembyval, + arrayRef->refelemlength, isNull); } /* ---------------------------------------------------------------- - * ExecEvalAggreg - * - * Returns a Datum whose value is the value of the precomputed - * aggregate found in the given expression context. + * ExecEvalAggreg + * + * Returns a Datum whose value is the value of the precomputed + * aggregate found in the given expression context. * ---------------------------------------------------------------- */ -static Datum -ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalAggreg(Aggreg * agg, ExprContext * econtext, bool * isNull) { - - *isNull = econtext->ecxt_nulls[agg->aggno]; - return econtext->ecxt_values[agg->aggno]; + + *isNull = econtext->ecxt_nulls[agg->aggno]; + return econtext->ecxt_values[agg->aggno]; } /* ---------------------------------------------------------------- - * ExecEvalVar - * - * Returns a Datum whose value is the value of a range - * variable with respect to given expression context. + * ExecEvalVar + * + * Returns a Datum whose value is the value of a range + * variable with respect to given expression context. + * + * + * As an entry condition, we expect that the the datatype the + * plan expects to get (as told by our "variable" argument) is in + * fact the datatype of the attribute the plan says to fetch (as + * seen in the current context, identified by our "econtext" + * argument). + * + * If we fetch a Type A attribute and Caller treats it as if it + * were Type B, there will be undefined results (e.g. crash). + * One way these might mismatch now is that we're accessing a + * catalog class and the type information in the pg_attribute + * class does not match the hardcoded pg_attribute information + * (in pg_attribute.h) for the class in question. * + * We have an Assert to make sure this entry condition is met. * - * As an entry condition, we expect that the the datatype the - * plan expects to get (as told by our "variable" argument) is in - * fact the datatype of the attribute the plan says to fetch (as - * seen in the current context, identified by our "econtext" - * argument). - * - * If we fetch a Type A attribute and Caller treats it as if it - * were Type B, there will be undefined results (e.g. crash). - * One way these might mismatch now is that we're accessing a - * catalog class and the type information in the pg_attribute - * class does not match the hardcoded pg_attribute information - * (in pg_attribute.h) for the class in question. - * - * We have an Assert to make sure this entry condition is met. - * * ---------------------------------------------------------------- */ -static Datum -ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalVar(Var * variable, ExprContext * econtext, bool * isNull) { - Datum result; - TupleTableSlot *slot; - AttrNumber attnum; - HeapTuple heapTuple; - TupleDesc tuple_type; - Buffer buffer; - bool byval; - int16 len; - - /* ---------------- - * get the slot we want - * ---------------- - */ - switch(variable->varno) { - case INNER: /* get the tuple from the inner node */ - slot = econtext->ecxt_innertuple; - break; - - case OUTER: /* get the tuple from the outer node */ - slot = econtext->ecxt_outertuple; - break; - - default: /* get the tuple from the relation being scanned */ - slot = econtext->ecxt_scantuple; - break; - } - - /* ---------------- - * extract tuple information from the slot - * ---------------- - */ - heapTuple = slot->val; - tuple_type = slot->ttc_tupleDescriptor; - buffer = slot->ttc_buffer; - - attnum = variable->varattno; - - /* (See prolog for explanation of this Assert) */ - Assert(attnum <= 0 || - (attnum - 1 <= tuple_type->natts - 1 && - tuple_type->attrs[attnum-1] != NULL && - variable->vartype == tuple_type->attrs[attnum-1]->atttypid)) - - /* - * If the attribute number is invalid, then we are supposed to - * return the entire tuple, we give back a whole slot so that - * callers know what the tuple looks like. - */ - if (attnum == InvalidAttrNumber) + Datum result; + TupleTableSlot *slot; + AttrNumber attnum; + HeapTuple heapTuple; + TupleDesc tuple_type; + Buffer buffer; + bool byval; + int16 len; + + /* ---------------- + * get the slot we want + * ---------------- + */ + switch (variable->varno) + { + case INNER: /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; + + case OUTER: /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; + + default: /* get the tuple from the relation being + * scanned */ + slot = econtext->ecxt_scantuple; + break; + } + + /* ---------------- + * extract tuple information from the slot + * ---------------- + */ + heapTuple = slot->val; + tuple_type = slot->ttc_tupleDescriptor; + buffer = slot->ttc_buffer; + + attnum = variable->varattno; + + /* (See prolog for explanation of this Assert) */ + Assert(attnum <= 0 || + (attnum - 1 <= tuple_type->natts - 1 && + tuple_type->attrs[attnum - 1] != NULL && + variable->vartype == tuple_type->attrs[attnum - 1]->atttypid)) + + /* + * If the attribute number is invalid, then we are supposed to return + * the entire tuple, we give back a whole slot so that callers know + * what the tuple looks like. + */ + if (attnum == InvalidAttrNumber) { - TupleTableSlot *tempSlot; - TupleDesc td; - HeapTuple tup; - - tempSlot = makeNode(TupleTableSlot); - tempSlot->ttc_shouldFree = false; - tempSlot->ttc_descIsNew = true; - tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL, - tempSlot->ttc_buffer = InvalidBuffer; - tempSlot->ttc_whichplan = -1; - - tup = heap_copytuple(slot->val); - td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); - - ExecSetSlotDescriptor(tempSlot, td); - - ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); - return (Datum) tempSlot; + TupleTableSlot *tempSlot; + TupleDesc td; + HeapTuple tup; + + tempSlot = makeNode(TupleTableSlot); + tempSlot->ttc_shouldFree = false; + tempSlot->ttc_descIsNew = true; + tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL, + tempSlot->ttc_buffer = InvalidBuffer; + tempSlot->ttc_whichplan = -1; + + tup = heap_copytuple(slot->val); + td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); + + ExecSetSlotDescriptor(tempSlot, td); + + ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); + return (Datum) tempSlot; } - - result = (Datum) - heap_getattr(heapTuple, /* tuple containing attribute */ - buffer, /* buffer associated with tuple */ - attnum, /* attribute number of desired attribute */ - tuple_type, /* tuple descriptor of tuple */ - isNull); /* return: is attribute null? */ - - /* ---------------- - * return null if att is null - * ---------------- - */ - if (*isNull) - return (Datum) NULL; - - /* ---------------- - * get length and type information.. - * ??? what should we do about variable length attributes - * - variable length attributes have their length stored - * in the first 4 bytes of the memory pointed to by the - * returned value.. If we can determine that the type - * is a variable length type, we can do the right thing. - * -cim 9/15/89 - * ---------------- - */ - if (attnum < 0) { + + result = (Datum) + heap_getattr(heapTuple, /* tuple containing attribute */ + buffer, /* buffer associated with tuple */ + attnum, /* attribute number of desired attribute */ + tuple_type,/* tuple descriptor of tuple */ + isNull); /* return: is attribute null? */ + /* ---------------- - * If this is a pseudo-att, we get the type and fake the length. - * There ought to be a routine to return the real lengths, so - * we'll mark this one ... XXX -mao + * return null if att is null * ---------------- */ - len = heap_sysattrlen(attnum); /* XXX see -mao above */ - byval = heap_sysattrbyval(attnum); /* XXX see -mao above */ - } else { - len = tuple_type->attrs[ attnum-1 ]->attlen; - byval = tuple_type->attrs[ attnum-1 ]->attbyval ? true : false ; - } - - execConstByVal = byval; - execConstLen = len; - - return result; + if (*isNull) + return (Datum) NULL; + + /* ---------------- + * get length and type information.. + * ??? what should we do about variable length attributes + * - variable length attributes have their length stored + * in the first 4 bytes of the memory pointed to by the + * returned value.. If we can determine that the type + * is a variable length type, we can do the right thing. + * -cim 9/15/89 + * ---------------- + */ + if (attnum < 0) + { + /* ---------------- + * If this is a pseudo-att, we get the type and fake the length. + * There ought to be a routine to return the real lengths, so + * we'll mark this one ... XXX -mao + * ---------------- + */ + len = heap_sysattrlen(attnum); /* XXX see -mao above */ + byval = heap_sysattrbyval(attnum); /* XXX see -mao above */ + } + else + { + len = tuple_type->attrs[attnum - 1]->attlen; + byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false; + } + + execConstByVal = byval; + execConstLen = len; + + return result; } /* ---------------------------------------------------------------- - * ExecEvalParam + * ExecEvalParam * - * Returns the value of a parameter. A param node contains - * something like ($.name) and the expression context contains - * the current parameter bindings (name = "sam") (age = 34)... - * so our job is to replace the param node with the datum - * containing the appropriate information ("sam"). + * Returns the value of a parameter. A param node contains + * something like ($.name) and the expression context contains + * the current parameter bindings (name = "sam") (age = 34)... + * so our job is to replace the param node with the datum + * containing the appropriate information ("sam"). * - * Q: if we have a parameter ($.foo) without a binding, i.e. - * there is no (foo = xxx) in the parameter list info, - * is this a fatal error or should this be a "not available" - * (in which case we shoud return a Const node with the - * isnull flag) ? -cim 10/13/89 + * Q: if we have a parameter ($.foo) without a binding, i.e. + * there is no (foo = xxx) in the parameter list info, + * is this a fatal error or should this be a "not available" + * (in which case we shoud return a Const node with the + * isnull flag) ? -cim 10/13/89 * - * Minor modification: Param nodes now have an extra field, - * `paramkind' which specifies the type of parameter - * (see params.h). So while searching the paramList for - * a paramname/value pair, we have also to check for `kind'. - * - * NOTE: The last entry in `paramList' is always an - * entry with kind == PARAM_INVALID. + * Minor modification: Param nodes now have an extra field, + * `paramkind' which specifies the type of parameter + * (see params.h). So while searching the paramList for + * a paramname/value pair, we have also to check for `kind'. + * + * NOTE: The last entry in `paramList' is always an + * entry with kind == PARAM_INVALID. * ---------------------------------------------------------------- */ Datum -ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull) +ExecEvalParam(Param * expression, ExprContext * econtext, bool * isNull) { - - char *thisParameterName; - int thisParameterKind; - AttrNumber thisParameterId; - int matchFound; - ParamListInfo paramList; - - thisParameterName = expression->paramname; - thisParameterKind = expression->paramkind; - thisParameterId = expression->paramid; - paramList = econtext->ecxt_param_list_info; - - *isNull = false; - /* - * search the list with the parameter info to find a matching name. - * An entry with an InvalidName denotes the last element in the array. - */ - matchFound = 0; - if (paramList != NULL) { + + char *thisParameterName; + int thisParameterKind; + AttrNumber thisParameterId; + int matchFound; + ParamListInfo paramList; + + thisParameterName = expression->paramname; + thisParameterKind = expression->paramkind; + thisParameterId = expression->paramid; + paramList = econtext->ecxt_param_list_info; + + *isNull = false; + /* - * search for an entry in 'paramList' that matches - * the `expression'. + * search the list with the parameter info to find a matching name. An + * entry with an InvalidName denotes the last element in the array. */ - while(paramList->kind != PARAM_INVALID && !matchFound) { - switch (thisParameterKind) { - case PARAM_NAMED: - if (thisParameterKind == paramList->kind && - strcmp(paramList->name, thisParameterName) == 0){ - matchFound = 1; - } - break; - case PARAM_NUM: - if (thisParameterKind == paramList->kind && - paramList->id == thisParameterId) { - matchFound = 1; - } - break; - case PARAM_OLD: - case PARAM_NEW: - if (thisParameterKind == paramList->kind && - paramList->id == thisParameterId) - { - matchFound = 1; - /* - * sanity check - */ - if (strcmp(paramList->name, thisParameterName) != 0){ - elog(WARN, - "ExecEvalParam: new/old params with same id & diff names"); + matchFound = 0; + if (paramList != NULL) + { + + /* + * search for an entry in 'paramList' that matches the + * `expression'. + */ + while (paramList->kind != PARAM_INVALID && !matchFound) + { + switch (thisParameterKind) + { + case PARAM_NAMED: + if (thisParameterKind == paramList->kind && + strcmp(paramList->name, thisParameterName) == 0) + { + matchFound = 1; + } + break; + case PARAM_NUM: + if (thisParameterKind == paramList->kind && + paramList->id == thisParameterId) + { + matchFound = 1; + } + break; + case PARAM_OLD: + case PARAM_NEW: + if (thisParameterKind == paramList->kind && + paramList->id == thisParameterId) + { + matchFound = 1; + + /* + * sanity check + */ + if (strcmp(paramList->name, thisParameterName) != 0) + { + elog(WARN, + "ExecEvalParam: new/old params with same id & diff names"); + } + } + break; + default: + + /* + * oops! this is not supposed to happen! + */ + elog(WARN, "ExecEvalParam: invalid paramkind %d", + thisParameterKind); } - } - break; - default: + if (!matchFound) + { + paramList++; + } + } /* while */ + } /* if */ + + if (!matchFound) + { + /* - * oops! this is not supposed to happen! + * ooops! we couldn't find this parameter in the parameter list. + * Signal an error */ - elog(WARN, "ExecEvalParam: invalid paramkind %d", - thisParameterKind); - } - if (! matchFound) { - paramList++; - } - } /*while*/ - } /*if*/ - - if (!matchFound) { + elog(WARN, "ExecEvalParam: Unknown value for parameter %s", + thisParameterName); + } + /* - * ooops! we couldn't find this parameter - * in the parameter list. Signal an error + * return the value. */ - elog(WARN, "ExecEvalParam: Unknown value for parameter %s", - thisParameterName); - } - - /* - * return the value. - */ - if (paramList->isnull) + if (paramList->isnull) { - *isNull = true; - return (Datum)NULL; + *isNull = true; + return (Datum) NULL; } - - if (expression->param_tlist != NIL) + + if (expression->param_tlist != NIL) { - HeapTuple tup; - Datum value; - List *tlist = expression->param_tlist; - TargetEntry *tle = (TargetEntry *)lfirst(tlist); - TupleTableSlot *slot = (TupleTableSlot *)paramList->value; - - tup = slot->val; - value = ProjectAttribute(slot->ttc_tupleDescriptor, - tle, tup, isNull); - return value; + HeapTuple tup; + Datum value; + List *tlist = expression->param_tlist; + TargetEntry *tle = (TargetEntry *) lfirst(tlist); + TupleTableSlot *slot = (TupleTableSlot *) paramList->value; + + tup = slot->val; + value = ProjectAttribute(slot->ttc_tupleDescriptor, + tle, tup, isNull); + return value; } - return(paramList->value); + return (paramList->value); } /* ---------------------------------------------------------------- - * ExecEvalOper / ExecEvalFunc support routines + * ExecEvalOper / ExecEvalFunc support routines * ---------------------------------------------------------------- */ /* ---------------- - * GetAttributeByName - * GetAttributeByNum + * GetAttributeByName + * GetAttributeByNum * - * These are functions which return the value of the - * named attribute out of the tuple from the arg slot. User defined - * C functions which take a tuple as an argument are expected - * to use this. Ex: overpaid(EMP) might call GetAttributeByNum(). + * These are functions which return the value of the + * named attribute out of the tuple from the arg slot. User defined + * C functions which take a tuple as an argument are expected + * to use this. Ex: overpaid(EMP) might call GetAttributeByNum(). * ---------------- */ #ifdef NOT_USED -static char * -GetAttributeByNum(TupleTableSlot *slot, - AttrNumber attrno, - bool *isNull) +static char * +GetAttributeByNum(TupleTableSlot * slot, + AttrNumber attrno, + bool * isNull) { - Datum retval; - - if (!AttributeNumberIsValid(attrno)) - elog(WARN, "GetAttributeByNum: Invalid attribute number"); - - if (!AttrNumberIsForUserDefinedAttr(attrno)) - elog(WARN, "GetAttributeByNum: cannot access system attributes here"); - - if (isNull == (bool *)NULL) - elog(WARN, "GetAttributeByNum: a NULL isNull flag was passed"); - - if (TupIsNull(slot)) + Datum retval; + + if (!AttributeNumberIsValid(attrno)) + elog(WARN, "GetAttributeByNum: Invalid attribute number"); + + if (!AttrNumberIsForUserDefinedAttr(attrno)) + elog(WARN, "GetAttributeByNum: cannot access system attributes here"); + + if (isNull == (bool *) NULL) + elog(WARN, "GetAttributeByNum: a NULL isNull flag was passed"); + + if (TupIsNull(slot)) { - *isNull = true; - return (char *) NULL; + *isNull = true; + return (char *) NULL; } - - retval = (Datum) - heap_getattr(slot->val, - slot->ttc_buffer, - attrno, - slot->ttc_tupleDescriptor, - isNull); - if (*isNull) - return (char *) NULL; - return (char *) retval; + + retval = (Datum) + heap_getattr(slot->val, + slot->ttc_buffer, + attrno, + slot->ttc_tupleDescriptor, + isNull); + if (*isNull) + return (char *) NULL; + return (char *) retval; } + #endif /* XXX char16 name for catalogs */ #ifdef NOT_USED -char * -att_by_num(TupleTableSlot *slot, - AttrNumber attrno, - bool *isNull) +char * +att_by_num(TupleTableSlot * slot, + AttrNumber attrno, + bool * isNull) { - return(GetAttributeByNum(slot, attrno, isNull)); + return (GetAttributeByNum(slot, attrno, isNull)); } + #endif -char * -GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) +char * +GetAttributeByName(TupleTableSlot * slot, char *attname, bool * isNull) { - AttrNumber attrno; - TupleDesc tupdesc; - HeapTuple tuple; - Datum retval; - int natts; - int i; - - if (attname == NULL) - elog(WARN, "GetAttributeByName: Invalid attribute name"); - - if (isNull == (bool *)NULL) - elog(WARN, "GetAttributeByName: a NULL isNull flag was passed"); - - if (TupIsNull(slot)) + AttrNumber attrno; + TupleDesc tupdesc; + HeapTuple tuple; + Datum retval; + int natts; + int i; + + if (attname == NULL) + elog(WARN, "GetAttributeByName: Invalid attribute name"); + + if (isNull == (bool *) NULL) + elog(WARN, "GetAttributeByName: a NULL isNull flag was passed"); + + if (TupIsNull(slot)) { - *isNull = true; - return (char *) NULL; + *isNull = true; + return (char *) NULL; } - - tupdesc = slot->ttc_tupleDescriptor; - tuple = slot->val; - - natts = tuple->t_natts; - - attrno = InvalidAttrNumber; - for (i=0;i<tupdesc->natts;i++) { - if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) { - attrno = tupdesc->attrs[i]->attnum; - break; + + tupdesc = slot->ttc_tupleDescriptor; + tuple = slot->val; + + natts = tuple->t_natts; + + attrno = InvalidAttrNumber; + for (i = 0; i < tupdesc->natts; i++) + { + if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) + { + attrno = tupdesc->attrs[i]->attnum; + break; + } } - } - - if (attrno == InvalidAttrNumber) - elog(WARN, "GetAttributeByName: attribute %s not found", attname); - - retval = (Datum) - heap_getattr(slot->val, - slot->ttc_buffer, - attrno, - tupdesc, - isNull); - if (*isNull) - return (char *) NULL; - return (char *) retval; + + if (attrno == InvalidAttrNumber) + elog(WARN, "GetAttributeByName: attribute %s not found", attname); + + retval = (Datum) + heap_getattr(slot->val, + slot->ttc_buffer, + attrno, + tupdesc, + isNull); + if (*isNull) + return (char *) NULL; + return (char *) retval; } /* XXX char16 name for catalogs */ #ifdef NOT_USED -char * -att_by_name(TupleTableSlot *slot, char *attname, bool *isNull) +char * +att_by_name(TupleTableSlot * slot, char *attname, bool * isNull) { - return(GetAttributeByName(slot, attname, isNull)); + return (GetAttributeByName(slot, attname, isNull)); } + #endif static void ExecEvalFuncArgs(FunctionCachePtr fcache, - ExprContext *econtext, - List *argList, - Datum argV[], - bool *argIsDone) + ExprContext * econtext, + List * argList, + Datum argV[], + bool * argIsDone) { - int i; - bool argIsNull, *nullVect; - List *arg; - - nullVect = fcache->nullVect; - - i = 0; - foreach (arg, argList) { - /* ---------------- - * evaluate the expression, in general functions cannot take - * sets as arguments but we make an exception in the case of - * nested dot expressions. We have to watch out for this case - * here. - * ---------------- - */ - argV[i] = (Datum) - ExecEvalExpr((Node *) lfirst(arg), - econtext, - &argIsNull, - argIsDone); - if (! (*argIsDone)) - { - Assert(i == 0); - fcache->setArg = (char *)argV[0]; - fcache->hasSetArg = true; - } - if (argIsNull) - nullVect[i] = true; - else - nullVect[i] = false; - i++; - } + int i; + bool argIsNull, + *nullVect; + List *arg; + + nullVect = fcache->nullVect; + + i = 0; + foreach(arg, argList) + { + /* ---------------- + * evaluate the expression, in general functions cannot take + * sets as arguments but we make an exception in the case of + * nested dot expressions. We have to watch out for this case + * here. + * ---------------- + */ + argV[i] = (Datum) + ExecEvalExpr((Node *) lfirst(arg), + econtext, + &argIsNull, + argIsDone); + if (!(*argIsDone)) + { + Assert(i == 0); + fcache->setArg = (char *) argV[0]; + fcache->hasSetArg = true; + } + if (argIsNull) + nullVect[i] = true; + else + nullVect[i] = false; + i++; + } } /* ---------------- - * ExecMakeFunctionResult + * ExecMakeFunctionResult * ---------------- */ -static Datum -ExecMakeFunctionResult(Node *node, - List *arguments, - ExprContext *econtext, - bool *isNull, - bool *isDone) +static Datum +ExecMakeFunctionResult(Node * node, + List * arguments, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - Datum argv[MAXFMGRARGS]; - FunctionCachePtr fcache; - Func *funcNode = NULL; - Oper *operNode = NULL; - bool funcisset = false; - - /* - * This is kind of ugly, Func nodes now have targetlists so that - * we know when and what to project out from postquel function results. - * This means we have to pass the func node all the way down instead - * of using only the fcache struct as before. ExecMakeFunctionResult - * becomes a little bit more of a dual personality as a result. - */ - if (IsA(node,Func)) + Datum argv[MAXFMGRARGS]; + FunctionCachePtr fcache; + Func *funcNode = NULL; + Oper *operNode = NULL; + bool funcisset = false; + + /* + * This is kind of ugly, Func nodes now have targetlists so that we + * know when and what to project out from postquel function results. + * This means we have to pass the func node all the way down instead + * of using only the fcache struct as before. ExecMakeFunctionResult + * becomes a little bit more of a dual personality as a result. + */ + if (IsA(node, Func)) { - funcNode = (Func *)node; - fcache = funcNode->func_fcache; + funcNode = (Func *) node; + fcache = funcNode->func_fcache; } - else + else { - operNode = (Oper *)node; - fcache = operNode->op_fcache; + operNode = (Oper *) node; + fcache = operNode->op_fcache; } - - /* ---------------- - * arguments is a list of expressions to evaluate - * before passing to the function manager. - * We collect the results of evaluating the expressions - * into a datum array (argv) and pass this array to arrayFmgr() - * ---------------- - */ - if (fcache->nargs != 0) { - bool argDone; - - if (fcache->nargs > MAXFMGRARGS) - elog(WARN, "ExecMakeFunctionResult: too many arguments"); - - /* - * If the setArg in the fcache is set we have an argument - * returning a set of tuples (i.e. a nested dot expression). We - * don't want to evaluate the arguments again until the function - * is done. hasSetArg will always be false until we eval the args - * for the first time. We should set this in the parser. + + /* ---------------- + * arguments is a list of expressions to evaluate + * before passing to the function manager. + * We collect the results of evaluating the expressions + * into a datum array (argv) and pass this array to arrayFmgr() + * ---------------- */ - if ((fcache->hasSetArg) && fcache->setArg != NULL) - { - argv[0] = (Datum)fcache->setArg; - argDone = false; - } - else - ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); - - if ((fcache->hasSetArg) && (argDone)) { - if (isDone) *isDone = true; - return (Datum)NULL; - } - } - - /* If this function is really a set, we have to diddle with things. - * If the function has already been called at least once, then the - * setArg field of the fcache holds - * the OID of this set in pg_proc. (This is not quite legit, since - * the setArg field is really for functions which take sets of tuples - * as input - set functions take no inputs at all. But it's a nice - * place to stash this value, for now.) - * - * If this is the first call of the set's function, then - * the call to ExecEvalFuncArgs above just returned the OID of - * the pg_proc tuple which defines this set. So replace the existing - * funcid in the funcnode with the set's OID. Also, we want a new - * fcache which points to the right function, so get that, now that - * we have the right OID. Also zero out the argv, since the real - * set doesn't take any arguments. - */ - if (((Func *)node)->funcid == SetEvalRegProcedure) { - funcisset = true; - if (fcache->setArg) { - argv[0] = 0; - - ((Func *)node)->funcid = (Oid) PointerGetDatum(fcache->setArg); - - } else { - ((Func *)node)->funcid = (Oid) argv[0]; - setFcache(node, argv[0], NIL,econtext); - fcache = ((Func *)node)->func_fcache; - fcache->setArg = (char*)argv[0]; - argv[0] = (Datum)0; + if (fcache->nargs != 0) + { + bool argDone; + + if (fcache->nargs > MAXFMGRARGS) + elog(WARN, "ExecMakeFunctionResult: too many arguments"); + + /* + * If the setArg in the fcache is set we have an argument + * returning a set of tuples (i.e. a nested dot expression). We + * don't want to evaluate the arguments again until the function + * is done. hasSetArg will always be false until we eval the args + * for the first time. We should set this in the parser. + */ + if ((fcache->hasSetArg) && fcache->setArg != NULL) + { + argv[0] = (Datum) fcache->setArg; + argDone = false; + } + else + ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); + + if ((fcache->hasSetArg) && (argDone)) + { + if (isDone) + *isDone = true; + return (Datum) NULL; + } } - } - - /* ---------------- - * now return the value gotten by calling the function manager, - * passing the function the evaluated parameter values. - * ---------------- - */ - if (fcache->language == SQLlanguageId) { - Datum result; - - Assert(funcNode); - result = postquel_function (funcNode, (char **) argv, isNull, isDone); + /* - * finagle the situation where we are iterating through all results - * in a nested dot function (whose argument function returns a set - * of tuples) and the current function finally finishes. We need to - * get the next argument in the set and run the function all over - * again. This is getting unclean. + * If this function is really a set, we have to diddle with things. If + * the function has already been called at least once, then the setArg + * field of the fcache holds the OID of this set in pg_proc. (This is + * not quite legit, since the setArg field is really for functions + * which take sets of tuples as input - set functions take no inputs + * at all. But it's a nice place to stash this value, for now.) + * + * If this is the first call of the set's function, then the call to + * ExecEvalFuncArgs above just returned the OID of the pg_proc tuple + * which defines this set. So replace the existing funcid in the + * funcnode with the set's OID. Also, we want a new fcache which + * points to the right function, so get that, now that we have the + * right OID. Also zero out the argv, since the real set doesn't take + * any arguments. */ - if ((*isDone) && (fcache->hasSetArg)) { - bool argDone; - - ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); - - if (argDone) { - fcache->setArg = (char *)NULL; - *isDone = true; - result = (Datum)NULL; - } - else - result = postquel_function(funcNode, - (char **) argv, - isNull, - isDone); + if (((Func *) node)->funcid == SetEvalRegProcedure) + { + funcisset = true; + if (fcache->setArg) + { + argv[0] = 0; + + ((Func *) node)->funcid = (Oid) PointerGetDatum(fcache->setArg); + + } + else + { + ((Func *) node)->funcid = (Oid) argv[0]; + setFcache(node, argv[0], NIL, econtext); + fcache = ((Func *) node)->func_fcache; + fcache->setArg = (char *) argv[0]; + argv[0] = (Datum) 0; + } } - if (funcisset) { - /* reset the funcid so that next call to this routine will - * still recognize this func as a set. - * Note that for now we assume that the set function in - * pg_proc must be a Postquel function - the funcid is - * not reset below for C functions. - */ - ((Func *)node)->funcid = SetEvalRegProcedure; - /* If we're done with the results of this function, get rid - * of its func cache. - */ - if (*isDone) { - ((Func *)node)->func_fcache = NULL; - } + + /* ---------------- + * now return the value gotten by calling the function manager, + * passing the function the evaluated parameter values. + * ---------------- + */ + if (fcache->language == SQLlanguageId) + { + Datum result; + + Assert(funcNode); + result = postquel_function(funcNode, (char **) argv, isNull, isDone); + + /* + * finagle the situation where we are iterating through all + * results in a nested dot function (whose argument function + * returns a set of tuples) and the current function finally + * finishes. We need to get the next argument in the set and run + * the function all over again. This is getting unclean. + */ + if ((*isDone) && (fcache->hasSetArg)) + { + bool argDone; + + ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone); + + if (argDone) + { + fcache->setArg = (char *) NULL; + *isDone = true; + result = (Datum) NULL; + } + else + result = postquel_function(funcNode, + (char **) argv, + isNull, + isDone); + } + if (funcisset) + { + + /* + * reset the funcid so that next call to this routine will + * still recognize this func as a set. Note that for now we + * assume that the set function in pg_proc must be a Postquel + * function - the funcid is not reset below for C functions. + */ + ((Func *) node)->funcid = SetEvalRegProcedure; + + /* + * If we're done with the results of this function, get rid of + * its func cache. + */ + if (*isDone) + { + ((Func *) node)->func_fcache = NULL; + } + } + return result; } - return result; - } - else + else { - int i; - - if (isDone) *isDone = true; - for (i = 0; i < fcache->nargs; i++) - if (fcache->nullVect[i] == true) *isNull = true; - - return((Datum) fmgr_c(fcache->func, fcache->foid, fcache->nargs, - (FmgrValues *) argv, isNull)); + int i; + + if (isDone) + *isDone = true; + for (i = 0; i < fcache->nargs; i++) + if (fcache->nullVect[i] == true) + *isNull = true; + + return ((Datum) fmgr_c(fcache->func, fcache->foid, fcache->nargs, + (FmgrValues *) argv, isNull)); } } /* ---------------------------------------------------------------- - * ExecEvalOper - * ExecEvalFunc - * - * Evaluate the functional result of a list of arguments by calling the - * function manager. Note that in the case of operator expressions, the - * optimizer had better have already replaced the operator OID with the - * appropriate function OID or we're hosed. + * ExecEvalOper + * ExecEvalFunc + * + * Evaluate the functional result of a list of arguments by calling the + * function manager. Note that in the case of operator expressions, the + * optimizer had better have already replaced the operator OID with the + * appropriate function OID or we're hosed. * * old comments - * Presumably the function manager will not take null arguments, so we - * check for null arguments before sending the arguments to (fmgr). - * - * Returns the value of the functional expression. + * Presumably the function manager will not take null arguments, so we + * check for null arguments before sending the arguments to (fmgr). + * + * Returns the value of the functional expression. * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecEvalOper + * ExecEvalOper * ---------------------------------------------------------------- */ -static Datum -ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalOper(Expr * opClause, ExprContext * econtext, bool * isNull) { - Oper *op; - List *argList; - FunctionCachePtr fcache; - bool isDone; - - /* ---------------- - * an opclause is a list (op args). (I think) - * - * we extract the oid of the function associated with - * the op and then pass the work onto ExecMakeFunctionResult - * which evaluates the arguments and returns the result of - * calling the function on the evaluated arguments. - * ---------------- - */ - op = (Oper *) opClause->oper; - argList = opClause->args; - - /* - * get the fcache from the Oper node. - * If it is NULL, then initialize it - */ - fcache = op->op_fcache; - if (fcache == NULL) { - setFcache((Node*)op, op->opid, argList, econtext); - fcache = op->op_fcache; - } - - /* ----------- - * call ExecMakeFunctionResult() with a dummy isDone that we ignore. - * We don't have operator whose arguments are sets. - * ----------- - */ - return - ExecMakeFunctionResult((Node *)op, argList, econtext, isNull, &isDone); + Oper *op; + List *argList; + FunctionCachePtr fcache; + bool isDone; + + /* ---------------- + * an opclause is a list (op args). (I think) + * + * we extract the oid of the function associated with + * the op and then pass the work onto ExecMakeFunctionResult + * which evaluates the arguments and returns the result of + * calling the function on the evaluated arguments. + * ---------------- + */ + op = (Oper *) opClause->oper; + argList = opClause->args; + + /* + * get the fcache from the Oper node. If it is NULL, then initialize + * it + */ + fcache = op->op_fcache; + if (fcache == NULL) + { + setFcache((Node *) op, op->opid, argList, econtext); + fcache = op->op_fcache; + } + + /* ----------- + * call ExecMakeFunctionResult() with a dummy isDone that we ignore. + * We don't have operator whose arguments are sets. + * ----------- + */ + return + ExecMakeFunctionResult((Node *) op, argList, econtext, isNull, &isDone); } /* ---------------------------------------------------------------- - * ExecEvalFunc + * ExecEvalFunc * ---------------------------------------------------------------- */ -static Datum -ExecEvalFunc(Expr *funcClause, - ExprContext *econtext, - bool *isNull, - bool *isDone) +static Datum +ExecEvalFunc(Expr * funcClause, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - Func *func; - List *argList; - FunctionCachePtr fcache; - - /* ---------------- - * an funcclause is a list (func args). (I think) - * - * we extract the oid of the function associated with - * the func node and then pass the work onto ExecMakeFunctionResult - * which evaluates the arguments and returns the result of - * calling the function on the evaluated arguments. - * - * this is nearly identical to the ExecEvalOper code. - * ---------------- - */ - func = (Func *)funcClause->oper; - argList = funcClause->args; - - /* - * get the fcache from the Func node. - * If it is NULL, then initialize it - */ - fcache = func->func_fcache; - if (fcache == NULL) { - setFcache((Node*)func, func->funcid, argList, econtext); - fcache = func->func_fcache; - } - - return - ExecMakeFunctionResult((Node*)func, argList, econtext, isNull, isDone); + Func *func; + List *argList; + FunctionCachePtr fcache; + + /* ---------------- + * an funcclause is a list (func args). (I think) + * + * we extract the oid of the function associated with + * the func node and then pass the work onto ExecMakeFunctionResult + * which evaluates the arguments and returns the result of + * calling the function on the evaluated arguments. + * + * this is nearly identical to the ExecEvalOper code. + * ---------------- + */ + func = (Func *) funcClause->oper; + argList = funcClause->args; + + /* + * get the fcache from the Func node. If it is NULL, then initialize + * it + */ + fcache = func->func_fcache; + if (fcache == NULL) + { + setFcache((Node *) func, func->funcid, argList, econtext); + fcache = func->func_fcache; + } + + return + ExecMakeFunctionResult((Node *) func, argList, econtext, isNull, isDone); } /* ---------------------------------------------------------------- - * ExecEvalNot - * ExecEvalOr - * ExecEvalAnd - * - * Evaluate boolean expressions. Evaluation of 'or' is - * short-circuited when the first true (or null) value is found. + * ExecEvalNot + * ExecEvalOr + * ExecEvalAnd * - * The query planner reformulates clause expressions in the - * qualification to conjunctive normal form. If we ever get - * an AND to evaluate, we can be sure that it's not a top-level - * clause in the qualification, but appears lower (as a function - * argument, for example), or in the target list. Not that you - * need to know this, mind you... + * Evaluate boolean expressions. Evaluation of 'or' is + * short-circuited when the first true (or null) value is found. + * + * The query planner reformulates clause expressions in the + * qualification to conjunctive normal form. If we ever get + * an AND to evaluate, we can be sure that it's not a top-level + * clause in the qualification, but appears lower (as a function + * argument, for example), or in the target list. Not that you + * need to know this, mind you... * ---------------------------------------------------------------- */ -static Datum -ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalNot(Expr * notclause, ExprContext * econtext, bool * isNull) { - Datum expr_value; - Node *clause; - bool isDone; - - clause = lfirst(notclause->args); - - /* ---------------- - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. - * ---------------- - */ - expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone); - - /* ---------------- - * if the expression evaluates to null, then we just - * cascade the null back to whoever called us. - * ---------------- - */ - if (*isNull) - return expr_value; - - /* ---------------- - * evaluation of 'not' is simple.. expr is false, then - * return 'true' and vice versa. - * ---------------- - */ - if (DatumGetInt32(expr_value) == 0) - return (Datum) true; - - return (Datum) false; -} + Datum expr_value; + Node *clause; + bool isDone; + + clause = lfirst(notclause->args); -/* ---------------------------------------------------------------- - * ExecEvalOr - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull) -{ - List *clauses; - List *clause; - bool isDone; - bool IsNull; - Datum const_value = 0; - - IsNull = false; - clauses = orExpr->args; - - /* ---------------- - * we use three valued logic functions here... - * we evaluate each of the clauses in turn, - * as soon as one is true we return that - * value. If none is true and none of the - * clauses evaluate to NULL we return - * the value of the last clause evaluated (which - * should be false) with *isNull set to false else - * if none is true and at least one clause evaluated - * to NULL we set *isNull flag to true - - * ---------------- - */ - foreach (clause, clauses) { - /* ---------------- - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. + * We don't iterate over sets in the quals, so pass in an isDone + * flag, but ignore it. * ---------------- */ - const_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, - isNull, - &isDone); - + expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone); + /* ---------------- - * if the expression evaluates to null, then we - * remember it in the local IsNull flag, if none of the - * clauses are true then we need to set *isNull - * to true again. + * if the expression evaluates to null, then we just + * cascade the null back to whoever called us. * ---------------- */ if (*isNull) - IsNull = *isNull; - + return expr_value; + /* ---------------- - * if we have a true result, then we return it. + * evaluation of 'not' is simple.. expr is false, then + * return 'true' and vice versa. * ---------------- */ - if (DatumGetInt32(const_value) != 0) - return const_value; - } - - /* IsNull is true if at least one clause evaluated to NULL */ - *isNull = IsNull; - return const_value; + if (DatumGetInt32(expr_value) == 0) + return (Datum) true; + + return (Datum) false; } /* ---------------------------------------------------------------- - * ExecEvalAnd + * ExecEvalOr * ---------------------------------------------------------------- */ -static Datum -ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) +static Datum +ExecEvalOr(Expr * orExpr, ExprContext * econtext, bool * isNull) { - List *clauses; - List *clause; - Datum const_value = 0; - bool isDone; - bool IsNull; - - IsNull = false; - - clauses = andExpr->args; - - /* ---------------- - * we evaluate each of the clauses in turn, - * as soon as one is false we return that - * value. If none are false or NULL then we return - * the value of the last clause evaluated, which - * should be true. - * ---------------- - */ - foreach (clause, clauses) { - - /* ---------------- - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. - * ---------------- - */ - const_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, - isNull, - &isDone); - + List *clauses; + List *clause; + bool isDone; + bool IsNull; + Datum const_value = 0; + + IsNull = false; + clauses = orExpr->args; + /* ---------------- - * if the expression evaluates to null, then we - * remember it in IsNull, if none of the clauses after - * this evaluates to false we will have to set *isNull - * to true again. + * we use three valued logic functions here... + * we evaluate each of the clauses in turn, + * as soon as one is true we return that + * value. If none is true and none of the + * clauses evaluate to NULL we return + * the value of the last clause evaluated (which + * should be false) with *isNull set to false else + * if none is true and at least one clause evaluated + * to NULL we set *isNull flag to true - * ---------------- */ - if (*isNull) - IsNull = *isNull; - + foreach(clause, clauses) + { + + /* ---------------- + * We don't iterate over sets in the quals, so pass in an isDone + * flag, but ignore it. + * ---------------- + */ + const_value = ExecEvalExpr((Node *) lfirst(clause), + econtext, + isNull, + &isDone); + + /* ---------------- + * if the expression evaluates to null, then we + * remember it in the local IsNull flag, if none of the + * clauses are true then we need to set *isNull + * to true again. + * ---------------- + */ + if (*isNull) + IsNull = *isNull; + + /* ---------------- + * if we have a true result, then we return it. + * ---------------- + */ + if (DatumGetInt32(const_value) != 0) + return const_value; + } + + /* IsNull is true if at least one clause evaluated to NULL */ + *isNull = IsNull; + return const_value; +} + +/* ---------------------------------------------------------------- + * ExecEvalAnd + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalAnd(Expr * andExpr, ExprContext * econtext, bool * isNull) +{ + List *clauses; + List *clause; + Datum const_value = 0; + bool isDone; + bool IsNull; + + IsNull = false; + + clauses = andExpr->args; + /* ---------------- - * if we have a false result, then we return it, since the - * conjunction must be false. + * we evaluate each of the clauses in turn, + * as soon as one is false we return that + * value. If none are false or NULL then we return + * the value of the last clause evaluated, which + * should be true. * ---------------- */ - if (DatumGetInt32(const_value) == 0) - return const_value; - } - - *isNull = IsNull; - return const_value; + foreach(clause, clauses) + { + + /* ---------------- + * We don't iterate over sets in the quals, so pass in an isDone + * flag, but ignore it. + * ---------------- + */ + const_value = ExecEvalExpr((Node *) lfirst(clause), + econtext, + isNull, + &isDone); + + /* ---------------- + * if the expression evaluates to null, then we + * remember it in IsNull, if none of the clauses after + * this evaluates to false we will have to set *isNull + * to true again. + * ---------------- + */ + if (*isNull) + IsNull = *isNull; + + /* ---------------- + * if we have a false result, then we return it, since the + * conjunction must be false. + * ---------------- + */ + if (DatumGetInt32(const_value) == 0) + return const_value; + } + + *isNull = IsNull; + return const_value; } -/* ---------------------------------------------------------------- - * ExecEvalExpr - * - * Recursively evaluate a targetlist or qualification expression. +/* ---------------------------------------------------------------- + * ExecEvalExpr + * + * Recursively evaluate a targetlist or qualification expression. * - * This routine is an inner loop routine and should be as fast - * as possible. + * This routine is an inner loop routine and should be as fast + * as possible. * - * Node comparison functions were replaced by macros for speed and to plug - * memory leaks incurred by using the planner's Lispy stuff for - * comparisons. Order of evaluation of node comparisons IS IMPORTANT; - * the macros do no checks. Order of evaluation: - * - * o an isnull check, largely to avoid coredumps since greg doubts this - * routine is called with a null ptr anyway in proper operation, but is - * not completely sure... - * o ExactNodeType checks. - * o clause checks or other checks where we look at the lfirst of something. + * Node comparison functions were replaced by macros for speed and to plug + * memory leaks incurred by using the planner's Lispy stuff for + * comparisons. Order of evaluation of node comparisons IS IMPORTANT; + * the macros do no checks. Order of evaluation: + * + * o an isnull check, largely to avoid coredumps since greg doubts this + * routine is called with a null ptr anyway in proper operation, but is + * not completely sure... + * o ExactNodeType checks. + * o clause checks or other checks where we look at the lfirst of something. * ---------------------------------------------------------------- */ Datum -ExecEvalExpr(Node *expression, - ExprContext *econtext, - bool *isNull, - bool *isDone) +ExecEvalExpr(Node * expression, + ExprContext * econtext, + bool * isNull, + bool * isDone) { - Datum retDatum = 0; - - *isNull = false; - - /* - * Some callers don't care about is done and only want 1 result. They - * indicate this by passing NULL - */ - if (isDone) - *isDone = true; - - /* ---------------- - * here we dispatch the work to the appropriate type - * of function given the type of our expression. - * ---------------- - */ - if (expression == NULL) { - *isNull = true; - return (Datum) true; - } - - switch(nodeTag(expression)) { - case T_Var: - retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull); - break; - case T_Const: { - Const *con = (Const *)expression; - - if (con->constisnull) - *isNull = true; - retDatum = con->constvalue; - break; - } - case T_Param: - retDatum = (Datum)ExecEvalParam((Param *)expression, econtext, isNull); - break; - case T_Iter: - retDatum = (Datum) ExecEvalIter((Iter *) expression, - econtext, - isNull, - isDone); - break; - case T_Aggreg: - retDatum = (Datum) ExecEvalAggreg((Aggreg *)expression, - econtext, - isNull); - break; - case T_ArrayRef: - retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression, - econtext, - isNull, - isDone); - break; - case T_Expr: { - Expr *expr = (Expr *)expression; - switch (expr->opType) { - case OP_EXPR: - retDatum = (Datum) ExecEvalOper(expr, econtext, isNull); - break; - case FUNC_EXPR: - retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone); - break; - case OR_EXPR: - retDatum = (Datum) ExecEvalOr(expr, econtext, isNull); - break; - case AND_EXPR: - retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull); - break; - case NOT_EXPR: - retDatum = (Datum) ExecEvalNot(expr, econtext, isNull); - break; + Datum retDatum = 0; + + *isNull = false; + + /* + * Some callers don't care about is done and only want 1 result. They + * indicate this by passing NULL + */ + if (isDone) + *isDone = true; + + /* ---------------- + * here we dispatch the work to the appropriate type + * of function given the type of our expression. + * ---------------- + */ + if (expression == NULL) + { + *isNull = true; + return (Datum) true; + } + + switch (nodeTag(expression)) + { + case T_Var: + retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull); + break; + case T_Const: + { + Const *con = (Const *) expression; + + if (con->constisnull) + *isNull = true; + retDatum = con->constvalue; + break; + } + case T_Param: + retDatum = (Datum) ExecEvalParam((Param *) expression, econtext, isNull); + break; + case T_Iter: + retDatum = (Datum) ExecEvalIter((Iter *) expression, + econtext, + isNull, + isDone); + break; + case T_Aggreg: + retDatum = (Datum) ExecEvalAggreg((Aggreg *) expression, + econtext, + isNull); + break; + case T_ArrayRef: + retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression, + econtext, + isNull, + isDone); + break; + case T_Expr: + { + Expr *expr = (Expr *) expression; + + switch (expr->opType) + { + case OP_EXPR: + retDatum = (Datum) ExecEvalOper(expr, econtext, isNull); + break; + case FUNC_EXPR: + retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone); + break; + case OR_EXPR: + retDatum = (Datum) ExecEvalOr(expr, econtext, isNull); + break; + case AND_EXPR: + retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull); + break; + case NOT_EXPR: + retDatum = (Datum) ExecEvalNot(expr, econtext, isNull); + break; + default: + elog(WARN, "ExecEvalExpr: unknown expression type"); + break; + } + break; + } default: - elog(WARN, "ExecEvalExpr: unknown expression type"); - break; + elog(WARN, "ExecEvalExpr: unknown expression type"); + break; } - break; - } - default: - elog(WARN, "ExecEvalExpr: unknown expression type"); - break; - } - - return retDatum; + + return retDatum; } /* ---------------------------------------------------------------- - * ExecQual / ExecTargetList + * ExecQual / ExecTargetList * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecQualClause + * ExecQualClause * - * this is a workhorse for ExecQual. ExecQual has to deal - * with a list of qualifications, so it passes each qualification - * in the list to this function one at a time. ExecQualClause - * returns true when the qualification *fails* and false if - * the qualification succeeded (meaning we have to test the - * rest of the qualification) + * this is a workhorse for ExecQual. ExecQual has to deal + * with a list of qualifications, so it passes each qualification + * in the list to this function one at a time. ExecQualClause + * returns true when the qualification *fails* and false if + * the qualification succeeded (meaning we have to test the + * rest of the qualification) * ---------------------------------------------------------------- */ -static bool -ExecQualClause(Node *clause, ExprContext *econtext) +static bool +ExecQualClause(Node * clause, ExprContext * econtext) { - Datum expr_value; - bool isNull; - bool isDone; - - /* when there is a null clause, consider the qualification to be true */ - if (clause == NULL) - return true; - - /* - * pass isDone, but ignore it. We don't iterate over multiple - * returns in the qualifications. - */ - expr_value = (Datum) - ExecEvalExpr(clause, econtext, &isNull, &isDone); - - /* ---------------- - * this is interesting behaviour here. When a clause evaluates - * to null, then we consider this as passing the qualification. - * it seems kind of like, if the qual is NULL, then there's no - * qual.. - * ---------------- - */ - if (isNull) - return true; - - /* ---------------- - * remember, we return true when the qualification fails.. - * ---------------- - */ - if (DatumGetInt32(expr_value) == 0) - return true; - - return false; + Datum expr_value; + bool isNull; + bool isDone; + + /* when there is a null clause, consider the qualification to be true */ + if (clause == NULL) + return true; + + /* + * pass isDone, but ignore it. We don't iterate over multiple returns + * in the qualifications. + */ + expr_value = (Datum) + ExecEvalExpr(clause, econtext, &isNull, &isDone); + + /* ---------------- + * this is interesting behaviour here. When a clause evaluates + * to null, then we consider this as passing the qualification. + * it seems kind of like, if the qual is NULL, then there's no + * qual.. + * ---------------- + */ + if (isNull) + return true; + + /* ---------------- + * remember, we return true when the qualification fails.. + * ---------------- + */ + if (DatumGetInt32(expr_value) == 0) + return true; + + return false; } /* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression and returns t - * iff none of the subexpressions are false (or null). + * ExecQual + * + * Evaluates a conjunctive boolean expression and returns t + * iff none of the subexpressions are false (or null). * ---------------------------------------------------------------- */ bool -ExecQual(List *qual, ExprContext *econtext) +ExecQual(List * qual, ExprContext * econtext) { - List *clause; - bool result; - - /* ---------------- - * debugging stuff - * ---------------- - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - IncrProcessed(); - - /* ---------------- - * return true immediately if no qual - * ---------------- - */ - if (qual == NIL) - return true; - - /* ---------------- - * a "qual" is a list of clauses. To evaluate the - * qual, we evaluate each of the clauses in the list. - * - * ExecQualClause returns true when we know the qualification - * *failed* so we just pass each clause in qual to it until - * we know the qual failed or there are no more clauses. - * ---------------- - */ - result = false; - foreach (clause, qual) { - result = ExecQualClause((Node *)lfirst(clause), econtext); + List *clause; + bool result; + + /* ---------------- + * debugging stuff + * ---------------- + */ + EV_printf("ExecQual: qual is "); + EV_nodeDisplay(qual); + EV_printf("\n"); + + IncrProcessed(); + + /* ---------------- + * return true immediately if no qual + * ---------------- + */ + if (qual == NIL) + return true; + + /* ---------------- + * a "qual" is a list of clauses. To evaluate the + * qual, we evaluate each of the clauses in the list. + * + * ExecQualClause returns true when we know the qualification + * *failed* so we just pass each clause in qual to it until + * we know the qual failed or there are no more clauses. + * ---------------- + */ + result = false; + foreach(clause, qual) + { + result = ExecQualClause((Node *) lfirst(clause), econtext); + if (result == true) + break; + } + + /* ---------------- + * if result is true, then it means a clause failed so we + * return false. if result is false then it means no clause + * failed so we return true. + * ---------------- + */ if (result == true) - break; - } - - /* ---------------- - * if result is true, then it means a clause failed so we - * return false. if result is false then it means no clause - * failed so we return true. - * ---------------- - */ - if (result == true) - return false; - - return true; + return false; + + return true; } int -ExecTargetListLength(List *targetlist) +ExecTargetListLength(List * targetlist) { - int len; - List *tl; - TargetEntry *curTle; - - len = 0; - foreach (tl, targetlist) { - curTle = lfirst(tl); - - if (curTle->resdom != NULL) - len++; - else - len += curTle->fjoin->fj_nNodes; - } - return len; + int len; + List *tl; + TargetEntry *curTle; + + len = 0; + foreach(tl, targetlist) + { + curTle = lfirst(tl); + + if (curTle->resdom != NULL) + len++; + else + len += curTle->fjoin->fj_nNodes; + } + return len; } /* ---------------------------------------------------------------- - * ExecTargetList - * - * Evaluates a targetlist with respect to the current - * expression context and return a tuple. + * ExecTargetList + * + * Evaluates a targetlist with respect to the current + * expression context and return a tuple. * ---------------------------------------------------------------- */ -static HeapTuple -ExecTargetList(List *targetlist, - int nodomains, - TupleDesc targettype, - Datum *values, - ExprContext *econtext, - bool *isDone) +static HeapTuple +ExecTargetList(List * targetlist, + int nodomains, + TupleDesc targettype, + Datum * values, + ExprContext * econtext, + bool * isDone) { - char nulls_array[64]; - bool fjNullArray[64]; - bool *fjIsNull; - char *null_head; - List *tl; - TargetEntry *tle; - Node *expr; - Resdom *resdom; - AttrNumber resind; - Datum constvalue; - HeapTuple newTuple; - bool isNull; - - /* ---------------- - * debugging stuff - * ---------------- - */ - EV_printf("ExecTargetList: tl is "); - EV_nodeDisplay(targetlist); - EV_printf("\n"); - - /* ---------------- - * Return a dummy tuple if the targetlist is empty . - * the dummy tuple is necessary to differentiate - * between passing and failing the qualification. - * ---------------- - */ - if (targetlist == NIL) { + char nulls_array[64]; + bool fjNullArray[64]; + bool *fjIsNull; + char *null_head; + List *tl; + TargetEntry *tle; + Node *expr; + Resdom *resdom; + AttrNumber resind; + Datum constvalue; + HeapTuple newTuple; + bool isNull; + /* ---------------- - * I now think that the only time this makes - * any sence is when we run a delete query. Then - * we need to return something other than nil - * so we know to delete the tuple associated - * with the saved tupleid.. see what ExecutePlan - * does with the returned tuple.. -cim 9/21/89 - * - * It could also happen in queries like: - * retrieve (foo.all) where bar.a = 3 - * - * is this a new phenomenon? it might cause bogus behavior - * if we try to free this tuple later!! I put a hook in - * ExecProject to watch out for this case -mer 24 Aug 1992 + * debugging stuff * ---------------- */ - CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext); - *isDone = true; - return (HeapTuple) true; - } - - /* ---------------- - * allocate an array of char's to hold the "null" information - * only if we have a really large targetlist. otherwise we use - * the stack. - * ---------------- - */ - if (nodomains > 64) { - null_head = (char *) palloc(nodomains+1); - fjIsNull = (bool *) palloc(nodomains+1); - } else { - null_head = &nulls_array[0]; - fjIsNull = &fjNullArray[0]; - } - - /* ---------------- - * evaluate all the expressions in the target list - * ---------------- - */ - EV_printf("ExecTargetList: setting target list values\n"); - - *isDone = true; - foreach (tl, targetlist) { + EV_printf("ExecTargetList: tl is "); + EV_nodeDisplay(targetlist); + EV_printf("\n"); + /* ---------------- - * remember, a target list is a list of lists: - * - * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...) - * - * tl is a pointer to successive cdr's of the targetlist - * tle is a pointer to the target list entry in tl + * Return a dummy tuple if the targetlist is empty . + * the dummy tuple is necessary to differentiate + * between passing and failing the qualification. + * ---------------- + */ + if (targetlist == NIL) + { + /* ---------------- + * I now think that the only time this makes + * any sence is when we run a delete query. Then + * we need to return something other than nil + * so we know to delete the tuple associated + * with the saved tupleid.. see what ExecutePlan + * does with the returned tuple.. -cim 9/21/89 + * + * It could also happen in queries like: + * retrieve (foo.all) where bar.a = 3 + * + * is this a new phenomenon? it might cause bogus behavior + * if we try to free this tuple later!! I put a hook in + * ExecProject to watch out for this case -mer 24 Aug 1992 + * ---------------- + */ + CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext); + *isDone = true; + return (HeapTuple) true; + } + + /* ---------------- + * allocate an array of char's to hold the "null" information + * only if we have a really large targetlist. otherwise we use + * the stack. * ---------------- */ - tle = lfirst(tl); - - if (tle->resdom != NULL) { - expr = tle->expr; - resdom = tle->resdom; - resind = resdom->resno - 1; - constvalue = (Datum) ExecEvalExpr(expr, - econtext, - &isNull, - isDone); - - if ((IsA(expr,Iter)) && (*isDone)) - return (HeapTuple)NULL; - - values[resind] = constvalue; - - if (!isNull) - null_head[resind] = ' '; - else - null_head[resind] = 'n'; - }else { - int curNode; - Resdom *fjRes; - List *fjTlist = (List *)tle->expr; - Fjoin *fjNode = tle->fjoin; - int nNodes = fjNode->fj_nNodes; - DatumPtr results = fjNode->fj_results; - - ExecEvalFjoin(tle, econtext, fjIsNull, isDone); - if (*isDone) - return (HeapTuple)NULL; - - /* - * get the result from the inner node - */ - fjRes = (Resdom *)fjNode->fj_innerNode; - resind = fjRes->resno - 1; - if (fjIsNull[0]) - null_head[resind] = 'n'; - else { - null_head[resind] = ' '; - values[resind] = results[0]; - } - - /* - * Get results from all of the outer nodes - */ - for (curNode = 1; - curNode < nNodes; - curNode++, fjTlist = lnext(fjTlist)) + if (nodomains > 64) + { + null_head = (char *) palloc(nodomains + 1); + fjIsNull = (bool *) palloc(nodomains + 1); + } + else + { + null_head = &nulls_array[0]; + fjIsNull = &fjNullArray[0]; + } + + /* ---------------- + * evaluate all the expressions in the target list + * ---------------- + */ + EV_printf("ExecTargetList: setting target list values\n"); + + *isDone = true; + foreach(tl, targetlist) + { + /* ---------------- + * remember, a target list is a list of lists: + * + * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...) + * + * tl is a pointer to successive cdr's of the targetlist + * tle is a pointer to the target list entry in tl + * ---------------- + */ + tle = lfirst(tl); + + if (tle->resdom != NULL) + { + expr = tle->expr; + resdom = tle->resdom; + resind = resdom->resno - 1; + constvalue = (Datum) ExecEvalExpr(expr, + econtext, + &isNull, + isDone); + + if ((IsA(expr, Iter)) && (*isDone)) + return (HeapTuple) NULL; + + values[resind] = constvalue; + + if (!isNull) + null_head[resind] = ' '; + else + null_head[resind] = 'n'; + } + else { -#if 0 /* what is this?? */ - Node *outernode = lfirst(fjTlist); - fjRes = (Resdom *)outernode->iterexpr; -#endif - resind = fjRes->resno - 1; - if (fjIsNull[curNode]) { - null_head[resind] = 'n'; - }else { - null_head[resind] = ' '; - values[resind] = results[curNode]; - } + int curNode; + Resdom *fjRes; + List *fjTlist = (List *) tle->expr; + Fjoin *fjNode = tle->fjoin; + int nNodes = fjNode->fj_nNodes; + DatumPtr results = fjNode->fj_results; + + ExecEvalFjoin(tle, econtext, fjIsNull, isDone); + if (*isDone) + return (HeapTuple) NULL; + + /* + * get the result from the inner node + */ + fjRes = (Resdom *) fjNode->fj_innerNode; + resind = fjRes->resno - 1; + if (fjIsNull[0]) + null_head[resind] = 'n'; + else + { + null_head[resind] = ' '; + values[resind] = results[0]; + } + + /* + * Get results from all of the outer nodes + */ + for (curNode = 1; + curNode < nNodes; + curNode++, fjTlist = lnext(fjTlist)) + { +#if 0 /* what is this?? */ + Node *outernode = lfirst(fjTlist); + + fjRes = (Resdom *) outernode->iterexpr; +#endif + resind = fjRes->resno - 1; + if (fjIsNull[curNode]) + { + null_head[resind] = 'n'; + } + else + { + null_head[resind] = ' '; + values[resind] = results[curNode]; + } + } } } - } - - /* ---------------- - * form the new result tuple (in the "normal" context) - * ---------------- - */ - newTuple = (HeapTuple) - heap_formtuple(targettype, values, null_head); - - /* ---------------- - * free the nulls array if we allocated one.. - * ---------------- - */ - if (nodomains > 64) pfree(null_head); - - return - newTuple; + + /* ---------------- + * form the new result tuple (in the "normal" context) + * ---------------- + */ + newTuple = (HeapTuple) + heap_formtuple(targettype, values, null_head); + + /* ---------------- + * free the nulls array if we allocated one.. + * ---------------- + */ + if (nodomains > 64) + pfree(null_head); + + return + newTuple; } /* ---------------------------------------------------------------- - * ExecProject - * - * projects a tuple based in projection info and stores - * it in the specified tuple table slot. + * ExecProject + * + * projects a tuple based in projection info and stores + * it in the specified tuple table slot. * - * Note: someday soon the executor can be extended to eliminate - * redundant projections by storing pointers to datums - * in the tuple table and then passing these around when - * possible. this should make things much quicker. - * -cim 6/3/91 + * Note: someday soon the executor can be extended to eliminate + * redundant projections by storing pointers to datums + * in the tuple table and then passing these around when + * possible. this should make things much quicker. + * -cim 6/3/91 * ---------------------------------------------------------------- */ TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, bool *isDone) +ExecProject(ProjectionInfo * projInfo, bool * isDone) { - TupleTableSlot *slot; - List *targetlist; - int len; - TupleDesc tupType; - Datum *tupValue; - ExprContext *econtext; - HeapTuple newTuple; - - /* ---------------- - * sanity checks - * ---------------- - */ - if (projInfo == NULL) - return (TupleTableSlot *) NULL; - - /* ---------------- - * get the projection info we want - * ---------------- - */ - slot = projInfo->pi_slot; - targetlist = projInfo->pi_targetlist; - len = projInfo->pi_len; - tupType = slot->ttc_tupleDescriptor; - - tupValue = projInfo->pi_tupValue; - econtext = projInfo->pi_exprContext; - - if (targetlist == NIL) { - *isDone = true; - return (TupleTableSlot *) NULL; - } - - /* ---------------- - * form a new (result) tuple - * ---------------- - */ - newTuple = ExecTargetList(targetlist, - len, - tupType, - tupValue, - econtext, - isDone); - - /* ---------------- - * store the tuple in the projection slot and return the slot. - * - * If there's no projection target list we don't want to pfree - * the bogus tuple that ExecTargetList passes back to us. - * -mer 24 Aug 1992 - * ---------------- - */ - return (TupleTableSlot *) - ExecStoreTuple(newTuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* tuple has no buffer */ - true); -} + TupleTableSlot *slot; + List *targetlist; + int len; + TupleDesc tupType; + Datum *tupValue; + ExprContext *econtext; + HeapTuple newTuple; + + /* ---------------- + * sanity checks + * ---------------- + */ + if (projInfo == NULL) + return (TupleTableSlot *) NULL; + /* ---------------- + * get the projection info we want + * ---------------- + */ + slot = projInfo->pi_slot; + targetlist = projInfo->pi_targetlist; + len = projInfo->pi_len; + tupType = slot->ttc_tupleDescriptor; + + tupValue = projInfo->pi_tupValue; + econtext = projInfo->pi_exprContext; + + if (targetlist == NIL) + { + *isDone = true; + return (TupleTableSlot *) NULL; + } + + /* ---------------- + * form a new (result) tuple + * ---------------- + */ + newTuple = ExecTargetList(targetlist, + len, + tupType, + tupValue, + econtext, + isDone); + + /* ---------------- + * store the tuple in the projection slot and return the slot. + * + * If there's no projection target list we don't want to pfree + * the bogus tuple that ExecTargetList passes back to us. + * -mer 24 Aug 1992 + * ---------------- + */ + return (TupleTableSlot *) + ExecStoreTuple(newTuple,/* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* tuple has no buffer */ + true); +} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 8e69f491731..6ea50bb2a93 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * execScan.c-- - * This code provides support for generalized relation scans. ExecScan - * is passed a node and a pointer to a function to "do the right thing" - * and return a tuple from the relation. ExecScan then does the tedious - * stuff - checking the qualification and projecting the tuple - * appropriately. + * This code provides support for generalized relation scans. ExecScan + * is passed a node and a pointer to a function to "do the right thing" + * and return a tuple from the relation. ExecScan then does the tedious + * stuff - checking the qualification and projecting the tuple + * appropriately. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.3 1997/07/28 00:53:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.4 1997/09/07 04:41:23 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,117 +23,123 @@ #include "executor/executor.h" /* ---------------------------------------------------------------- - * ExecScan - * - * Scans the relation using the 'access method' indicated and - * returns the next qualifying tuple in the direction specified - * in the global variable ExecDirection. - * The access method returns the next tuple and execScan() is - * responisble for checking the tuple returned against the qual-clause. - * - * Conditions: - * -- the "cursor" maintained by the AMI is positioned at the tuple - * returned previously. - * - * Initial States: - * -- the relation indicated is opened for scanning so that the - * "cursor" is positioned before the first qualifying tuple. + * ExecScan * - * May need to put startmmgr and endmmgr in here. + * Scans the relation using the 'access method' indicated and + * returns the next qualifying tuple in the direction specified + * in the global variable ExecDirection. + * The access method returns the next tuple and execScan() is + * responisble for checking the tuple returned against the qual-clause. + * + * Conditions: + * -- the "cursor" maintained by the AMI is positioned at the tuple + * returned previously. + * + * Initial States: + * -- the relation indicated is opened for scanning so that the + * "cursor" is positioned before the first qualifying tuple. + * + * May need to put startmmgr and endmmgr in here. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecScan(Scan *node, - TupleTableSlot* (*accessMtd)()) /* function returning a tuple */ +ExecScan(Scan * node, + TupleTableSlot * (*accessMtd) ()) /* function returning a + * tuple */ { - CommonScanState *scanstate; - EState *estate; - List *qual; - bool isDone; - - TupleTableSlot *slot; - TupleTableSlot *resultSlot; - HeapTuple newTuple; - - ExprContext *econtext; - ProjectionInfo *projInfo; - - - /* ---------------- - * initialize misc variables - * ---------------- - */ - newTuple = NULL; - slot = NULL; - - estate = node->plan.state; - scanstate = node->scanstate; - - /* ---------------- - * get the expression context - * ---------------- - */ - econtext = scanstate->cstate.cs_ExprContext; - - /* ---------------- - * initialize fields in ExprContext which don't change - * in the course of the scan.. - * ---------------- - */ - qual = node->plan.qual; - econtext->ecxt_relation = scanstate->css_currentRelation; - econtext->ecxt_relid = node->scanrelid; - - if (scanstate->cstate.cs_TupFromTlist) { - projInfo = scanstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - if (!isDone) - return resultSlot; - } - /* - * get a tuple from the access method - * loop until we obtain a tuple which passes the qualification. - */ - for(;;) { - slot = (TupleTableSlot *) (*accessMtd)(node); + CommonScanState *scanstate; + EState *estate; + List *qual; + bool isDone; + + TupleTableSlot *slot; + TupleTableSlot *resultSlot; + HeapTuple newTuple; + + ExprContext *econtext; + ProjectionInfo *projInfo; + /* ---------------- - * if the slot returned by the accessMtd contains - * NULL, then it means there is nothing more to scan - * so we just return the empty slot. + * initialize misc variables * ---------------- */ - if (TupIsNull(slot)) return slot; - + newTuple = NULL; + slot = NULL; + + estate = node->plan.state; + scanstate = node->scanstate; + /* ---------------- - * place the current tuple into the expr context + * get the expression context * ---------------- */ - econtext->ecxt_scantuple = slot; - + econtext = scanstate->cstate.cs_ExprContext; + /* ---------------- - * check that the current tuple satisfies the qual-clause - * if our qualification succeeds then we - * leave the loop. + * initialize fields in ExprContext which don't change + * in the course of the scan.. * ---------------- */ + qual = node->plan.qual; + econtext->ecxt_relation = scanstate->css_currentRelation; + econtext->ecxt_relid = node->scanrelid; + + if (scanstate->cstate.cs_TupFromTlist) + { + projInfo = scanstate->cstate.cs_ProjInfo; + resultSlot = ExecProject(projInfo, &isDone); + if (!isDone) + return resultSlot; + } + + /* + * get a tuple from the access method loop until we obtain a tuple + * which passes the qualification. + */ + for (;;) + { + slot = (TupleTableSlot *) (*accessMtd) (node); + + /* ---------------- + * if the slot returned by the accessMtd contains + * NULL, then it means there is nothing more to scan + * so we just return the empty slot. + * ---------------- + */ + if (TupIsNull(slot)) + return slot; + + /* ---------------- + * place the current tuple into the expr context + * ---------------- + */ + econtext->ecxt_scantuple = slot; + + /* ---------------- + * check that the current tuple satisfies the qual-clause + * if our qualification succeeds then we + * leave the loop. + * ---------------- + */ + + /* + * add a check for non-nil qual here to avoid a function call to + * ExecQual() when the qual is nil + */ + if (!qual || ExecQual(qual, econtext) == true) + break; + } - /* add a check for non-nil qual here to avoid a - function call to ExecQual() when the qual is nil */ - if (!qual || ExecQual(qual, econtext) == true) - break; - } - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - projInfo = scanstate->cstate.cs_ProjInfo; + /* ---------------- + * form a projection tuple, store it in the result tuple + * slot and return it. + * ---------------- + */ + projInfo = scanstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - scanstate->cstate.cs_TupFromTlist = !isDone; + resultSlot = ExecProject(projInfo, &isDone); + scanstate->cstate.cs_TupFromTlist = !isDone; - return resultSlot; + return resultSlot; } - diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 0d5e7fda9fb..287f75699af 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1,119 +1,119 @@ /*------------------------------------------------------------------------- * * execTuples.c-- - * Routines dealing with the executor tuple tables. These are used to - * ensure that the executor frees copies of tuples (made by - * ExecTargetList) properly. + * Routines dealing with the executor tuple tables. These are used to + * ensure that the executor frees copies of tuples (made by + * ExecTargetList) properly. + * + * Routines dealing with the type information for tuples. Currently, + * the type information for a tuple is an array of FormData_pg_attribute. + * This information is needed by routines manipulating tuples + * (getattribute, formtuple, etc.). * - * Routines dealing with the type information for tuples. Currently, - * the type information for a tuple is an array of FormData_pg_attribute. - * This information is needed by routines manipulating tuples - * (getattribute, formtuple, etc.). - * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.6 1997/08/19 21:31:05 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.7 1997/09/07 04:41:24 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * - * TABLE CREATE/DELETE - * ExecCreateTupleTable - create a new tuple table - * ExecDestroyTupleTable - destroy a table + * TABLE CREATE/DELETE + * ExecCreateTupleTable - create a new tuple table + * ExecDestroyTupleTable - destroy a table * - * SLOT RESERVERATION - * ExecAllocTableSlot - find an available slot in the table + * SLOT RESERVERATION + * ExecAllocTableSlot - find an available slot in the table * - * SLOT ACCESSORS - * ExecStoreTuple - store a tuple in the table - * ExecFetchTuple - fetch a tuple from the table - * ExecClearTuple - clear contents of a table slot - * ExecSlotPolicy - return slot's tuple pfree policy - * ExecSetSlotPolicy - diddle the slot policy - * ExecSlotDescriptor - type of tuple in a slot - * ExecSetSlotDescriptor - set a slot's tuple descriptor - * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag - * ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once - * ExecSlotBuffer - return buffer of tuple in slot - * ExecSetSlotBuffer - set the buffer for tuple in slot - * ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer + * SLOT ACCESSORS + * ExecStoreTuple - store a tuple in the table + * ExecFetchTuple - fetch a tuple from the table + * ExecClearTuple - clear contents of a table slot + * ExecSlotPolicy - return slot's tuple pfree policy + * ExecSetSlotPolicy - diddle the slot policy + * ExecSlotDescriptor - type of tuple in a slot + * ExecSetSlotDescriptor - set a slot's tuple descriptor + * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag + * ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once + * ExecSlotBuffer - return buffer of tuple in slot + * ExecSetSlotBuffer - set the buffer for tuple in slot + * ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer * - * SLOT STATUS PREDICATES - * TupIsNull - true when slot contains no tuple - * ExecSlotDescriptorIsNew - true if we're now storing a different - * type of tuple in a slot + * SLOT STATUS PREDICATES + * TupIsNull - true when slot contains no tuple + * ExecSlotDescriptorIsNew - true if we're now storing a different + * type of tuple in a slot * - * CONVENIENCE INITIALIZATION ROUTINES - * ExecInitResultTupleSlot \ convience routines to initialize - * ExecInitScanTupleSlot \ the various tuple slots for nodes - * ExecInitMarkedTupleSlot / which store copies of tuples. - * ExecInitOuterTupleSlot / - * ExecInitHashTupleSlot / + * CONVENIENCE INITIALIZATION ROUTINES + * ExecInitResultTupleSlot \ convience routines to initialize + * ExecInitScanTupleSlot \ the various tuple slots for nodes + * ExecInitMarkedTupleSlot / which store copies of tuples. + * ExecInitOuterTupleSlot / + * ExecInitHashTupleSlot / * - * old routines: - * ExecGetTupType - get type of tuple returned by this node - * ExecTypeFromTL - form a TupleDesc from a target list + * old routines: + * ExecGetTupType - get type of tuple returned by this node + * ExecTypeFromTL - form a TupleDesc from a target list * - * EXAMPLE OF HOW TABLE ROUTINES WORK - * Suppose we have a query such as retrieve (EMP.name) and we have - * a single SeqScan node in the query plan. + * EXAMPLE OF HOW TABLE ROUTINES WORK + * Suppose we have a query such as retrieve (EMP.name) and we have + * a single SeqScan node in the query plan. * - * At ExecStart() - * ---------------- - * - InitPlan() calls ExecCreateTupleTable() to create the tuple - * table which will hold tuples processed by the executor. + * At ExecStart() + * ---------------- + * - InitPlan() calls ExecCreateTupleTable() to create the tuple + * table which will hold tuples processed by the executor. * - * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and - * ExecInitResultTupleSlot() to reserve places in the tuple - * table for the tuples returned by the access methods and the - * tuples resulting from preforming target list projections. + * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and + * ExecInitResultTupleSlot() to reserve places in the tuple + * table for the tuples returned by the access methods and the + * tuples resulting from preforming target list projections. * - * During ExecRun() - * ---------------- - * - SeqNext() calls ExecStoreTuple() to place the tuple returned - * by the access methods into the scan tuple slot. + * During ExecRun() + * ---------------- + * - SeqNext() calls ExecStoreTuple() to place the tuple returned + * by the access methods into the scan tuple slot. * - * - ExecSeqScan() calls ExecStoreTuple() to take the result - * tuple from ExecTargetList() and place it into the result tuple - * slot. + * - ExecSeqScan() calls ExecStoreTuple() to take the result + * tuple from ExecTargetList() and place it into the result tuple + * slot. * - * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of - * the slot passed to it by calling ExecFetchTuple(). this tuple - * is then returned. + * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of + * the slot passed to it by calling ExecFetchTuple(). this tuple + * is then returned. * - * At ExecEnd() - * ---------------- - * - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining - * tuples left over from executing the query. + * At ExecEnd() + * ---------------- + * - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining + * tuples left over from executing the query. * - * The important thing to watch in the executor code is how pointers - * to the slots containing tuples are passed instead of the tuples - * themselves. This facilitates the communication of related information - * (such as whether or not a tuple should be pfreed, what buffer contains - * this tuple, the tuple's tuple descriptor, etc). Note that much of - * this information is also kept in the ExprContext of each node. - * Soon the executor will be redesigned and ExprContext's will contain - * only slot pointers. -cim 3/14/91 + * The important thing to watch in the executor code is how pointers + * to the slots containing tuples are passed instead of the tuples + * themselves. This facilitates the communication of related information + * (such as whether or not a tuple should be pfreed, what buffer contains + * this tuple, the tuple's tuple descriptor, etc). Note that much of + * this information is also kept in the ExprContext of each node. + * Soon the executor will be redesigned and ExprContext's will contain + * only slot pointers. -cim 3/14/91 * - * NOTES - * The tuple table stuff is relatively new, put here to alleviate - * the process growth problems in the executor. The other routines - * are old (from the original lisp system) and may someday become - * obsolete. -cim 6/23/90 + * NOTES + * The tuple table stuff is relatively new, put here to alleviate + * the process growth problems in the executor. The other routines + * are old (from the original lisp system) and may someday become + * obsolete. -cim 6/23/90 * - * In the implementation of nested-dot queries such as - * "retrieve (EMP.hobbies.all)", a single scan may return tuples - * of many types, so now we return pointers to tuple descriptors - * along with tuples returned via the tuple table. This means - * we now have a bunch of routines to diddle the slot descriptors - * too. -cim 1/18/90 + * In the implementation of nested-dot queries such as + * "retrieve (EMP.hobbies.all)", a single scan may return tuples + * of many types, so now we return pointers to tuple descriptors + * along with tuples returned via the tuple table. This means + * we now have a bunch of routines to diddle the slot descriptors + * too. -cim 1/18/90 * - * The tuple table stuff depends on the executor/tuptable.h macros, - * and the TupleTableSlot node in execnodes.h. + * The tuple table stuff depends on the executor/tuptable.h macros, + * and the TupleTableSlot node in execnodes.h. * */ #include <string.h> @@ -131,902 +131,938 @@ #include "parser/catalog_utils.h" #include "catalog/pg_type.h" -static TupleTableSlot *NodeGetResultTupleSlot(Plan *node); +static TupleTableSlot *NodeGetResultTupleSlot(Plan * node); /* ---------------------------------------------------------------- - * tuple table create/delete functions + * tuple table create/delete functions * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecCreateTupleTable + * ExecCreateTupleTable * - * This creates a new tuple table of the specified initial - * size. If the size is insufficient, ExecAllocTableSlot() - * will grow the table as necessary. + * This creates a new tuple table of the specified initial + * size. If the size is insufficient, ExecAllocTableSlot() + * will grow the table as necessary. * - * This should be used by InitPlan() to allocate the table. - * The table's address will be stored in the EState structure. + * This should be used by InitPlan() to allocate the table. + * The table's address will be stored in the EState structure. * -------------------------------- */ -TupleTable /* return: address of table */ -ExecCreateTupleTable(int initialSize) /* initial number of slots in table */ +TupleTable /* return: address of table */ +ExecCreateTupleTable(int initialSize) /* initial number of slots + * in table */ { - TupleTable newtable; /* newly allocated table */ - TupleTableSlot* array; /* newly allocated slot array */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(initialSize >= 1); - - /* ---------------- - * Now allocate our new table along with space for the pointers - * to the tuples. - */ - - newtable = (TupleTable) palloc(sizeof(TupleTableData)); - array = (TupleTableSlot*) palloc(initialSize * sizeof(TupleTableSlot)); - - /* ---------------- - * clean out the slots we just allocated - * ---------------- - */ - memset(array, 0, initialSize * sizeof(TupleTableSlot)); - - /* ---------------- - * initialize the new table and return it to the caller. - * ---------------- - */ - newtable->size = initialSize; - newtable->next = 0; - newtable->array = array; - - return newtable; + TupleTable newtable; /* newly allocated table */ + TupleTableSlot *array; /* newly allocated slot array */ + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(initialSize >= 1); + + /* ---------------- + * Now allocate our new table along with space for the pointers + * to the tuples. + */ + + newtable = (TupleTable) palloc(sizeof(TupleTableData)); + array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot)); + + /* ---------------- + * clean out the slots we just allocated + * ---------------- + */ + memset(array, 0, initialSize * sizeof(TupleTableSlot)); + + /* ---------------- + * initialize the new table and return it to the caller. + * ---------------- + */ + newtable->size = initialSize; + newtable->next = 0; + newtable->array = array; + + return newtable; } /* -------------------------------- - * ExecDestroyTupleTable + * ExecDestroyTupleTable * - * This pfrees the storage assigned to the tuple table and - * optionally pfrees the contents of the table also. - * It is expected that this routine be called by EndPlan(). + * This pfrees the storage assigned to the tuple table and + * optionally pfrees the contents of the table also. + * It is expected that this routine be called by EndPlan(). * -------------------------------- */ void -ExecDestroyTupleTable(TupleTable table, /* tuple table */ - bool shouldFree) /* true if we should free slot contents */ +ExecDestroyTupleTable(TupleTable table, /* tuple table */ + bool shouldFree) /* true if we should free slot + * contents */ { - int next; /* next avaliable slot */ - TupleTableSlot *array; /* start of table array */ - int i; /* counter */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(table != NULL); - - /* ---------------- - * get information from the table - * ---------------- - */ - array = table->array; - next = table->next; - - /* ---------------- - * first free all the valid pointers in the tuple array - * if that's what the caller wants.. - * - * Note: we do nothing about the Buffer and Tuple Descriptor's - * we store in the slots. This may have to change (ex: we should - * probably worry about pfreeing tuple descs too) -cim 3/14/91 - * ---------------- - */ - if (shouldFree) - for (i = 0; i < next; i++) { - TupleTableSlot slot; - HeapTuple tuple; - - slot = array[i]; - tuple = slot.val; - - if (tuple != NULL) { - slot.val = (HeapTuple)NULL; - if (slot.ttc_shouldFree) { - /* ---------------- - * since a tuple may contain a pointer to - * lock information allocated along with the - * tuple, we have to be careful to free any - * rule locks also -cim 1/17/90 - * ---------------- - */ - pfree(tuple); + int next; /* next avaliable slot */ + TupleTableSlot *array; /* start of table array */ + int i; /* counter */ + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(table != NULL); + + /* ---------------- + * get information from the table + * ---------------- + */ + array = table->array; + next = table->next; + + /* ---------------- + * first free all the valid pointers in the tuple array + * if that's what the caller wants.. + * + * Note: we do nothing about the Buffer and Tuple Descriptor's + * we store in the slots. This may have to change (ex: we should + * probably worry about pfreeing tuple descs too) -cim 3/14/91 + * ---------------- + */ + if (shouldFree) + for (i = 0; i < next; i++) + { + TupleTableSlot slot; + HeapTuple tuple; + + slot = array[i]; + tuple = slot.val; + + if (tuple != NULL) + { + slot.val = (HeapTuple) NULL; + if (slot.ttc_shouldFree) + { + /* ---------------- + * since a tuple may contain a pointer to + * lock information allocated along with the + * tuple, we have to be careful to free any + * rule locks also -cim 1/17/90 + * ---------------- + */ + pfree(tuple); + } + } } - } - } - - /* ---------------- - * finally free the tuple array and the table itself. - * ---------------- - */ - pfree(array); - pfree(table); - + + /* ---------------- + * finally free the tuple array and the table itself. + * ---------------- + */ + pfree(array); + pfree(table); + } /* ---------------------------------------------------------------- - * tuple table slot reservation functions + * tuple table slot reservation functions * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecAllocTableSlot + * ExecAllocTableSlot * - * This routine is used to reserve slots in the table for - * use by the various plan nodes. It is expected to be - * called by the node init routines (ex: ExecInitNestLoop). - * once per slot needed by the node. Not all nodes need - * slots (some just pass tuples around). + * This routine is used to reserve slots in the table for + * use by the various plan nodes. It is expected to be + * called by the node init routines (ex: ExecInitNestLoop). + * once per slot needed by the node. Not all nodes need + * slots (some just pass tuples around). * -------------------------------- */ -TupleTableSlot* /* return: the slot allocated in the tuple table */ +TupleTableSlot * /* return: the slot allocated in the tuple + * table */ ExecAllocTableSlot(TupleTable table) { - int slotnum; /* new slot number */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(table != NULL); - - /* ---------------- - * if our table is full we have to allocate a larger - * size table. Since ExecAllocTableSlot() is only called - * before the table is ever used to store tuples, we don't - * have to worry about the contents of the old table. - * If this changes, then we will have to preserve the contents. - * -cim 6/23/90 - * - * Unfortunately, we *cannot* do this. All of the nodes in - * the plan that have already initialized their slots will have - * pointers into _freed_ memory. This leads to bad ends. We - * now count the number of slots we will need and create all the - * slots we will need ahead of time. The if below should never - * happen now. Give a WARN if it does. -mer 4 Aug 1992 - * ---------------- - */ - if (table->next >= table->size) { - /* - * int newsize = NewTableSize(table->size); + int slotnum; /* new slot number */ + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(table != NULL); + + /* ---------------- + * if our table is full we have to allocate a larger + * size table. Since ExecAllocTableSlot() is only called + * before the table is ever used to store tuples, we don't + * have to worry about the contents of the old table. + * If this changes, then we will have to preserve the contents. + * -cim 6/23/90 * - * pfree(table->array); - * table->array = (Pointer) palloc(newsize * TableSlotSize); - * bzero(table->array, newsize * TableSlotSize); - * table->size = newsize; + * Unfortunately, we *cannot* do this. All of the nodes in + * the plan that have already initialized their slots will have + * pointers into _freed_ memory. This leads to bad ends. We + * now count the number of slots we will need and create all the + * slots we will need ahead of time. The if below should never + * happen now. Give a WARN if it does. -mer 4 Aug 1992 + * ---------------- */ - elog(NOTICE, "Plan requires more slots than are available"); - elog(WARN, "send mail to your local executor guru to fix this"); - } - - /* ---------------- - * at this point, space in the table is guaranteed so we - * reserve the next slot, initialize and return it. - * ---------------- - */ - slotnum = table->next; - table->next++; - - table->array[slotnum].type = T_TupleTableSlot; - - return &(table->array[slotnum]); + if (table->next >= table->size) + { + + /* + * int newsize = NewTableSize(table->size); + * + * pfree(table->array); table->array = (Pointer) palloc(newsize * + * TableSlotSize); bzero(table->array, newsize * TableSlotSize); + * table->size = newsize; + */ + elog(NOTICE, "Plan requires more slots than are available"); + elog(WARN, "send mail to your local executor guru to fix this"); + } + + /* ---------------- + * at this point, space in the table is guaranteed so we + * reserve the next slot, initialize and return it. + * ---------------- + */ + slotnum = table->next; + table->next++; + + table->array[slotnum].type = T_TupleTableSlot; + + return &(table->array[slotnum]); } /* ---------------------------------------------------------------- - * tuple table slot accessor functions + * tuple table slot accessor functions * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecStoreTuple + * ExecStoreTuple * - * This function is used to store a tuple into a specified - * slot in the tuple table. Note: the only slots which should - * be called with shouldFree == false are those slots used to - * store tuples not allocated with pfree(). Currently the - * seqscan and indexscan nodes use this for the tuples returned - * by amgetattr, which are actually pointers onto disk pages. + * This function is used to store a tuple into a specified + * slot in the tuple table. Note: the only slots which should + * be called with shouldFree == false are those slots used to + * store tuples not allocated with pfree(). Currently the + * seqscan and indexscan nodes use this for the tuples returned + * by amgetattr, which are actually pointers onto disk pages. * -------------------------------- */ -TupleTableSlot* /* return: slot passed */ -ExecStoreTuple(HeapTuple tuple, /* tuple to store */ - TupleTableSlot* slot, /* slot in which to store tuple */ - Buffer buffer, /* buffer associated with tuple */ - bool shouldFree) /* true if we call pfree() when we gc. */ +TupleTableSlot * /* return: slot passed */ +ExecStoreTuple(HeapTuple tuple, /* tuple to store */ + TupleTableSlot * slot, /* slot in which to store tuple */ + Buffer buffer, /* buffer associated with tuple */ + bool shouldFree) /* true if we call pfree() when we gc. */ { - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(slot != NULL); - - /* clear out the slot first */ - ExecClearTuple(slot); - - /* ---------------- - * store the new tuple into the specified slot and - * return the slot into which we stored the tuple. - * ---------------- - */ - slot->val = tuple; - slot->ttc_buffer = buffer; - slot->ttc_shouldFree = shouldFree; - - return slot; + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(slot != NULL); + + /* clear out the slot first */ + ExecClearTuple(slot); + + /* ---------------- + * store the new tuple into the specified slot and + * return the slot into which we stored the tuple. + * ---------------- + */ + slot->val = tuple; + slot->ttc_buffer = buffer; + slot->ttc_shouldFree = shouldFree; + + return slot; } /* -------------------------------- - * ExecClearTuple + * ExecClearTuple * - * This function is used to clear out a slot in the tuple table. + * This function is used to clear out a slot in the tuple table. * -------------------------------- */ -TupleTableSlot* /* return: slot passed */ -ExecClearTuple(TupleTableSlot* slot) /* slot in which to store tuple */ +TupleTableSlot * /* return: slot passed */ +ExecClearTuple(TupleTableSlot * slot) /* slot in which to store tuple */ { - HeapTuple oldtuple; /* prior contents of slot */ - - /* ---------------- - * sanity checks - * ---------------- - */ - Assert(slot != NULL); - - /* ---------------- - * get information from the tuple table - * ---------------- - */ - oldtuple = slot->val; - - /* ---------------- - * free the old contents of the specified slot if necessary. - * ---------------- - */ - if (slot->ttc_shouldFree && oldtuple != NULL) { + HeapTuple oldtuple; /* prior contents of slot */ + /* ---------------- - * since a tuple may contain a pointer to - * lock information allocated along with the - * tuple, we have to be careful to free any - * rule locks also -cim 1/17/90 + * sanity checks * ---------------- */ - pfree(oldtuple); - } - - /* ---------------- - * store NULL into the specified slot and return the slot. - * - also set buffer to InvalidBuffer -cim 3/14/91 - * ---------------- - */ - slot->val = (HeapTuple)NULL; - - if (BufferIsValid(slot->ttc_buffer)) - ReleaseBuffer(slot->ttc_buffer); - - slot->ttc_buffer = InvalidBuffer; - slot->ttc_shouldFree = true; - - return slot; + Assert(slot != NULL); + + /* ---------------- + * get information from the tuple table + * ---------------- + */ + oldtuple = slot->val; + + /* ---------------- + * free the old contents of the specified slot if necessary. + * ---------------- + */ + if (slot->ttc_shouldFree && oldtuple != NULL) + { + /* ---------------- + * since a tuple may contain a pointer to + * lock information allocated along with the + * tuple, we have to be careful to free any + * rule locks also -cim 1/17/90 + * ---------------- + */ + pfree(oldtuple); + } + + /* ---------------- + * store NULL into the specified slot and return the slot. + * - also set buffer to InvalidBuffer -cim 3/14/91 + * ---------------- + */ + slot->val = (HeapTuple) NULL; + + if (BufferIsValid(slot->ttc_buffer)) + ReleaseBuffer(slot->ttc_buffer); + + slot->ttc_buffer = InvalidBuffer; + slot->ttc_shouldFree = true; + + return slot; } /* -------------------------------- - * ExecSlotPolicy + * ExecSlotPolicy * - * This function is used to get the call/don't call pfree - * setting of a slot. Most executor routines don't need this. - * It's only when you do tricky things like marking tuples for - * merge joins that you need to diddle the slot policy. + * This function is used to get the call/don't call pfree + * setting of a slot. Most executor routines don't need this. + * It's only when you do tricky things like marking tuples for + * merge joins that you need to diddle the slot policy. * -------------------------------- */ #ifdef NOT_USED -bool /* return: slot policy */ -ExecSlotPolicy(TupleTableSlot* slot) /* slot to inspect */ +bool /* return: slot policy */ +ExecSlotPolicy(TupleTableSlot * slot) /* slot to inspect */ { - return slot->ttc_shouldFree; + return slot->ttc_shouldFree; } + #endif /* -------------------------------- - * ExecSetSlotPolicy + * ExecSetSlotPolicy * - * This function is used to change the call/don't call pfree - * setting of a slot. Most executor routines don't need this. - * It's only when you do tricky things like marking tuples for - * merge joins that you need to diddle the slot policy. + * This function is used to change the call/don't call pfree + * setting of a slot. Most executor routines don't need this. + * It's only when you do tricky things like marking tuples for + * merge joins that you need to diddle the slot policy. * -------------------------------- */ -bool /* return: old slot policy */ -ExecSetSlotPolicy(TupleTableSlot* slot, /* slot to change */ - bool shouldFree) /* true if we call pfree() when we gc. */ +bool /* return: old slot policy */ +ExecSetSlotPolicy(TupleTableSlot * slot, /* slot to change */ + bool shouldFree) /* true if we call pfree() when we + * gc. */ { - bool old_shouldFree = slot->ttc_shouldFree; - slot->ttc_shouldFree = shouldFree; + bool old_shouldFree = slot->ttc_shouldFree; - return old_shouldFree; + slot->ttc_shouldFree = shouldFree; + + return old_shouldFree; } /* -------------------------------- - * ExecSlotDescriptor + * ExecSlotDescriptor * - * This function is used to get the tuple descriptor associated - * with the slot's tuple. + * This function is used to get the tuple descriptor associated + * with the slot's tuple. * * Now a macro in tuptable.h -mer 5 March 1992 * -------------------------------- */ /* -------------------------------- - * ExecSetSlotDescriptor + * ExecSetSlotDescriptor * - * This function is used to set the tuple descriptor associated - * with the slot's tuple. + * This function is used to set the tuple descriptor associated + * with the slot's tuple. * -------------------------------- */ -TupleDesc /* return: old slot tuple descriptor */ -ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ - TupleDesc tupdesc) /* tuple descriptor */ +TupleDesc /* return: old slot tuple descriptor */ +ExecSetSlotDescriptor(TupleTableSlot * slot, /* slot to change */ + TupleDesc tupdesc) /* tuple descriptor */ { - TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; + TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; - slot->ttc_tupleDescriptor = tupdesc; - return old_tupdesc; + slot->ttc_tupleDescriptor = tupdesc; + return old_tupdesc; } /* -------------------------------- - * ExecSetSlotDescriptorIsNew + * ExecSetSlotDescriptorIsNew * - * This function is used to change the setting of the "isNew" flag + * This function is used to change the setting of the "isNew" flag * -------------------------------- */ void -ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,/* slot to change */ - bool isNew) /* "isNew" setting */ +ExecSetSlotDescriptorIsNew(TupleTableSlot * slot, /* slot to change */ + bool isNew) /* "isNew" setting */ { - slot->ttc_descIsNew = isNew; + slot->ttc_descIsNew = isNew; } /* -------------------------------- - * ExecSetNewSlotDescriptor + * ExecSetNewSlotDescriptor * - * This function is used to set the tuple descriptor associated - * with the slot's tuple, and set the "isNew" flag at the same time. + * This function is used to set the tuple descriptor associated + * with the slot's tuple, and set the "isNew" flag at the same time. * -------------------------------- */ #ifdef NOT_USED -TupleDesc /* return: old slot tuple descriptor */ -ExecSetNewSlotDescriptor(TupleTableSlot *slot, /* slot to change */ - TupleDesc tupdesc) /* tuple descriptor */ +TupleDesc /* return: old slot tuple descriptor */ +ExecSetNewSlotDescriptor(TupleTableSlot * slot, /* slot to change */ + TupleDesc tupdesc) /* tuple descriptor */ { - TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; - slot->ttc_tupleDescriptor = tupdesc; - slot->ttc_descIsNew = true; - - return old_tupdesc; + TupleDesc old_tupdesc = slot->ttc_tupleDescriptor; + + slot->ttc_tupleDescriptor = tupdesc; + slot->ttc_descIsNew = true; + + return old_tupdesc; } + #endif /* -------------------------------- - * ExecSlotBuffer + * ExecSlotBuffer * - * This function is used to get the tuple descriptor associated - * with the slot's tuple. Be very careful with this as it does not - * balance the reference counts. If the buffer returned is stored - * someplace else, then also use ExecIncrSlotBufferRefcnt(). + * This function is used to get the tuple descriptor associated + * with the slot's tuple. Be very careful with this as it does not + * balance the reference counts. If the buffer returned is stored + * someplace else, then also use ExecIncrSlotBufferRefcnt(). * * Now a macro in tuptable.h * -------------------------------- */ /* -------------------------------- - * ExecSetSlotBuffer + * ExecSetSlotBuffer * - * This function is used to set the tuple descriptor associated - * with the slot's tuple. Be very careful with this as it does not - * balance the reference counts. If we're using this then we should - * also use ExecIncrSlotBufferRefcnt(). + * This function is used to set the tuple descriptor associated + * with the slot's tuple. Be very careful with this as it does not + * balance the reference counts. If we're using this then we should + * also use ExecIncrSlotBufferRefcnt(). * -------------------------------- */ #ifdef NOT_USED -Buffer /* return: old slot buffer */ -ExecSetSlotBuffer(TupleTableSlot *slot, /* slot to change */ - Buffer b) /* tuple descriptor */ +Buffer /* return: old slot buffer */ +ExecSetSlotBuffer(TupleTableSlot * slot, /* slot to change */ + Buffer b) /* tuple descriptor */ { - Buffer oldb = slot->ttc_buffer; - slot->ttc_buffer = b; - - return oldb; + Buffer oldb = slot->ttc_buffer; + + slot->ttc_buffer = b; + + return oldb; } + #endif /* -------------------------------- - * ExecIncrSlotBufferRefcnt + * ExecIncrSlotBufferRefcnt * - * When we pass around buffers in the tuple table, we have to - * be careful to increment reference counts appropriately. - * This is used mainly in the mergejoin code. + * When we pass around buffers in the tuple table, we have to + * be careful to increment reference counts appropriately. + * This is used mainly in the mergejoin code. * -------------------------------- */ void -ExecIncrSlotBufferRefcnt(TupleTableSlot *slot) /* slot to bump refcnt */ +ExecIncrSlotBufferRefcnt(TupleTableSlot * slot) /* slot to bump refcnt */ { -/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */ - Buffer b = slot->ttc_buffer; - if (BufferIsValid(b)) - IncrBufferRefCount(b); +/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */ + Buffer b = slot->ttc_buffer; + + if (BufferIsValid(b)) + IncrBufferRefCount(b); } /* ---------------------------------------------------------------- - * tuple table slot status predicates + * tuple table slot status predicates * ---------------------------------------------------------------- */ /* ---------------- - * TupIsNull + * TupIsNull * - * This is used mainly to detect when there are no more - * tuples to process. + * This is used mainly to detect when there are no more + * tuples to process. * ---------------- */ -bool /* return: true if tuple in slot is NULL */ -TupIsNull(TupleTableSlot* slot) /* slot to check */ +bool /* return: true if tuple in slot is NULL */ +TupIsNull(TupleTableSlot * slot) /* slot to check */ { - HeapTuple tuple; /* contents of slot (returned) */ - - /* ---------------- - * if the slot itself is null then we return true - * ---------------- - */ - if (slot == NULL) - return true; - - /* ---------------- - * get information from the slot and return true or - * false depending on the contents of the slot. - * ---------------- - */ - tuple = slot->val; - - return - (tuple == NULL ? true : false); + HeapTuple tuple; /* contents of slot (returned) */ + + /* ---------------- + * if the slot itself is null then we return true + * ---------------- + */ + if (slot == NULL) + return true; + + /* ---------------- + * get information from the slot and return true or + * false depending on the contents of the slot. + * ---------------- + */ + tuple = slot->val; + + return + (tuple == NULL ? true : false); } /* -------------------------------- - * ExecSlotDescriptorIsNew + * ExecSlotDescriptorIsNew * - * This function is used to check if the tuple descriptor - * associated with this slot has just changed. ie: we are - * now storing a new type of tuple in this slot + * This function is used to check if the tuple descriptor + * associated with this slot has just changed. ie: we are + * now storing a new type of tuple in this slot * -------------------------------- */ #ifdef NOT_USED -bool /* return: descriptor "is new" */ -ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */ +bool /* return: descriptor "is new" */ +ExecSlotDescriptorIsNew(TupleTableSlot * slot) /* slot to inspect */ { -/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot); - return isNew; */ - return slot->ttc_descIsNew; +/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot); + return isNew; */ + return slot->ttc_descIsNew; } + #endif /* ---------------------------------------------------------------- - * convenience initialization routines + * convenience initialization routines * ---------------------------------------------------------------- */ /* -------------------------------- - * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot + * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot * - * These are convenience routines to initialize the specfied slot - * in nodes inheriting the appropriate state. + * These are convenience routines to initialize the specfied slot + * in nodes inheriting the appropriate state. * -------------------------------- */ #define INIT_SLOT_DEFS \ - TupleTable tupleTable; \ - TupleTableSlot* slot - + TupleTable tupleTable; \ + TupleTableSlot* slot + #define INIT_SLOT_ALLOC \ - tupleTable = (TupleTable) estate->es_tupleTable; \ - slot = ExecAllocTableSlot(tupleTable); \ - slot->val = (HeapTuple)NULL; \ - slot->ttc_shouldFree = true; \ - slot->ttc_tupleDescriptor = (TupleDesc)NULL; \ - slot->ttc_whichplan = -1;\ - slot->ttc_descIsNew = true; + tupleTable = (TupleTable) estate->es_tupleTable; \ + slot = ExecAllocTableSlot(tupleTable); \ + slot->val = (HeapTuple)NULL; \ + slot->ttc_shouldFree = true; \ + slot->ttc_tupleDescriptor = (TupleDesc)NULL; \ + slot->ttc_whichplan = -1;\ + slot->ttc_descIsNew = true; /* ---------------- - * ExecInitResultTupleSlot + * ExecInitResultTupleSlot * ---------------- */ void -ExecInitResultTupleSlot(EState *estate, CommonState *commonstate) +ExecInitResultTupleSlot(EState * estate, CommonState * commonstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot; } /* ---------------- - * ExecInitScanTupleSlot + * ExecInitScanTupleSlot * ---------------- */ void -ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate) +ExecInitScanTupleSlot(EState * estate, CommonScanState * commonscanstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - commonscanstate->css_ScanTupleSlot = (TupleTableSlot *)slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot; } /* ---------------- - * ExecInitMarkedTupleSlot + * ExecInitMarkedTupleSlot * ---------------- */ void -ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate) +ExecInitMarkedTupleSlot(EState * estate, MergeJoinState * mergestate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot; } /* ---------------- - * ExecInitOuterTupleSlot + * ExecInitOuterTupleSlot * ---------------- */ void -ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate) +ExecInitOuterTupleSlot(EState * estate, HashJoinState * hashstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - hashstate->hj_OuterTupleSlot = slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + hashstate->hj_OuterTupleSlot = slot; } /* ---------------- - * ExecInitHashTupleSlot + * ExecInitHashTupleSlot * ---------------- */ #ifdef NOT_USED void -ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate) +ExecInitHashTupleSlot(EState * estate, HashJoinState * hashstate) { - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - hashstate->hj_HashTupleSlot = slot; + INIT_SLOT_DEFS; + INIT_SLOT_ALLOC; + hashstate->hj_HashTupleSlot = slot; } + #endif static TupleTableSlot * -NodeGetResultTupleSlot(Plan *node) +NodeGetResultTupleSlot(Plan * node) { - TupleTableSlot *slot; - - switch(nodeTag(node)) { - - case T_Result: - { - ResultState *resstate = ((Result *)node)->resstate; - slot = resstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_SeqScan: - { - CommonScanState *scanstate = ((SeqScan *)node)->scanstate; - slot = scanstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_NestLoop: - { - NestLoopState *nlstate = ((NestLoop *)node)->nlstate; - slot = nlstate->jstate.cs_ResultTupleSlot; - } - break; - - case T_Append: - { - Append *n = (Append *)node; - AppendState *unionstate; - List *unionplans; - int whichplan; - Plan *subplan; - - unionstate = n->unionstate; - unionplans = n->unionplans; - whichplan = unionstate->as_whichplan; - - subplan = (Plan*) nth(whichplan, unionplans); - slot = NodeGetResultTupleSlot(subplan); - break; - } - - case T_IndexScan: - { - CommonScanState *scanstate = ((IndexScan *)node)->scan.scanstate; - slot = scanstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_Material: - { - MaterialState *matstate = ((Material *)node)->matstate; - slot = matstate->csstate.css_ScanTupleSlot; - } - break; - - case T_Sort: - { - SortState *sortstate = ((Sort *)node)->sortstate; - slot = sortstate->csstate.css_ScanTupleSlot; - } - break; - - case T_Agg: - { - AggState *aggstate = ((Agg *)node)->aggstate; - slot = aggstate->csstate.cstate.cs_ResultTupleSlot; - } - break; - - case T_Group: - { - GroupState *grpstate = ((Group *)node)->grpstate; - slot = grpstate->csstate.cstate.cs_ResultTupleSlot; - } - break; - - case T_Hash: - { - HashState *hashstate = ((Hash *)node)->hashstate; - slot = hashstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_Unique: - { - UniqueState *uniquestate = ((Unique *)node)->uniquestate; - slot = uniquestate->cs_ResultTupleSlot; - } - break; - - case T_MergeJoin: - { - MergeJoinState *mergestate = ((MergeJoin *)node)->mergestate; - slot = mergestate->jstate.cs_ResultTupleSlot; - } - break; - - case T_HashJoin: + TupleTableSlot *slot; + + switch (nodeTag(node)) { - HashJoinState *hashjoinstate = ((HashJoin *)node)->hashjoinstate; - slot = hashjoinstate->jstate.cs_ResultTupleSlot; + + case T_Result: + { + ResultState *resstate = ((Result *) node)->resstate; + + slot = resstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_SeqScan: + { + CommonScanState *scanstate = ((SeqScan *) node)->scanstate; + + slot = scanstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_NestLoop: + { + NestLoopState *nlstate = ((NestLoop *) node)->nlstate; + + slot = nlstate->jstate.cs_ResultTupleSlot; + } + break; + + case T_Append: + { + Append *n = (Append *) node; + AppendState *unionstate; + List *unionplans; + int whichplan; + Plan *subplan; + + unionstate = n->unionstate; + unionplans = n->unionplans; + whichplan = unionstate->as_whichplan; + + subplan = (Plan *) nth(whichplan, unionplans); + slot = NodeGetResultTupleSlot(subplan); + break; + } + + case T_IndexScan: + { + CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate; + + slot = scanstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_Material: + { + MaterialState *matstate = ((Material *) node)->matstate; + + slot = matstate->csstate.css_ScanTupleSlot; + } + break; + + case T_Sort: + { + SortState *sortstate = ((Sort *) node)->sortstate; + + slot = sortstate->csstate.css_ScanTupleSlot; + } + break; + + case T_Agg: + { + AggState *aggstate = ((Agg *) node)->aggstate; + + slot = aggstate->csstate.cstate.cs_ResultTupleSlot; + } + break; + + case T_Group: + { + GroupState *grpstate = ((Group *) node)->grpstate; + + slot = grpstate->csstate.cstate.cs_ResultTupleSlot; + } + break; + + case T_Hash: + { + HashState *hashstate = ((Hash *) node)->hashstate; + + slot = hashstate->cstate.cs_ResultTupleSlot; + } + break; + + case T_Unique: + { + UniqueState *uniquestate = ((Unique *) node)->uniquestate; + + slot = uniquestate->cs_ResultTupleSlot; + } + break; + + case T_MergeJoin: + { + MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate; + + slot = mergestate->jstate.cs_ResultTupleSlot; + } + break; + + case T_HashJoin: + { + HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate; + + slot = hashjoinstate->jstate.cs_ResultTupleSlot; + } + break; + + case T_Tee: + { + TeeState *teestate = ((Tee *) node)->teestate; + + slot = teestate->cstate.cs_ResultTupleSlot; + } + break; + + default: + /* ---------------- + * should never get here + * ---------------- + */ + elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ", + nodeTag(node)); + + return NULL; } - break; - - case T_Tee: - { - TeeState *teestate = ((Tee*)node)->teestate; - slot = teestate->cstate.cs_ResultTupleSlot; - } - break; - - default: - /* ---------------- - * should never get here - * ---------------- - */ - elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ", - nodeTag(node)); - - return NULL; - } - return slot; + return slot; } /* ---------------------------------------------------------------- - * ExecGetTupType + * ExecGetTupType * - * this gives you the tuple descriptor for tuples returned - * by this node. I really wish I could ditch this routine, - * but since not all nodes store their type info in the same - * place, we have to do something special for each node type. + * this gives you the tuple descriptor for tuples returned + * by this node. I really wish I could ditch this routine, + * but since not all nodes store their type info in the same + * place, we have to do something special for each node type. * - * Soon, the system will have to adapt to deal with changing - * tuple descriptors as we deal with dynamic tuple types - * being returned from procedure nodes. Perhaps then this - * routine can be retired. -cim 6/3/91 + * Soon, the system will have to adapt to deal with changing + * tuple descriptors as we deal with dynamic tuple types + * being returned from procedure nodes. Perhaps then this + * routine can be retired. -cim 6/3/91 * * old comments - * This routine just gets the type information out of the - * node's state. If you already have a node's state, you - * can get this information directly, but this is a useful - * routine if you want to get the type information from - * the node's inner or outer subplan easily without having - * to inspect the subplan.. -cim 10/16/89 + * This routine just gets the type information out of the + * node's state. If you already have a node's state, you + * can get this information directly, but this is a useful + * routine if you want to get the type information from + * the node's inner or outer subplan easily without having + * to inspect the subplan.. -cim 10/16/89 * - * Assume that for existential nodes, we get the targetlist out - * of the right node's targetlist + * Assume that for existential nodes, we get the targetlist out + * of the right node's targetlist * ---------------------------------------------------------------- */ TupleDesc -ExecGetTupType(Plan *node) +ExecGetTupType(Plan * node) { - TupleTableSlot *slot; - TupleDesc tupType; - - if (node == NULL) - return NULL; - - slot = NodeGetResultTupleSlot(node); - tupType = slot->ttc_tupleDescriptor; - return tupType; + TupleTableSlot *slot; + TupleDesc tupType; + + if (node == NULL) + return NULL; + + slot = NodeGetResultTupleSlot(node); + tupType = slot->ttc_tupleDescriptor; + return tupType; } /* TupleDesc -ExecCopyTupType(TupleDesc td, int natts) +ExecCopyTupType(TupleDesc td, int natts) { - TupleDesc newTd; - int i; - - newTd = CreateTemplateTupleDesc(natts); - i = 0; - while (i < natts) - { - newTd[i] = - (AttributeTupleForm)palloc(sizeof(FormData_pg_attribute)); - memmove(newTd[i], td[i], sizeof(FormData_pg_attribute)); - i++; - } - return newTd; + TupleDesc newTd; + int i; + + newTd = CreateTemplateTupleDesc(natts); + i = 0; + while (i < natts) + { + newTd[i] = + (AttributeTupleForm)palloc(sizeof(FormData_pg_attribute)); + memmove(newTd[i], td[i], sizeof(FormData_pg_attribute)); + i++; + } + return newTd; } */ /* ---------------------------------------------------------------- - * ExecTypeFromTL - * - * Currently there are about 4 different places where we create - * TupleDescriptors. They should all be merged, or perhaps - * be rewritten to call BuildDesc(). - * - * old comments - * Forms attribute type info from the target list in the node. - * It assumes all domains are individually specified in the target list. - * It fails if the target list contains something like Emp.all - * which represents all the attributes from EMP relation. - * - * Conditions: - * The inner and outer subtrees should be initialized because it - * might be necessary to know the type infos of the subtrees. + * ExecTypeFromTL + * + * Currently there are about 4 different places where we create + * TupleDescriptors. They should all be merged, or perhaps + * be rewritten to call BuildDesc(). + * + * old comments + * Forms attribute type info from the target list in the node. + * It assumes all domains are individually specified in the target list. + * It fails if the target list contains something like Emp.all + * which represents all the attributes from EMP relation. + * + * Conditions: + * The inner and outer subtrees should be initialized because it + * might be necessary to know the type infos of the subtrees. * ---------------------------------------------------------------- */ TupleDesc -ExecTypeFromTL(List *targetList) +ExecTypeFromTL(List * targetList) { - List *tlcdr; - TupleDesc typeInfo; - Resdom *resdom; - Oid restype; - int len; - - /* ---------------- - * examine targetlist - if empty then return NULL - * ---------------- - */ - len = ExecTargetListLength(targetList); - - if (len == 0) - return NULL; - - /* ---------------- - * allocate a new typeInfo - * ---------------- - */ - typeInfo = CreateTemplateTupleDesc(len); - - /* ---------------- - * notes: get resdom from (resdom expr) - * get_typbyval comes from src/lib/l-lisp/lsyscache.c - * ---------------- - */ - tlcdr = targetList; - while (tlcdr != NIL) { - TargetEntry *tle = lfirst(tlcdr); - if (tle->resdom != NULL) { - resdom = tle->resdom; - restype = resdom->restype; - - TupleDescInitEntry(typeInfo, - resdom->resno, - resdom->resname, - /* fix for SELECT NULL ... */ - get_id_typname(restype?restype:UNKNOWNOID), - 0, - false); + List *tlcdr; + TupleDesc typeInfo; + Resdom *resdom; + Oid restype; + int len; + + /* ---------------- + * examine targetlist - if empty then return NULL + * ---------------- + */ + len = ExecTargetListLength(targetList); + + if (len == 0) + return NULL; + + /* ---------------- + * allocate a new typeInfo + * ---------------- + */ + typeInfo = CreateTemplateTupleDesc(len); + + /* ---------------- + * notes: get resdom from (resdom expr) + * get_typbyval comes from src/lib/l-lisp/lsyscache.c + * ---------------- + */ + tlcdr = targetList; + while (tlcdr != NIL) + { + TargetEntry *tle = lfirst(tlcdr); + + if (tle->resdom != NULL) + { + resdom = tle->resdom; + restype = resdom->restype; + + TupleDescInitEntry(typeInfo, + resdom->resno, + resdom->resname, + /* fix for SELECT NULL ... */ + get_id_typname(restype ? restype : UNKNOWNOID), + 0, + false); /* - ExecSetTypeInfo(resdom->resno - 1, - typeInfo, - (Oid) restype, - resdom->resno, - resdom->reslen, - resdom->resname->data, - get_typbyval(restype), - get_typalign(restype)); + ExecSetTypeInfo(resdom->resno - 1, + typeInfo, + (Oid) restype, + resdom->resno, + resdom->reslen, + resdom->resname->data, + get_typbyval(restype), + get_typalign(restype)); */ - } - else { - Resdom *fjRes; - List *fjTlistP; - List *fjList = lfirst(tlcdr); + } + else + { + Resdom *fjRes; + List *fjTlistP; + List *fjList = lfirst(tlcdr); + #ifdef SETS_FIXED - TargetEntry *tle; - Fjoin *fjNode = ((TargetEntry *)lfirst(fjList))->fjoin; + TargetEntry *tle; + Fjoin *fjNode = ((TargetEntry *) lfirst(fjList))->fjoin; - tle = fjNode->fj_innerNode; /* ??? */ + tle = fjNode->fj_innerNode; /* ??? */ #endif - fjRes = tle->resdom; - restype = fjRes->restype; - - TupleDescInitEntry(typeInfo, - fjRes->resno, - fjRes->resname, - get_id_typname(restype), - 0, - false); + fjRes = tle->resdom; + restype = fjRes->restype; + + TupleDescInitEntry(typeInfo, + fjRes->resno, + fjRes->resname, + get_id_typname(restype), + 0, + false); /* - ExecSetTypeInfo(fjRes->resno - 1, - typeInfo, - (Oid) restype, - fjRes->resno, - fjRes->reslen, - (char *) fjRes->resname, - get_typbyval(restype), - get_typalign(restype)); + ExecSetTypeInfo(fjRes->resno - 1, + typeInfo, + (Oid) restype, + fjRes->resno, + fjRes->reslen, + (char *) fjRes->resname, + get_typbyval(restype), + get_typalign(restype)); */ - - foreach(fjTlistP, lnext(fjList)) { - TargetEntry *fjTle = lfirst(fjTlistP); - - fjRes = fjTle->resdom; - - TupleDescInitEntry(typeInfo, - fjRes->resno, - fjRes->resname, - get_id_typname(restype), - 0, - false); - + + foreach(fjTlistP, lnext(fjList)) + { + TargetEntry *fjTle = lfirst(fjTlistP); + + fjRes = fjTle->resdom; + + TupleDescInitEntry(typeInfo, + fjRes->resno, + fjRes->resname, + get_id_typname(restype), + 0, + false); + /* - ExecSetTypeInfo(fjRes->resno - 1, - typeInfo, - (Oid) fjRes->restype, - fjRes->resno, - fjRes->reslen, - (char *) fjRes->resname, - get_typbyval(fjRes->restype), - get_typalign(fjRes->restype)); + ExecSetTypeInfo(fjRes->resno - 1, + typeInfo, + (Oid) fjRes->restype, + fjRes->resno, + fjRes->reslen, + (char *) fjRes->resname, + get_typbyval(fjRes->restype), + get_typalign(fjRes->restype)); */ - } - } - - tlcdr = lnext(tlcdr); - } - - return typeInfo; -} + } + } + tlcdr = lnext(tlcdr); + } + return typeInfo; +} diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index ac2d3516036..3795c2d1018 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1,43 +1,43 @@ /*------------------------------------------------------------------------- * * execUtils.c-- - * miscellanious executor utility routines + * miscellanious executor utility routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.14 1997/08/22 03:12:19 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.15 1997/09/07 04:41:26 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecAssignNodeBaseInfo \ - * ExecAssignDebugHooks > preforms misc work done in all the - * ExecAssignExprContext / init node routines. + * ExecAssignNodeBaseInfo \ + * ExecAssignDebugHooks > preforms misc work done in all the + * ExecAssignExprContext / init node routines. * - * ExecGetTypeInfo | old execCStructs interface - * ExecMakeTypeInfo | code from the version 1 - * ExecOrderTypeInfo | lisp system. These should - * ExecSetTypeInfo | go away or be updated soon. - * ExecFreeTypeInfo | -cim 11/1/89 - * ExecTupleAttributes / + * ExecGetTypeInfo | old execCStructs interface + * ExecMakeTypeInfo | code from the version 1 + * ExecOrderTypeInfo | lisp system. These should + * ExecSetTypeInfo | go away or be updated soon. + * ExecFreeTypeInfo | -cim 11/1/89 + * ExecTupleAttributes / * - * QueryDescGetTypeInfo - moved here from main.c - * am not sure what uses it -cim 10/12/89 + * QueryDescGetTypeInfo - moved here from main.c + * am not sure what uses it -cim 10/12/89 * - * ExecGetIndexKeyInfo \ - * ExecOpenIndices | referenced by InitPlan, EndPlan, - * ExecCloseIndices | ExecAppend, ExecReplace - * ExecFormIndexTuple | - * ExecInsertIndexTuple / + * ExecGetIndexKeyInfo \ + * ExecOpenIndices | referenced by InitPlan, EndPlan, + * ExecCloseIndices | ExecAppend, ExecReplace + * ExecFormIndexTuple | + * ExecInsertIndexTuple / + * + * NOTES + * This file has traditionally been the place to stick misc. + * executor support stuff that doesn't really go anyplace else. * - * NOTES - * This file has traditionally been the place to stick misc. - * executor support stuff that doesn't really go anyplace else. - * */ #include "postgres.h" @@ -58,1149 +58,1195 @@ #include "catalog/pg_type.h" #include "parser/parsetree.h" -static void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP, - AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP); +static void +ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP, + AttrNumber ** attsOutP, FuncIndexInfoPtr fInfoP); /* ---------------------------------------------------------------- - * global counters for number of tuples processed, retrieved, - * appended, replaced, deleted. + * global counters for number of tuples processed, retrieved, + * appended, replaced, deleted. * ---------------------------------------------------------------- */ -int NTupleProcessed; -int NTupleRetrieved; -int NTupleReplaced; -int NTupleAppended; -int NTupleDeleted; -int NIndexTupleInserted; -extern int NIndexTupleProcessed; /* have to be defined in the access - method level so that the cinterface.a - will link ok. */ +int NTupleProcessed; +int NTupleRetrieved; +int NTupleReplaced; +int NTupleAppended; +int NTupleDeleted; +int NIndexTupleInserted; +extern int NIndexTupleProcessed; /* have to be defined in the + * access method level so that the + * cinterface.a will link ok. */ /* ---------------------------------------------------------------- - * statistic functions + * statistic functions * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ResetTupleCount + * ResetTupleCount * ---------------------------------------------------------------- */ #ifdef NOT_USED void ResetTupleCount(void) { - NTupleProcessed = 0; - NTupleRetrieved = 0; - NTupleAppended = 0; - NTupleDeleted = 0; - NTupleReplaced = 0; - NIndexTupleProcessed = 0; + NTupleProcessed = 0; + NTupleRetrieved = 0; + NTupleAppended = 0; + NTupleDeleted = 0; + NTupleReplaced = 0; + NIndexTupleProcessed = 0; } + #endif /* ---------------------------------------------------------------- - * PrintTupleCount + * PrintTupleCount * ---------------------------------------------------------------- */ #ifdef NOT_USED void -DisplayTupleCount(FILE *statfp) +DisplayTupleCount(FILE * statfp) { - if (NTupleProcessed > 0) - fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed, - (NTupleProcessed == 1) ? "" : "s"); - else { - fprintf(statfp, "!\tno tuples processed.\n"); - return; - } - if (NIndexTupleProcessed > 0) - fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed, - (NIndexTupleProcessed == 1) ? "" : "s"); - if (NIndexTupleInserted > 0) - fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted, - (NIndexTupleInserted == 1) ? "" : "s"); - if (NTupleRetrieved > 0) - fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved, - (NTupleRetrieved == 1) ? "" : "s"); - if (NTupleAppended > 0) - fprintf(statfp, "%d tuple%s appended. ", NTupleAppended, - (NTupleAppended == 1) ? "" : "s"); - if (NTupleDeleted > 0) - fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted, - (NTupleDeleted == 1) ? "" : "s"); - if (NTupleReplaced > 0) - fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced, - (NTupleReplaced == 1) ? "" : "s"); - fprintf(statfp, "\n"); + if (NTupleProcessed > 0) + fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed, + (NTupleProcessed == 1) ? "" : "s"); + else + { + fprintf(statfp, "!\tno tuples processed.\n"); + return; + } + if (NIndexTupleProcessed > 0) + fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed, + (NIndexTupleProcessed == 1) ? "" : "s"); + if (NIndexTupleInserted > 0) + fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted, + (NIndexTupleInserted == 1) ? "" : "s"); + if (NTupleRetrieved > 0) + fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved, + (NTupleRetrieved == 1) ? "" : "s"); + if (NTupleAppended > 0) + fprintf(statfp, "%d tuple%s appended. ", NTupleAppended, + (NTupleAppended == 1) ? "" : "s"); + if (NTupleDeleted > 0) + fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted, + (NTupleDeleted == 1) ? "" : "s"); + if (NTupleReplaced > 0) + fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced, + (NTupleReplaced == 1) ? "" : "s"); + fprintf(statfp, "\n"); } + #endif /* ---------------------------------------------------------------- - * miscellanious init node support functions + * miscellanious init node support functions * - * ExecAssignNodeBaseInfo - assigns the baseid field of the node - * ExecAssignDebugHooks - assigns the node's debugging hooks - * ExecAssignExprContext - assigns the node's expression context + * ExecAssignNodeBaseInfo - assigns the baseid field of the node + * ExecAssignDebugHooks - assigns the node's debugging hooks + * ExecAssignExprContext - assigns the node's expression context * ---------------------------------------------------------------- */ /* ---------------- - * ExecAssignNodeBaseInfo + * ExecAssignNodeBaseInfo * - * as it says, this assigns the baseid field of the node and - * increments the counter in the estate. In addition, it initializes - * the base_parent field of the basenode. + * as it says, this assigns the baseid field of the node and + * increments the counter in the estate. In addition, it initializes + * the base_parent field of the basenode. * ---------------- */ void -ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent) +ExecAssignNodeBaseInfo(EState * estate, CommonState * cstate, Plan * parent) { - int baseId; - - baseId = estate->es_BaseId; - cstate->cs_base_id = baseId; - estate->es_BaseId = baseId + 1; + int baseId; + + baseId = estate->es_BaseId; + cstate->cs_base_id = baseId; + estate->es_BaseId = baseId + 1; } /* ---------------- - * ExecAssignExprContext + * ExecAssignExprContext * - * This initializes the ExprContext field. It is only necessary - * to do this for nodes which use ExecQual or ExecTargetList - * because those routines depend on econtext. Other nodes which - * dont have to evaluate expressions don't need to do this. + * This initializes the ExprContext field. It is only necessary + * to do this for nodes which use ExecQual or ExecTargetList + * because those routines depend on econtext. Other nodes which + * dont have to evaluate expressions don't need to do this. * ---------------- */ void -ExecAssignExprContext(EState *estate, CommonState *commonstate) +ExecAssignExprContext(EState * estate, CommonState * commonstate) { - ExprContext *econtext; - ParamListInfo paraminfo; - List *rangeTable; - - paraminfo = estate->es_param_list_info; - rangeTable = estate->es_range_table; - - econtext = makeNode(ExprContext); - econtext->ecxt_scantuple = NULL; /* scan tuple slot */ - econtext->ecxt_innertuple = NULL; /* inner tuple slot */ - econtext->ecxt_outertuple = NULL; /* outer tuple slot */ - econtext->ecxt_relation = NULL; /* relation */ - econtext->ecxt_relid = 0; /* relid */ - econtext->ecxt_param_list_info = paraminfo; /* param list info */ - econtext->ecxt_range_table = rangeTable; /* range table */ - - commonstate->cs_ExprContext = econtext; + ExprContext *econtext; + ParamListInfo paraminfo; + List *rangeTable; + + paraminfo = estate->es_param_list_info; + rangeTable = estate->es_range_table; + + econtext = makeNode(ExprContext); + econtext->ecxt_scantuple = NULL; /* scan tuple slot */ + econtext->ecxt_innertuple = NULL; /* inner tuple slot */ + econtext->ecxt_outertuple = NULL; /* outer tuple slot */ + econtext->ecxt_relation = NULL; /* relation */ + econtext->ecxt_relid = 0; /* relid */ + econtext->ecxt_param_list_info = paraminfo; /* param list info */ + econtext->ecxt_range_table = rangeTable; /* range table */ + + commonstate->cs_ExprContext = econtext; } /* ---------------------------------------------------------------- - * Result slot tuple type and ProjectionInfo support + * Result slot tuple type and ProjectionInfo support * ---------------------------------------------------------------- */ /* ---------------- - * ExecAssignResultType + * ExecAssignResultType * ---------------- */ void -ExecAssignResultType(CommonState *commonstate, - TupleDesc tupDesc) +ExecAssignResultType(CommonState * commonstate, + TupleDesc tupDesc) { - TupleTableSlot *slot; - - slot = commonstate->cs_ResultTupleSlot; - slot->ttc_tupleDescriptor = tupDesc; + TupleTableSlot *slot; + + slot = commonstate->cs_ResultTupleSlot; + slot->ttc_tupleDescriptor = tupDesc; } /* ---------------- - * ExecAssignResultTypeFromOuterPlan + * ExecAssignResultTypeFromOuterPlan * ---------------- */ void -ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate) +ExecAssignResultTypeFromOuterPlan(Plan * node, CommonState * commonstate) { - Plan *outerPlan; - TupleDesc tupDesc; - - outerPlan = outerPlan(node); - tupDesc = ExecGetTupType(outerPlan); - - ExecAssignResultType(commonstate, tupDesc); + Plan *outerPlan; + TupleDesc tupDesc; + + outerPlan = outerPlan(node); + tupDesc = ExecGetTupType(outerPlan); + + ExecAssignResultType(commonstate, tupDesc); } /* ---------------- - * ExecAssignResultTypeFromTL + * ExecAssignResultTypeFromTL * ---------------- */ void -ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate) +ExecAssignResultTypeFromTL(Plan * node, CommonState * commonstate) { - List *targetList; - int i; - int len; - List *tl; - TargetEntry *tle; - List *fjtl; - TupleDesc origTupDesc; - - targetList = node->targetlist; - origTupDesc = ExecTypeFromTL(targetList); - len = ExecTargetListLength(targetList); - - fjtl = NIL; - tl = targetList; - i = 0; - while (tl != NIL || fjtl != NIL) { - if (fjtl != NIL) { - tle = lfirst(fjtl); - fjtl = lnext(fjtl); - } - else { - tle = lfirst(tl); - tl = lnext(tl); - } + List *targetList; + int i; + int len; + List *tl; + TargetEntry *tle; + List *fjtl; + TupleDesc origTupDesc; + + targetList = node->targetlist; + origTupDesc = ExecTypeFromTL(targetList); + len = ExecTargetListLength(targetList); + + fjtl = NIL; + tl = targetList; + i = 0; + while (tl != NIL || fjtl != NIL) + { + if (fjtl != NIL) + { + tle = lfirst(fjtl); + fjtl = lnext(fjtl); + } + else + { + tle = lfirst(tl); + tl = lnext(tl); + } #ifdef SETS_FIXED - if (!tl_is_resdom(tle)) { - Fjoin *fj = (Fjoin *)lfirst(tle); - /* it is a FJoin */ - fjtl = lnext(tle); - tle = fj->fj_innerNode; - } + if (!tl_is_resdom(tle)) + { + Fjoin *fj = (Fjoin *) lfirst(tle); + + /* it is a FJoin */ + fjtl = lnext(tle); + tle = fj->fj_innerNode; + } #endif - i++; - } - if (len > 0) { - ExecAssignResultType(commonstate, - origTupDesc); - } - else - ExecAssignResultType(commonstate, - (TupleDesc)NULL); + i++; + } + if (len > 0) + { + ExecAssignResultType(commonstate, + origTupDesc); + } + else + ExecAssignResultType(commonstate, + (TupleDesc) NULL); } /* ---------------- - * ExecGetResultType + * ExecGetResultType * ---------------- */ TupleDesc -ExecGetResultType(CommonState *commonstate) +ExecGetResultType(CommonState * commonstate) { - TupleTableSlot *slot = commonstate->cs_ResultTupleSlot; - - return slot->ttc_tupleDescriptor; + TupleTableSlot *slot = commonstate->cs_ResultTupleSlot; + + return slot->ttc_tupleDescriptor; } /* ---------------- - * ExecFreeResultType + * ExecFreeResultType * ---------------- */ #ifdef NOT_USED void -ExecFreeResultType(CommonState *commonstate) +ExecFreeResultType(CommonState * commonstate) { - TupleTableSlot *slot; - TupleDesc tupType; - - slot = commonstate->cs_ResultTupleSlot; - tupType = slot->ttc_tupleDescriptor; - -/* ExecFreeTypeInfo(tupType); */ - pfree(tupType); + TupleTableSlot *slot; + TupleDesc tupType; + + slot = commonstate->cs_ResultTupleSlot; + tupType = slot->ttc_tupleDescriptor; + +/* ExecFreeTypeInfo(tupType); */ + pfree(tupType); } + #endif /* ---------------- - * ExecAssignProjectionInfo - forms the projection information from the node's targetlist + * ExecAssignProjectionInfo + forms the projection information from the node's targetlist * ---------------- */ void -ExecAssignProjectionInfo(Plan *node, CommonState *commonstate) +ExecAssignProjectionInfo(Plan * node, CommonState * commonstate) { - ProjectionInfo *projInfo; - List *targetList; - int len; - - targetList = node->targetlist; - len = ExecTargetListLength(targetList); - - projInfo = makeNode(ProjectionInfo); - projInfo->pi_targetlist = targetList; - projInfo->pi_len = len; - projInfo->pi_tupValue = - (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len); - projInfo->pi_exprContext = commonstate->cs_ExprContext; - projInfo->pi_slot = commonstate->cs_ResultTupleSlot; - - commonstate->cs_ProjInfo = projInfo; + ProjectionInfo *projInfo; + List *targetList; + int len; + + targetList = node->targetlist; + len = ExecTargetListLength(targetList); + + projInfo = makeNode(ProjectionInfo); + projInfo->pi_targetlist = targetList; + projInfo->pi_len = len; + projInfo->pi_tupValue = + (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len); + projInfo->pi_exprContext = commonstate->cs_ExprContext; + projInfo->pi_slot = commonstate->cs_ResultTupleSlot; + + commonstate->cs_ProjInfo = projInfo; } /* ---------------- - * ExecFreeProjectionInfo + * ExecFreeProjectionInfo * ---------------- */ void -ExecFreeProjectionInfo(CommonState *commonstate) +ExecFreeProjectionInfo(CommonState * commonstate) { - ProjectionInfo *projInfo; - - /* ---------------- - * get projection info. if NULL then this node has - * none so we just return. - * ---------------- - */ - projInfo = commonstate->cs_ProjInfo; - if (projInfo == NULL) - return; - - /* ---------------- - * clean up memory used. - * ---------------- - */ - if (projInfo->pi_tupValue != NULL) - pfree(projInfo->pi_tupValue); - - pfree(projInfo); - commonstate->cs_ProjInfo = NULL; + ProjectionInfo *projInfo; + + /* ---------------- + * get projection info. if NULL then this node has + * none so we just return. + * ---------------- + */ + projInfo = commonstate->cs_ProjInfo; + if (projInfo == NULL) + return; + + /* ---------------- + * clean up memory used. + * ---------------- + */ + if (projInfo->pi_tupValue != NULL) + pfree(projInfo->pi_tupValue); + + pfree(projInfo); + commonstate->cs_ProjInfo = NULL; } /* ---------------------------------------------------------------- - * the following scan type support functions are for - * those nodes which are stubborn and return tuples in - * their Scan tuple slot instead of their Result tuple - * slot.. luck fur us, these nodes do not do projections - * so we don't have to worry about getting the ProjectionInfo - * right for them... -cim 6/3/91 + * the following scan type support functions are for + * those nodes which are stubborn and return tuples in + * their Scan tuple slot instead of their Result tuple + * slot.. luck fur us, these nodes do not do projections + * so we don't have to worry about getting the ProjectionInfo + * right for them... -cim 6/3/91 * ---------------------------------------------------------------- */ /* ---------------- - * ExecGetScanType + * ExecGetScanType * ---------------- */ TupleDesc -ExecGetScanType(CommonScanState *csstate) +ExecGetScanType(CommonScanState * csstate) { - TupleTableSlot *slot = csstate->css_ScanTupleSlot; - return slot->ttc_tupleDescriptor; + TupleTableSlot *slot = csstate->css_ScanTupleSlot; + + return slot->ttc_tupleDescriptor; } /* ---------------- - * ExecFreeScanType + * ExecFreeScanType * ---------------- */ #ifdef NOT_USED void -ExecFreeScanType(CommonScanState *csstate) +ExecFreeScanType(CommonScanState * csstate) { - TupleTableSlot *slot; - TupleDesc tupType; - - slot = csstate->css_ScanTupleSlot; - tupType = slot->ttc_tupleDescriptor; - -/* ExecFreeTypeInfo(tupType); */ - pfree(tupType); + TupleTableSlot *slot; + TupleDesc tupType; + + slot = csstate->css_ScanTupleSlot; + tupType = slot->ttc_tupleDescriptor; + +/* ExecFreeTypeInfo(tupType); */ + pfree(tupType); } + #endif /* ---------------- - * ExecAssignScanType + * ExecAssignScanType * ---------------- */ void -ExecAssignScanType(CommonScanState *csstate, - TupleDesc tupDesc) +ExecAssignScanType(CommonScanState * csstate, + TupleDesc tupDesc) { - TupleTableSlot *slot; - - slot = (TupleTableSlot *) csstate->css_ScanTupleSlot; - slot->ttc_tupleDescriptor = tupDesc; + TupleTableSlot *slot; + + slot = (TupleTableSlot *) csstate->css_ScanTupleSlot; + slot->ttc_tupleDescriptor = tupDesc; } /* ---------------- - * ExecAssignScanTypeFromOuterPlan + * ExecAssignScanTypeFromOuterPlan * ---------------- */ void -ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate) +ExecAssignScanTypeFromOuterPlan(Plan * node, CommonScanState * csstate) { - Plan *outerPlan; - TupleDesc tupDesc; - - outerPlan = outerPlan(node); - tupDesc = ExecGetTupType(outerPlan); + Plan *outerPlan; + TupleDesc tupDesc; + + outerPlan = outerPlan(node); + tupDesc = ExecGetTupType(outerPlan); - ExecAssignScanType(csstate, tupDesc); + ExecAssignScanType(csstate, tupDesc); } /* ---------------------------------------------------------------- - * ExecTypeFromTL support routines. + * ExecTypeFromTL support routines. * - * these routines are used mainly from ExecTypeFromTL. - * -cim 6/12/90 + * these routines are used mainly from ExecTypeFromTL. + * -cim 6/12/90 * * old comments - * Routines dealing with the structure 'attribute' which conatains - * the type information about attributes in a tuple: + * Routines dealing with the structure 'attribute' which conatains + * the type information about attributes in a tuple: * - * ExecMakeTypeInfo(noType) -- - * returns pointer to array of 'noType' structure 'attribute'. - * ExecSetTypeInfo(index, typeInfo, attNum, attLen) -- - * sets the element indexed by 'index' in typeInfo with - * the values: attNum, attLen. - * ExecFreeTypeInfo(typeInfo) -- - * frees the structure 'typeInfo'. + * ExecMakeTypeInfo(noType) -- + * returns pointer to array of 'noType' structure 'attribute'. + * ExecSetTypeInfo(index, typeInfo, attNum, attLen) -- + * sets the element indexed by 'index' in typeInfo with + * the values: attNum, attLen. + * ExecFreeTypeInfo(typeInfo) -- + * frees the structure 'typeInfo'. * ---------------------------------------------------------------- */ /* ---------------- - * ExecSetTypeInfo + * ExecSetTypeInfo * - * This initializes fields of a single attribute in a - * tuple descriptor from the specified parameters. + * This initializes fields of a single attribute in a + * tuple descriptor from the specified parameters. * - * XXX this duplicates much of the functionality of TupleDescInitEntry. - * the routines should be moved to the same place and be rewritten - * to share common code. + * XXX this duplicates much of the functionality of TupleDescInitEntry. + * the routines should be moved to the same place and be rewritten + * to share common code. * ---------------- */ -#ifdef NOT_USED +#ifdef NOT_USED void ExecSetTypeInfo(int index, - TupleDesc typeInfo, - Oid typeID, - int attNum, - int attLen, - char *attName, - bool attbyVal, - char attalign) + TupleDesc typeInfo, + Oid typeID, + int attNum, + int attLen, + char *attName, + bool attbyVal, + char attalign) { - AttributeTupleForm att; - - /* ---------------- - * get attribute pointer and preform a sanity check.. - * ---------------- - */ - att = typeInfo[index]; - if (att == NULL) - elog(WARN, "ExecSetTypeInfo: trying to assign through NULL"); - - /* ---------------- - * assign values to the tuple descriptor, being careful not - * to copy a null attName.. - * - * XXX it is unknown exactly what information is needed to - * initialize the attribute struct correctly so for now - * we use 0. this should be fixed -- otherwise we run the - * risk of using garbage data. -cim 5/5/91 - * ---------------- - */ - att->attrelid = 0; /* dummy value */ - - if (attName != (char *) NULL) - strNcpy(att->attname.data, attName, NAMEDATALEN-1); - else - memset(att->attname.data,0,NAMEDATALEN); - - att->atttypid = typeID; - att->attdefrel = 0; /* dummy value */ - att->attdisbursion = 0; /* dummy value */ - att->atttyparg = 0; /* dummy value */ - att->attlen = attLen; - att->attnum = attNum; - att->attbound = 0; /* dummy value */ - att->attbyval = attbyVal; - att->attcanindex = 0; /* dummy value */ - att->attproc = 0; /* dummy value */ - att->attnelems = 0; /* dummy value */ - att->attcacheoff = -1; - att->attisset = false; - att->attalign = attalign; + AttributeTupleForm att; + + /* ---------------- + * get attribute pointer and preform a sanity check.. + * ---------------- + */ + att = typeInfo[index]; + if (att == NULL) + elog(WARN, "ExecSetTypeInfo: trying to assign through NULL"); + + /* ---------------- + * assign values to the tuple descriptor, being careful not + * to copy a null attName.. + * + * XXX it is unknown exactly what information is needed to + * initialize the attribute struct correctly so for now + * we use 0. this should be fixed -- otherwise we run the + * risk of using garbage data. -cim 5/5/91 + * ---------------- + */ + att->attrelid = 0; /* dummy value */ + + if (attName != (char *) NULL) + strNcpy(att->attname.data, attName, NAMEDATALEN - 1); + else + memset(att->attname.data, 0, NAMEDATALEN); + + att->atttypid = typeID; + att->attdefrel = 0; /* dummy value */ + att->attdisbursion = 0; /* dummy value */ + att->atttyparg = 0; /* dummy value */ + att->attlen = attLen; + att->attnum = attNum; + att->attbound = 0; /* dummy value */ + att->attbyval = attbyVal; + att->attcanindex = 0; /* dummy value */ + att->attproc = 0; /* dummy value */ + att->attnelems = 0; /* dummy value */ + att->attcacheoff = -1; + att->attisset = false; + att->attalign = attalign; } /* ---------------- - * ExecFreeTypeInfo frees the array of attrbutes - * created by ExecMakeTypeInfo and returned by ExecTypeFromTL... + * ExecFreeTypeInfo frees the array of attrbutes + * created by ExecMakeTypeInfo and returned by ExecTypeFromTL... * ---------------- */ void ExecFreeTypeInfo(TupleDesc typeInfo) { - /* ---------------- - * do nothing if asked to free a null pointer - * ---------------- - */ - if (typeInfo == NULL) - return; - - /* ---------------- - * the entire array of typeinfo pointers created by - * ExecMakeTypeInfo was allocated with a single palloc() - * so we can deallocate the whole array with a single pfree(). - * (we should not try and free all the elements in the array) - * -cim 6/12/90 - * ---------------- - */ - pfree(typeInfo); + /* ---------------- + * do nothing if asked to free a null pointer + * ---------------- + */ + if (typeInfo == NULL) + return; + + /* ---------------- + * the entire array of typeinfo pointers created by + * ExecMakeTypeInfo was allocated with a single palloc() + * so we can deallocate the whole array with a single pfree(). + * (we should not try and free all the elements in the array) + * -cim 6/12/90 + * ---------------- + */ + pfree(typeInfo); } /* ---------------------------------------------------------------- - * QueryDescGetTypeInfo + * QueryDescGetTypeInfo * - *| I don't know how this is used, all I know is that it - *| appeared one day in main.c so I moved it here. -cim 11/1/89 + *| I don't know how this is used, all I know is that it + *| appeared one day in main.c so I moved it here. -cim 11/1/89 * ---------------------------------------------------------------- */ TupleDesc -QueryDescGetTypeInfo(QueryDesc *queryDesc) +QueryDescGetTypeInfo(QueryDesc * queryDesc) { - Plan *plan; - TupleDesc tupleType; - List *targetList; - AttrInfo *attinfo = (AttrInfo *)palloc(sizeof(AttrInfo)); - - plan = queryDesc->plantree; - tupleType = (TupleDesc) ExecGetTupType(plan); + Plan *plan; + TupleDesc tupleType; + List *targetList; + AttrInfo *attinfo = (AttrInfo *) palloc(sizeof(AttrInfo)); + + plan = queryDesc->plantree; + tupleType = (TupleDesc) ExecGetTupType(plan); /* - targetList = plan->targetlist; + targetList = plan->targetlist; - attinfo->numAttr = ExecTargetListLength(targetList); - attinfo->attrs = tupleType; + attinfo->numAttr = ExecTargetListLength(targetList); + attinfo->attrs = tupleType; */ - attinfo->numAttr = tupleType->natts; - attinfo->attrs = tupleType->attrs; - return attinfo; + attinfo->numAttr = tupleType->natts; + attinfo->attrs = tupleType->attrs; + return attinfo; } + #endif /* ---------------------------------------------------------------- - * ExecInsertIndexTuples support + * ExecInsertIndexTuples support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecGetIndexKeyInfo + * ExecGetIndexKeyInfo * - * Extracts the index key attribute numbers from - * an index tuple form (i.e. a tuple from the pg_index relation) - * into an array of attribute numbers. The array and the - * size of the array are returned to the caller via return - * parameters. + * Extracts the index key attribute numbers from + * an index tuple form (i.e. a tuple from the pg_index relation) + * into an array of attribute numbers. The array and the + * size of the array are returned to the caller via return + * parameters. * ---------------------------------------------------------------- */ static void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, - int *numAttsOutP, - AttrNumber **attsOutP, - FuncIndexInfoPtr fInfoP) + int *numAttsOutP, + AttrNumber ** attsOutP, + FuncIndexInfoPtr fInfoP) { - int i; - int numKeys; - AttrNumber *attKeys; - - /* ---------------- - * check parameters - * ---------------- - */ - if (numAttsOutP == NULL && attsOutP == NULL) { - elog(DEBUG, "ExecGetIndexKeyInfo: %s", - "invalid parameters: numAttsOutP and attsOutP must be non-NULL"); - } - - /* ---------------- - * set the procid for a possible functional index. - * ---------------- - */ - FIsetProcOid(fInfoP, indexTuple->indproc); - - /* ---------------- - * count the number of keys.. - * ---------------- - */ - numKeys = 0; - for (i=0; i<8 && indexTuple->indkey[i] != 0; i++) - numKeys++; - - /* ---------------- - * place number keys in callers return area - * or the number of arguments for a functional index. - * - * If we have a functional index then the number of - * attributes defined in the index must 1 (the function's - * single return value). - * ---------------- - */ - if (FIgetProcOid(fInfoP) != InvalidOid) { - FIsetnArgs(fInfoP, numKeys); - (*numAttsOutP) = 1; - } - else - (*numAttsOutP) = numKeys; - - if (numKeys < 1) { - elog(DEBUG, "ExecGetIndexKeyInfo: %s", - "all index key attribute numbers are zero!"); - (*attsOutP) = NULL; - return; - } - - /* ---------------- - * allocate and fill in array of key attribute numbers - * ---------------- - */ - CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext); - - attKeys = (AttrNumber*) - palloc(numKeys * sizeof(AttrNumber)); - - for (i=0; i<numKeys; i++) - attKeys[i] = indexTuple->indkey[i]; - - /* ---------------- - * return array to caller. - * ---------------- - */ - (*attsOutP) = attKeys; -} + int i; + int numKeys; + AttrNumber *attKeys; -/* ---------------------------------------------------------------- - * ExecOpenIndices - * - * Here we scan the pg_index relation to find indices - * associated with a given heap relation oid. Since we - * don't know in advance how many indices we have, we - * form lists containing the information we need from - * pg_index and then process these lists. - * - * Note: much of this code duplicates effort done by - * the IndexCatalogInformation function in plancat.c - * because IndexCatalogInformation is poorly written. - * - * It would be much better the functionality provided - * by this function and IndexCatalogInformation was - * in the form of a small set of orthogonal routines.. - * If you are trying to understand this, I suggest you - * look at the code to IndexCatalogInformation and - * FormIndexTuple.. -cim 9/27/89 - * ---------------------------------------------------------------- - */ -void -ExecOpenIndices(Oid resultRelationOid, - RelationInfo *resultRelationInfo) -{ - Relation indexRd; - HeapScanDesc indexSd; - ScanKeyData key; - HeapTuple tuple; - IndexTupleForm indexStruct; - Oid indexOid; - List *oidList; - List *nkeyList; - List *keyList; - List *fiList; - char *predString; - List *predList; - List *indexoid; - List *numkeys; - List *indexkeys; - List *indexfuncs; - List *indexpreds; - int len; - - RelationPtr relationDescs; - IndexInfo **indexInfoArray; - FuncIndexInfoPtr fInfoP; - int numKeyAtts; - AttrNumber *indexKeyAtts; - PredInfo *predicate; - int i; - - /* ---------------- - * open pg_index - * ---------------- - */ - indexRd = heap_openr(IndexRelationName); - - /* ---------------- - * form a scan key - * ---------------- - */ - ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(resultRelationOid)); - - /* ---------------- - * scan the index relation, looking for indices for our - * result relation.. - * ---------------- - */ - indexSd = heap_beginscan(indexRd, /* scan desc */ - false, /* scan backward flag */ - NowTimeQual, /* time qual */ - 1, /* number scan keys */ - &key); /* scan keys */ - - oidList = NIL; - nkeyList = NIL; - keyList = NIL; - fiList = NIL; - predList = NIL; - - while(tuple = heap_getnext(indexSd, /* scan desc */ - false, /* scan backward flag */ - NULL), /* return: buffer */ - HeapTupleIsValid(tuple)) { - /* ---------------- - * For each index relation we find, extract the information - * we need and store it in a list.. - * - * first get the oid of the index relation from the tuple + * check parameters * ---------------- */ - indexStruct = (IndexTupleForm) GETSTRUCT(tuple); - indexOid = indexStruct->indexrelid; - + if (numAttsOutP == NULL && attsOutP == NULL) + { + elog(DEBUG, "ExecGetIndexKeyInfo: %s", + "invalid parameters: numAttsOutP and attsOutP must be non-NULL"); + } + /* ---------------- - * allocate space for functional index information. + * set the procid for a possible functional index. * ---------------- */ - fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) ); - + FIsetProcOid(fInfoP, indexTuple->indproc); + /* ---------------- - * next get the index key information from the tuple + * count the number of keys.. * ---------------- */ - ExecGetIndexKeyInfo(indexStruct, - &numKeyAtts, - &indexKeyAtts, - fInfoP); - + numKeys = 0; + for (i = 0; i < 8 && indexTuple->indkey[i] != 0; i++) + numKeys++; + /* ---------------- - * next get the index predicate from the tuple + * place number keys in callers return area + * or the number of arguments for a functional index. + * + * If we have a functional index then the number of + * attributes defined in the index must 1 (the function's + * single return value). * ---------------- */ - if (VARSIZE(&indexStruct->indpred) != 0) { - predString = fmgr(F_TEXTOUT, &indexStruct->indpred); - predicate = (PredInfo*)stringToNode(predString); - pfree(predString); - } else { - predicate = NULL; + if (FIgetProcOid(fInfoP) != InvalidOid) + { + FIsetnArgs(fInfoP, numKeys); + (*numAttsOutP) = 1; + } + else + (*numAttsOutP) = numKeys; + + if (numKeys < 1) + { + elog(DEBUG, "ExecGetIndexKeyInfo: %s", + "all index key attribute numbers are zero!"); + (*attsOutP) = NULL; + return; } - + /* ---------------- - * save the index information into lists + * allocate and fill in array of key attribute numbers * ---------------- */ - oidList = lconsi(indexOid, oidList); - nkeyList = lconsi(numKeyAtts, nkeyList); - keyList = lcons(indexKeyAtts, keyList); - fiList = lcons(fInfoP, fiList); - predList = lcons(predicate, predList); - } - - /* ---------------- - * we have the info we need so close the pg_index relation.. - * ---------------- - */ - heap_endscan(indexSd); - heap_close(indexRd); - - /* ---------------- - * Now that we've collected the index information into three - * lists, we open the index relations and store the descriptors - * and the key information into arrays. - * ---------------- - */ - len = length(oidList); - if (len > 0) { + CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext); + + attKeys = (AttrNumber *) + palloc(numKeys * sizeof(AttrNumber)); + + for (i = 0; i < numKeys; i++) + attKeys[i] = indexTuple->indkey[i]; + /* ---------------- - * allocate space for relation descs + * return array to caller. * ---------------- */ - CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); - relationDescs = (RelationPtr) - palloc(len * sizeof(Relation)); - + (*attsOutP) = attKeys; +} + +/* ---------------------------------------------------------------- + * ExecOpenIndices + * + * Here we scan the pg_index relation to find indices + * associated with a given heap relation oid. Since we + * don't know in advance how many indices we have, we + * form lists containing the information we need from + * pg_index and then process these lists. + * + * Note: much of this code duplicates effort done by + * the IndexCatalogInformation function in plancat.c + * because IndexCatalogInformation is poorly written. + * + * It would be much better the functionality provided + * by this function and IndexCatalogInformation was + * in the form of a small set of orthogonal routines.. + * If you are trying to understand this, I suggest you + * look at the code to IndexCatalogInformation and + * FormIndexTuple.. -cim 9/27/89 + * ---------------------------------------------------------------- + */ +void +ExecOpenIndices(Oid resultRelationOid, + RelationInfo * resultRelationInfo) +{ + Relation indexRd; + HeapScanDesc indexSd; + ScanKeyData key; + HeapTuple tuple; + IndexTupleForm indexStruct; + Oid indexOid; + List *oidList; + List *nkeyList; + List *keyList; + List *fiList; + char *predString; + List *predList; + List *indexoid; + List *numkeys; + List *indexkeys; + List *indexfuncs; + List *indexpreds; + int len; + + RelationPtr relationDescs; + IndexInfo **indexInfoArray; + FuncIndexInfoPtr fInfoP; + int numKeyAtts; + AttrNumber *indexKeyAtts; + PredInfo *predicate; + int i; + /* ---------------- - * initialize index info array + * open pg_index * ---------------- */ - CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); - indexInfoArray = (IndexInfo**) - palloc(len * sizeof(IndexInfo*)); - - for (i=0; i<len; i++) { - IndexInfo *ii = makeNode(IndexInfo); - ii->ii_NumKeyAttributes = 0; - ii->ii_KeyAttributeNumbers = (AttrNumber*) NULL; - ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL; - ii->ii_Predicate = NULL; - indexInfoArray[i] = ii; - } - + indexRd = heap_openr(IndexRelationName); + /* ---------------- - * attempt to open each of the indices. If we succeed, - * then store the index relation descriptor into the - * relation descriptor array. + * form a scan key * ---------------- */ - i = 0; - foreach (indexoid, oidList) { - Relation indexDesc; - - indexOid = lfirsti(indexoid); - indexDesc = index_open(indexOid); - if (indexDesc != NULL) - relationDescs[i++] = indexDesc; + ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(resultRelationOid)); + + /* ---------------- + * scan the index relation, looking for indices for our + * result relation.. + * ---------------- + */ + indexSd = heap_beginscan(indexRd, /* scan desc */ + false, /* scan backward flag */ + NowTimeQual, /* time qual */ + 1, /* number scan keys */ + &key); /* scan keys */ + + oidList = NIL; + nkeyList = NIL; + keyList = NIL; + fiList = NIL; + predList = NIL; + + while (tuple = heap_getnext(indexSd, /* scan desc */ + false, /* scan backward flag */ + NULL), /* return: buffer */ + HeapTupleIsValid(tuple)) + { + + /* ---------------- + * For each index relation we find, extract the information + * we need and store it in a list.. + * + * first get the oid of the index relation from the tuple + * ---------------- + */ + indexStruct = (IndexTupleForm) GETSTRUCT(tuple); + indexOid = indexStruct->indexrelid; + + /* ---------------- + * allocate space for functional index information. + * ---------------- + */ + fInfoP = (FuncIndexInfoPtr) palloc(sizeof(*fInfoP)); + + /* ---------------- + * next get the index key information from the tuple + * ---------------- + */ + ExecGetIndexKeyInfo(indexStruct, + &numKeyAtts, + &indexKeyAtts, + fInfoP); + + /* ---------------- + * next get the index predicate from the tuple + * ---------------- + */ + if (VARSIZE(&indexStruct->indpred) != 0) + { + predString = fmgr(F_TEXTOUT, &indexStruct->indpred); + predicate = (PredInfo *) stringToNode(predString); + pfree(predString); + } + else + { + predicate = NULL; + } + + /* ---------------- + * save the index information into lists + * ---------------- + */ + oidList = lconsi(indexOid, oidList); + nkeyList = lconsi(numKeyAtts, nkeyList); + keyList = lcons(indexKeyAtts, keyList); + fiList = lcons(fInfoP, fiList); + predList = lcons(predicate, predList); } - + /* ---------------- - * store the relation descriptor array and number of - * descs into the result relation info. + * we have the info we need so close the pg_index relation.. * ---------------- */ - resultRelationInfo->ri_NumIndices = i; - resultRelationInfo->ri_IndexRelationDescs = relationDescs; - + heap_endscan(indexSd); + heap_close(indexRd); + /* ---------------- - * store the index key information collected in our - * lists into the index info array + * Now that we've collected the index information into three + * lists, we open the index relations and store the descriptors + * and the key information into arrays. * ---------------- */ - i = 0; - foreach (numkeys, nkeyList) { - numKeyAtts = lfirsti(numkeys); - indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts; - } - - i = 0; - foreach (indexkeys, keyList) { - indexKeyAtts = (AttrNumber*) lfirst(indexkeys); - indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts; - } - - i = 0; - foreach (indexfuncs, fiList) { - FuncIndexInfoPtr fiP = (FuncIndexInfoPtr)lfirst(indexfuncs); - indexInfoArray[i++]->ii_FuncIndexInfo = fiP; - } - - i = 0; - foreach (indexpreds, predList) { - indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds); + len = length(oidList); + if (len > 0) + { + /* ---------------- + * allocate space for relation descs + * ---------------- + */ + CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); + relationDescs = (RelationPtr) + palloc(len * sizeof(Relation)); + + /* ---------------- + * initialize index info array + * ---------------- + */ + CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); + indexInfoArray = (IndexInfo **) + palloc(len * sizeof(IndexInfo *)); + + for (i = 0; i < len; i++) + { + IndexInfo *ii = makeNode(IndexInfo); + + ii->ii_NumKeyAttributes = 0; + ii->ii_KeyAttributeNumbers = (AttrNumber *) NULL; + ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL; + ii->ii_Predicate = NULL; + indexInfoArray[i] = ii; + } + + /* ---------------- + * attempt to open each of the indices. If we succeed, + * then store the index relation descriptor into the + * relation descriptor array. + * ---------------- + */ + i = 0; + foreach(indexoid, oidList) + { + Relation indexDesc; + + indexOid = lfirsti(indexoid); + indexDesc = index_open(indexOid); + if (indexDesc != NULL) + relationDescs[i++] = indexDesc; + } + + /* ---------------- + * store the relation descriptor array and number of + * descs into the result relation info. + * ---------------- + */ + resultRelationInfo->ri_NumIndices = i; + resultRelationInfo->ri_IndexRelationDescs = relationDescs; + + /* ---------------- + * store the index key information collected in our + * lists into the index info array + * ---------------- + */ + i = 0; + foreach(numkeys, nkeyList) + { + numKeyAtts = lfirsti(numkeys); + indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts; + } + + i = 0; + foreach(indexkeys, keyList) + { + indexKeyAtts = (AttrNumber *) lfirst(indexkeys); + indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts; + } + + i = 0; + foreach(indexfuncs, fiList) + { + FuncIndexInfoPtr fiP = (FuncIndexInfoPtr) lfirst(indexfuncs); + + indexInfoArray[i++]->ii_FuncIndexInfo = fiP; + } + + i = 0; + foreach(indexpreds, predList) + { + indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds); + } + /* ---------------- + * store the index info array into relation info + * ---------------- + */ + resultRelationInfo->ri_IndexRelationInfo = indexInfoArray; } + /* ---------------- - * store the index info array into relation info + * All done, resultRelationInfo now contains complete information + * on the indices associated with the result relation. * ---------------- */ - resultRelationInfo->ri_IndexRelationInfo = indexInfoArray; - } - - /* ---------------- - * All done, resultRelationInfo now contains complete information - * on the indices associated with the result relation. - * ---------------- - */ - - /* should free oidList, nkeyList and keyList here */ - /* OK - let's do it -jolly */ - freeList(oidList); - freeList(nkeyList); - freeList(keyList); - freeList(fiList); - freeList(predList); + + /* should free oidList, nkeyList and keyList here */ + /* OK - let's do it -jolly */ + freeList(oidList); + freeList(nkeyList); + freeList(keyList); + freeList(fiList); + freeList(predList); } /* ---------------------------------------------------------------- - * ExecCloseIndices + * ExecCloseIndices * - * Close the index relations stored in resultRelationInfo + * Close the index relations stored in resultRelationInfo * ---------------------------------------------------------------- */ void -ExecCloseIndices(RelationInfo *resultRelationInfo) +ExecCloseIndices(RelationInfo * resultRelationInfo) { - int i; - int numIndices; - RelationPtr relationDescs; - - numIndices = resultRelationInfo->ri_NumIndices; - relationDescs = resultRelationInfo->ri_IndexRelationDescs; - - for (i=0; i<numIndices; i++) - if (relationDescs[i] != NULL) - index_close(relationDescs[i]); - /* - * XXX should free indexInfo array here too. - */ + int i; + int numIndices; + RelationPtr relationDescs; + + numIndices = resultRelationInfo->ri_NumIndices; + relationDescs = resultRelationInfo->ri_IndexRelationDescs; + + for (i = 0; i < numIndices; i++) + if (relationDescs[i] != NULL) + index_close(relationDescs[i]); + + /* + * XXX should free indexInfo array here too. + */ } /* ---------------------------------------------------------------- - * ExecFormIndexTuple + * ExecFormIndexTuple * - * Most of this code is cannabilized from DefaultBuild(). - * As said in the comments for ExecOpenIndices, most of - * this functionality should be rearranged into a proper - * set of routines.. + * Most of this code is cannabilized from DefaultBuild(). + * As said in the comments for ExecOpenIndices, most of + * this functionality should be rearranged into a proper + * set of routines.. * ---------------------------------------------------------------- */ #ifdef NOT_USED IndexTuple ExecFormIndexTuple(HeapTuple heapTuple, - Relation heapRelation, - Relation indexRelation, - IndexInfo *indexInfo) + Relation heapRelation, + Relation indexRelation, + IndexInfo * indexInfo) { - IndexTuple indexTuple; - TupleDesc heapDescriptor; - TupleDesc indexDescriptor; - Datum *datum; - char *nulls; - - int numberOfAttributes; - AttrNumber *keyAttributeNumbers; - FuncIndexInfoPtr fInfoP; - - /* ---------------- - * get information from index info structure - * ---------------- - */ - numberOfAttributes = indexInfo->ii_NumKeyAttributes; - keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; - fInfoP = indexInfo->ii_FuncIndexInfo; - - /* ---------------- - * datum and null are arrays in which we collect the index attributes - * when forming a new index tuple. - * ---------------- - */ - CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext); - datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); - nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); - - /* ---------------- - * get the tuple descriptors from the relations so we know - * how to form the index tuples.. - * ---------------- - */ - heapDescriptor = RelationGetTupleDescriptor(heapRelation); - indexDescriptor = RelationGetTupleDescriptor(indexRelation); - - /* ---------------- - * FormIndexDatum fills in its datum and null parameters - * with attribute information taken from the given heap tuple. - * ---------------- - */ - FormIndexDatum(numberOfAttributes, /* num attributes */ - keyAttributeNumbers, /* array of att nums to extract */ - heapTuple, /* tuple from base relation */ - heapDescriptor, /* heap tuple's descriptor */ - InvalidBuffer, /* buffer associated with heap tuple */ - datum, /* return: array of attributes */ - nulls, /* return: array of char's */ - fInfoP); /* functional index information */ - - indexTuple = index_formtuple(indexDescriptor, - datum, - nulls); - - /* ---------------- - * free temporary arrays - * - * XXX should store these in the IndexInfo instead of allocating - * and freeing on every insertion, but efficency here is not - * that important and FormIndexTuple is wasteful anyways.. - * -cim 9/27/89 - * ---------------- - */ - pfree(nulls); - pfree(datum); - - return indexTuple; + IndexTuple indexTuple; + TupleDesc heapDescriptor; + TupleDesc indexDescriptor; + Datum *datum; + char *nulls; + + int numberOfAttributes; + AttrNumber *keyAttributeNumbers; + FuncIndexInfoPtr fInfoP; + + /* ---------------- + * get information from index info structure + * ---------------- + */ + numberOfAttributes = indexInfo->ii_NumKeyAttributes; + keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; + fInfoP = indexInfo->ii_FuncIndexInfo; + + /* ---------------- + * datum and null are arrays in which we collect the index attributes + * when forming a new index tuple. + * ---------------- + */ + CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext); + datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); + nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); + + /* ---------------- + * get the tuple descriptors from the relations so we know + * how to form the index tuples.. + * ---------------- + */ + heapDescriptor = RelationGetTupleDescriptor(heapRelation); + indexDescriptor = RelationGetTupleDescriptor(indexRelation); + + /* ---------------- + * FormIndexDatum fills in its datum and null parameters + * with attribute information taken from the given heap tuple. + * ---------------- + */ + FormIndexDatum(numberOfAttributes, /* num attributes */ + keyAttributeNumbers, /* array of att nums to extract */ + heapTuple, /* tuple from base relation */ + heapDescriptor, /* heap tuple's descriptor */ + InvalidBuffer, /* buffer associated with heap + * tuple */ + datum, /* return: array of attributes */ + nulls, /* return: array of char's */ + fInfoP); /* functional index information */ + + indexTuple = index_formtuple(indexDescriptor, + datum, + nulls); + + /* ---------------- + * free temporary arrays + * + * XXX should store these in the IndexInfo instead of allocating + * and freeing on every insertion, but efficency here is not + * that important and FormIndexTuple is wasteful anyways.. + * -cim 9/27/89 + * ---------------- + */ + pfree(nulls); + pfree(datum); + + return indexTuple; } + #endif /* ---------------------------------------------------------------- - * ExecInsertIndexTuples + * ExecInsertIndexTuples * - * This routine takes care of inserting index tuples - * into all the relations indexing the result relation - * when a heap tuple is inserted into the result relation. - * Much of this code should be moved into the genam - * stuff as it only exists here because the genam stuff - * doesn't provide the functionality needed by the - * executor.. -cim 9/27/89 + * This routine takes care of inserting index tuples + * into all the relations indexing the result relation + * when a heap tuple is inserted into the result relation. + * Much of this code should be moved into the genam + * stuff as it only exists here because the genam stuff + * doesn't provide the functionality needed by the + * executor.. -cim 9/27/89 * ---------------------------------------------------------------- */ void -ExecInsertIndexTuples(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate, - bool is_update) +ExecInsertIndexTuples(TupleTableSlot * slot, + ItemPointer tupleid, + EState * estate, + bool is_update) { - HeapTuple heapTuple; - RelationInfo *resultRelationInfo; - int i; - int numIndices; - RelationPtr relationDescs; - Relation heapRelation; - IndexInfo **indexInfoArray; - IndexInfo *indexInfo; - Node *predicate; - bool satisfied; - ExprContext *econtext; - InsertIndexResult result; - int numberOfAttributes; - AttrNumber *keyAttributeNumbers; - FuncIndexInfoPtr fInfoP; - TupleDesc heapDescriptor; - Datum *datum; - char *nulls; - - heapTuple = slot->val; - - /* ---------------- - * get information from the result relation info structure. - * ---------------- - */ - resultRelationInfo = estate->es_result_relation_info; - numIndices = resultRelationInfo->ri_NumIndices; - relationDescs = resultRelationInfo->ri_IndexRelationDescs; - indexInfoArray = resultRelationInfo->ri_IndexRelationInfo; - heapRelation = resultRelationInfo->ri_RelationDesc; - - /* ---------------- - * for each index, form and insert the index tuple - * ---------------- - */ - econtext = NULL; - for (i=0; i<numIndices; i++) { - if (relationDescs[i] == NULL) continue; - - indexInfo = indexInfoArray[i]; - predicate = indexInfo->ii_Predicate; - if (predicate != NULL) { - if (econtext == NULL) { - econtext = makeNode(ExprContext); - } - econtext->ecxt_scantuple = slot; - - /* Skip this index-update if the predicate isn't satisfied */ - satisfied = ExecQual((List*)predicate, econtext); - if (satisfied == false) - continue; - } + HeapTuple heapTuple; + RelationInfo *resultRelationInfo; + int i; + int numIndices; + RelationPtr relationDescs; + Relation heapRelation; + IndexInfo **indexInfoArray; + IndexInfo *indexInfo; + Node *predicate; + bool satisfied; + ExprContext *econtext; + InsertIndexResult result; + int numberOfAttributes; + AttrNumber *keyAttributeNumbers; + FuncIndexInfoPtr fInfoP; + TupleDesc heapDescriptor; + Datum *datum; + char *nulls; + + heapTuple = slot->val; /* ---------------- - * get information from index info structure + * get information from the result relation info structure. * ---------------- */ - numberOfAttributes = indexInfo->ii_NumKeyAttributes; - keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; - fInfoP = indexInfo->ii_FuncIndexInfo; - datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); - nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); - heapDescriptor = (TupleDesc)RelationGetTupleDescriptor(heapRelation); - - FormIndexDatum(numberOfAttributes, /* num attributes */ - keyAttributeNumbers, /* array of att nums to extract */ - heapTuple, /* tuple from base relation */ - heapDescriptor, /* heap tuple's descriptor */ - InvalidBuffer, /* buffer associated with heap tuple */ - datum, /* return: array of attributes */ - nulls, /* return: array of char's */ - fInfoP); /* functional index information */ - - - result = index_insert(relationDescs[i], /* index relation */ - datum, /* array of heaptuple Datums */ - nulls, /* info on nulls */ - &(heapTuple->t_ctid), /* oid of heap tuple */ - heapRelation); - - /* ---------------- - * keep track of index inserts for debugging - * ---------------- - */ - IncrIndexInserted(); - + resultRelationInfo = estate->es_result_relation_info; + numIndices = resultRelationInfo->ri_NumIndices; + relationDescs = resultRelationInfo->ri_IndexRelationDescs; + indexInfoArray = resultRelationInfo->ri_IndexRelationInfo; + heapRelation = resultRelationInfo->ri_RelationDesc; + /* ---------------- - * free index tuple after insertion + * for each index, form and insert the index tuple * ---------------- */ - if (result) pfree(result); - } - if (econtext != NULL) pfree(econtext); + econtext = NULL; + for (i = 0; i < numIndices; i++) + { + if (relationDescs[i] == NULL) + continue; + + indexInfo = indexInfoArray[i]; + predicate = indexInfo->ii_Predicate; + if (predicate != NULL) + { + if (econtext == NULL) + { + econtext = makeNode(ExprContext); + } + econtext->ecxt_scantuple = slot; + + /* Skip this index-update if the predicate isn't satisfied */ + satisfied = ExecQual((List *) predicate, econtext); + if (satisfied == false) + continue; + } + + /* ---------------- + * get information from index info structure + * ---------------- + */ + numberOfAttributes = indexInfo->ii_NumKeyAttributes; + keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers; + fInfoP = indexInfo->ii_FuncIndexInfo; + datum = (Datum *) palloc(numberOfAttributes * sizeof *datum); + nulls = (char *) palloc(numberOfAttributes * sizeof *nulls); + heapDescriptor = (TupleDesc) RelationGetTupleDescriptor(heapRelation); + + FormIndexDatum(numberOfAttributes, /* num attributes */ + keyAttributeNumbers, /* array of att nums to + * extract */ + heapTuple, /* tuple from base relation */ + heapDescriptor, /* heap tuple's descriptor */ + InvalidBuffer, /* buffer associated with heap + * tuple */ + datum, /* return: array of attributes */ + nulls, /* return: array of char's */ + fInfoP); /* functional index information */ + + + result = index_insert(relationDescs[i], /* index relation */ + datum, /* array of heaptuple Datums */ + nulls, /* info on nulls */ + &(heapTuple->t_ctid), /* oid of heap tuple */ + heapRelation); + + /* ---------------- + * keep track of index inserts for debugging + * ---------------- + */ + IncrIndexInserted(); + + /* ---------------- + * free index tuple after insertion + * ---------------- + */ + if (result) + pfree(result); + } + if (econtext != NULL) + pfree(econtext); } /* ---------------------------------------------------------------- * setVarAttrLenForCreateTable - - * called when we do a SELECT * INTO TABLE tab - * needed for attributes that have a defined length, like bpchar and - * varchar + * called when we do a SELECT * INTO TABLE tab + * needed for attributes that have a defined length, like bpchar and + * varchar * ---------------------------------------------------------------- */ void -setVarAttrLenForCreateTable(TupleDesc tupType, List *targetList, - List *rangeTable) +setVarAttrLenForCreateTable(TupleDesc tupType, List * targetList, + List * rangeTable) { - List *tl; - TargetEntry *tle; - Node *expr; - int varno; - - tl = targetList; - - for (varno = 0; varno < tupType->natts; varno++) { - tle = lfirst(tl); - - if (tupType->attrs[varno]->atttypid == BPCHAROID || - tupType->attrs[varno]->atttypid == VARCHAROID) { - expr = tle->expr; - if (expr && IsA(expr,Var)) { - Var *var; - RangeTblEntry *rtentry; - Relation rd; - - var = (Var *)expr; - rtentry = rt_fetch(var->varnoold, rangeTable); - rd = heap_open(rtentry->relid); - /* set length to that defined in relation */ - tupType->attrs[varno]->attlen = - (*rd->rd_att->attrs[var->varoattno-1]).attlen; - heap_close(rd); - } - else - elog(WARN, "setVarAttrLenForCreateTable: can't get length for variable-length field"); - } - tl = lnext(tl); - } + List *tl; + TargetEntry *tle; + Node *expr; + int varno; + + tl = targetList; + + for (varno = 0; varno < tupType->natts; varno++) + { + tle = lfirst(tl); + + if (tupType->attrs[varno]->atttypid == BPCHAROID || + tupType->attrs[varno]->atttypid == VARCHAROID) + { + expr = tle->expr; + if (expr && IsA(expr, Var)) + { + Var *var; + RangeTblEntry *rtentry; + Relation rd; + + var = (Var *) expr; + rtentry = rt_fetch(var->varnoold, rangeTable); + rd = heap_open(rtentry->relid); + /* set length to that defined in relation */ + tupType->attrs[varno]->attlen = + (*rd->rd_att->attrs[var->varoattno - 1]).attlen; + heap_close(rd); + } + else + elog(WARN, "setVarAttrLenForCreateTable: can't get length for variable-length field"); + } + tl = lnext(tl); + } } -#ifdef NOT_USED /* look at execMain.c */ +#ifdef NOT_USED /* look at execMain.c */ /* ---------------------------------------------------------------- * resetVarAttrLenForCreateTable - - * called when we do a SELECT * INTO TABLE tab - * needed for attributes that have a defined length, like bpchar and - * varchar - * resets length to -1 for those types + * called when we do a SELECT * INTO TABLE tab + * needed for attributes that have a defined length, like bpchar and + * varchar + * resets length to -1 for those types * ---------------------------------------------------------------- */ void resetVarAttrLenForCreateTable(TupleDesc tupType) { - int varno; - - for (varno = 0; varno < tupType->natts; varno++) { - if (tupType->attrs[varno]->atttypid == BPCHAROID || - tupType->attrs[varno]->atttypid == VARCHAROID) - /* set length to original -1 */ - tupType->attrs[varno]->attlen = -1; - } + int varno; + + for (varno = 0; varno < tupType->natts; varno++) + { + if (tupType->attrs[varno]->atttypid == BPCHAROID || + tupType->attrs[varno]->atttypid == VARCHAROID) + /* set length to original -1 */ + tupType->attrs[varno]->attlen = -1; + } } + #endif diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 79f8bede085..96b9b19dcb6 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * functions.c-- - * Routines to handle functions called from the executor - * Putting this stuff in fmgr makes the postmaster a mess.... + * Routines to handle functions called from the executor + * Putting this stuff in fmgr makes the postmaster a mess.... * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.7 1997/08/29 09:02:50 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.8 1997/09/07 04:41:27 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -41,401 +41,438 @@ #undef new -typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus; +typedef enum +{ + F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE +} ExecStatus; -typedef struct local_es { - QueryDesc *qd; - EState *estate; - struct local_es *next; - ExecStatus status; -} execution_state; +typedef struct local_es +{ + QueryDesc *qd; + EState *estate; + struct local_es *next; + ExecStatus status; +} execution_state; #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL) /* non-export function prototypes */ -static TupleDesc postquel_start(execution_state *es); -static execution_state *init_execution_state(FunctionCachePtr fcache, - char *args[]); -static TupleTableSlot *postquel_getnext(execution_state *es); -static void postquel_end(execution_state *es); -static void postquel_sub_params(execution_state *es, int nargs, - char *args[], bool *nullV); -static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache, - List *fTlist, char **args, bool *isNull); - +static TupleDesc postquel_start(execution_state * es); +static execution_state * +init_execution_state(FunctionCachePtr fcache, + char *args[]); +static TupleTableSlot *postquel_getnext(execution_state * es); +static void postquel_end(execution_state * es); +static void +postquel_sub_params(execution_state * es, int nargs, + char *args[], bool * nullV); +static Datum +postquel_execute(execution_state * es, FunctionCachePtr fcache, + List * fTlist, char **args, bool * isNull); + Datum ProjectAttribute(TupleDesc TD, - TargetEntry *tlist, - HeapTuple tup, - bool *isnullP) + TargetEntry * tlist, + HeapTuple tup, + bool * isnullP) { - Datum val,valueP; - Var *attrVar = (Var *)tlist->expr; - AttrNumber attrno = attrVar->varattno; - - - val = PointerGetDatum(heap_getattr(tup, - InvalidBuffer, - attrno, - TD, - isnullP)); - if (*isnullP) - return (Datum) NULL; - - valueP = datumCopy(val, - TD->attrs[attrno-1]->atttypid, - TD->attrs[attrno-1]->attbyval, - (Size) TD->attrs[attrno-1]->attlen); - return valueP; + Datum val, + valueP; + Var *attrVar = (Var *) tlist->expr; + AttrNumber attrno = attrVar->varattno; + + + val = PointerGetDatum(heap_getattr(tup, + InvalidBuffer, + attrno, + TD, + isnullP)); + if (*isnullP) + return (Datum) NULL; + + valueP = datumCopy(val, + TD->attrs[attrno - 1]->atttypid, + TD->attrs[attrno - 1]->attbyval, + (Size) TD->attrs[attrno - 1]->attlen); + return valueP; } static execution_state * init_execution_state(FunctionCachePtr fcache, - char *args[]) + char *args[]) { - execution_state *newes; - execution_state *nextes; - execution_state *preves; - QueryTreeList *queryTree_list; - int i; - List *planTree_list; - int nargs; - - nargs = fcache->nargs; - - newes = (execution_state *) palloc(sizeof(execution_state)); - nextes = newes; - preves = (execution_state *)NULL; - - - planTree_list = (List *) - pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None); - - for (i=0; i < queryTree_list->len; i++) { - EState *estate; - Query *queryTree = (Query*) (queryTree_list->qtrees[i]); - Plan *planTree = lfirst(planTree_list); - - if (!nextes) - nextes = (execution_state *) palloc(sizeof(execution_state)); - if (preves) - preves->next = nextes; - - nextes->next = NULL; - nextes->status = F_EXEC_START; - nextes->qd = CreateQueryDesc(queryTree, - planTree, - None); - estate = CreateExecutorState(); - - if (nargs > 0) { - int i; - ParamListInfo paramLI; - - paramLI = - (ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData)); - - memset(paramLI, 0, nargs*sizeof(ParamListInfoData)); - - estate->es_param_list_info = paramLI; - - for (i=0; i<nargs; paramLI++, i++) { - paramLI->kind = PARAM_NUM; - paramLI->id = i+1; - paramLI->isnull = false; - paramLI->value = (Datum) NULL; - } - paramLI->kind = PARAM_INVALID; + execution_state *newes; + execution_state *nextes; + execution_state *preves; + QueryTreeList *queryTree_list; + int i; + List *planTree_list; + int nargs; + + nargs = fcache->nargs; + + newes = (execution_state *) palloc(sizeof(execution_state)); + nextes = newes; + preves = (execution_state *) NULL; + + + planTree_list = (List *) + pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None); + + for (i = 0; i < queryTree_list->len; i++) + { + EState *estate; + Query *queryTree = (Query *) (queryTree_list->qtrees[i]); + Plan *planTree = lfirst(planTree_list); + + if (!nextes) + nextes = (execution_state *) palloc(sizeof(execution_state)); + if (preves) + preves->next = nextes; + + nextes->next = NULL; + nextes->status = F_EXEC_START; + nextes->qd = CreateQueryDesc(queryTree, + planTree, + None); + estate = CreateExecutorState(); + + if (nargs > 0) + { + int i; + ParamListInfo paramLI; + + paramLI = + (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData)); + + memset(paramLI, 0, nargs * sizeof(ParamListInfoData)); + + estate->es_param_list_info = paramLI; + + for (i = 0; i < nargs; paramLI++, i++) + { + paramLI->kind = PARAM_NUM; + paramLI->id = i + 1; + paramLI->isnull = false; + paramLI->value = (Datum) NULL; + } + paramLI->kind = PARAM_INVALID; + } + else + estate->es_param_list_info = (ParamListInfo) NULL; + nextes->estate = estate; + preves = nextes; + nextes = (execution_state *) NULL; + + planTree_list = lnext(planTree_list); } - else - estate->es_param_list_info = (ParamListInfo)NULL; - nextes->estate = estate; - preves = nextes; - nextes = (execution_state *)NULL; - - planTree_list = lnext(planTree_list); - } - - return newes; + + return newes; } -static TupleDesc -postquel_start(execution_state *es) +static TupleDesc +postquel_start(execution_state * es) { #ifdef FUNC_UTIL_PATCH - /* - * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996 - */ - if (es->qd->operation == CMD_UTILITY) { - return (TupleDesc) NULL; - } + + /* + * Do nothing for utility commands. (create, destroy...) DZ - + * 30-8-1996 + */ + if (es->qd->operation == CMD_UTILITY) + { + return (TupleDesc) NULL; + } #endif - return ExecutorStart(es->qd, es->estate); + return ExecutorStart(es->qd, es->estate); } static TupleTableSlot * -postquel_getnext(execution_state *es) +postquel_getnext(execution_state * es) { - int feature; - + int feature; + #ifdef FUNC_UTIL_PATCH - if (es->qd->operation == CMD_UTILITY) { - /* - * Process an utility command. (create, destroy...) DZ - 30-8-1996 - */ - ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest); - if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement(); - return (TupleTableSlot*) NULL; - } + if (es->qd->operation == CMD_UTILITY) + { + + /* + * Process an utility command. (create, destroy...) DZ - + * 30-8-1996 + */ + ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest); + if (!LAST_POSTQUEL_COMMAND(es)) + CommandCounterIncrement(); + return (TupleTableSlot *) NULL; + } #endif - feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; - - return ExecutorRun(es->qd, es->estate, feature, 0); + feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; + + return ExecutorRun(es->qd, es->estate, feature, 0); } static void -postquel_end(execution_state *es) +postquel_end(execution_state * es) { #ifdef FUNC_UTIL_PATCH - /* - * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996 - */ - if (es->qd->operation == CMD_UTILITY) { - return; - } + + /* + * Do nothing for utility commands. (create, destroy...) DZ - + * 30-8-1996 + */ + if (es->qd->operation == CMD_UTILITY) + { + return; + } #endif - ExecutorEnd(es->qd, es->estate); + ExecutorEnd(es->qd, es->estate); } static void -postquel_sub_params(execution_state *es, - int nargs, - char *args[], - bool *nullV) +postquel_sub_params(execution_state * es, + int nargs, + char *args[], + bool * nullV) { - ParamListInfo paramLI; - EState *estate; - - estate = es->estate; - paramLI = estate->es_param_list_info; - - while (paramLI->kind != PARAM_INVALID) { - if (paramLI->kind == PARAM_NUM) { - Assert(paramLI->id <= nargs); - paramLI->value = (Datum)args[(paramLI->id - 1)]; - paramLI->isnull = nullV[(paramLI->id - 1)]; + ParamListInfo paramLI; + EState *estate; + + estate = es->estate; + paramLI = estate->es_param_list_info; + + while (paramLI->kind != PARAM_INVALID) + { + if (paramLI->kind == PARAM_NUM) + { + Assert(paramLI->id <= nargs); + paramLI->value = (Datum) args[(paramLI->id - 1)]; + paramLI->isnull = nullV[(paramLI->id - 1)]; + } + paramLI++; } - paramLI++; - } } static TupleTableSlot * copy_function_result(FunctionCachePtr fcache, - TupleTableSlot *resultSlot) + TupleTableSlot * resultSlot) { - TupleTableSlot *funcSlot; - TupleDesc resultTd; - HeapTuple newTuple; - HeapTuple oldTuple; - - Assert(! TupIsNull(resultSlot)); - oldTuple = resultSlot->val; - - funcSlot = (TupleTableSlot*)fcache->funcSlot; - - if (funcSlot == (TupleTableSlot*)NULL) - return resultSlot; - - resultTd = resultSlot->ttc_tupleDescriptor; - /* - * When the funcSlot is NULL we have to initialize the funcSlot's - * tuple descriptor. - */ - if (TupIsNull(funcSlot)) { - int i= 0; - TupleDesc funcTd = funcSlot->ttc_tupleDescriptor; - - while (i < oldTuple->t_natts) { - funcTd->attrs[i] = - (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); - memmove(funcTd->attrs[i], - resultTd->attrs[i], - ATTRIBUTE_TUPLE_SIZE); - i++; + TupleTableSlot *funcSlot; + TupleDesc resultTd; + HeapTuple newTuple; + HeapTuple oldTuple; + + Assert(!TupIsNull(resultSlot)); + oldTuple = resultSlot->val; + + funcSlot = (TupleTableSlot *) fcache->funcSlot; + + if (funcSlot == (TupleTableSlot *) NULL) + return resultSlot; + + resultTd = resultSlot->ttc_tupleDescriptor; + + /* + * When the funcSlot is NULL we have to initialize the funcSlot's + * tuple descriptor. + */ + if (TupIsNull(funcSlot)) + { + int i = 0; + TupleDesc funcTd = funcSlot->ttc_tupleDescriptor; + + while (i < oldTuple->t_natts) + { + funcTd->attrs[i] = + (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + memmove(funcTd->attrs[i], + resultTd->attrs[i], + ATTRIBUTE_TUPLE_SIZE); + i++; + } } - } - - newTuple = heap_copytuple(oldTuple); - - return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true); + + newTuple = heap_copytuple(oldTuple); + + return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true); } -static Datum -postquel_execute(execution_state *es, - FunctionCachePtr fcache, - List *fTlist, - char **args, - bool *isNull) +static Datum +postquel_execute(execution_state * es, + FunctionCachePtr fcache, + List * fTlist, + char **args, + bool * isNull) { - TupleTableSlot *slot; - Datum value; + TupleTableSlot *slot; + Datum value; #ifdef INDEXSCAN_PATCH - /* - * It's more right place to do it (before postquel_start->ExecutorStart). - * Now ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. - * (But note: I HOPE we can do it here). - vadim 01/22/97 - */ - if (fcache->nargs > 0) - postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); + + /* + * It's more right place to do it (before + * postquel_start->ExecutorStart). Now + * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But + * note: I HOPE we can do it here). - vadim 01/22/97 + */ + if (fcache->nargs > 0) + postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); #endif - - if (es->status == F_EXEC_START) + + if (es->status == F_EXEC_START) { - postquel_start(es); - es->status = F_EXEC_RUN; + postquel_start(es); + es->status = F_EXEC_RUN; } #ifndef INDEXSCAN_PATCH - if (fcache->nargs > 0) - postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); + if (fcache->nargs > 0) + postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); #endif - - slot = postquel_getnext(es); - - if (TupIsNull(slot)) { - postquel_end(es); - es->status = F_EXEC_DONE; - *isNull = true; - /* - * If this isn't the last command for the function - * we have to increment the command - * counter so that subsequent commands can see changes made - * by previous ones. - */ - if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement(); - return (Datum)NULL; - } - - if (LAST_POSTQUEL_COMMAND(es)) { - TupleTableSlot *resSlot; - - /* - * Copy the result. copy_function_result is smart enough - * to do nothing when no action is called for. This helps - * reduce the logic and code redundancy here. - */ - resSlot = copy_function_result(fcache, slot); - if (fTlist != NIL) { - HeapTuple tup; - TargetEntry *tle = lfirst(fTlist); - - tup = resSlot->val; - value = ProjectAttribute(resSlot->ttc_tupleDescriptor, - tle, - tup, - isNull); - }else { - value = (Datum)resSlot; - *isNull = false; + + slot = postquel_getnext(es); + + if (TupIsNull(slot)) + { + postquel_end(es); + es->status = F_EXEC_DONE; + *isNull = true; + + /* + * If this isn't the last command for the function we have to + * increment the command counter so that subsequent commands can + * see changes made by previous ones. + */ + if (!LAST_POSTQUEL_COMMAND(es)) + CommandCounterIncrement(); + return (Datum) NULL; } - + + if (LAST_POSTQUEL_COMMAND(es)) + { + TupleTableSlot *resSlot; + + /* + * Copy the result. copy_function_result is smart enough to do + * nothing when no action is called for. This helps reduce the + * logic and code redundancy here. + */ + resSlot = copy_function_result(fcache, slot); + if (fTlist != NIL) + { + HeapTuple tup; + TargetEntry *tle = lfirst(fTlist); + + tup = resSlot->val; + value = ProjectAttribute(resSlot->ttc_tupleDescriptor, + tle, + tup, + isNull); + } + else + { + value = (Datum) resSlot; + *isNull = false; + } + + /* + * If this is a single valued function we have to end the function + * execution now. + */ + if (fcache->oneResult) + { + postquel_end(es); + es->status = F_EXEC_DONE; + } + + return value; + } + /* - * If this is a single valued function we have to end the - * function execution now. + * If this isn't the last command for the function, we don't return + * any results, but we have to increment the command counter so that + * subsequent commands can see changes made by previous ones. */ - if (fcache->oneResult) { - postquel_end(es); - es->status = F_EXEC_DONE; - } - - return value; - } - /* - * If this isn't the last command for the function, we don't - * return any results, but we have to increment the command - * counter so that subsequent commands can see changes made - * by previous ones. - */ - CommandCounterIncrement(); - return (Datum)NULL; + CommandCounterIncrement(); + return (Datum) NULL; } Datum -postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone) +postquel_function(Func * funcNode, char **args, bool * isNull, bool * isDone) { - execution_state *es; - Datum result = 0; - FunctionCachePtr fcache = funcNode->func_fcache; - CommandId savedId; - - /* - * Before we start do anything we must save CurrentScanCommandId - * to restore it before return to upper Executor. Also, we have to - * set CurrentScanCommandId equal to CurrentCommandId. - * - vadim 08/29/97 - */ - savedId = GetScanCommandId (); - SetScanCommandId (GetCurrentCommandId ()); - - es = (execution_state *) fcache->func_state; - if (es == NULL) + execution_state *es; + Datum result = 0; + FunctionCachePtr fcache = funcNode->func_fcache; + CommandId savedId; + + /* + * Before we start do anything we must save CurrentScanCommandId to + * restore it before return to upper Executor. Also, we have to set + * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97 + */ + savedId = GetScanCommandId(); + SetScanCommandId(GetCurrentCommandId()); + + es = (execution_state *) fcache->func_state; + if (es == NULL) { - es = init_execution_state(fcache, args); - fcache->func_state = (char *) es; + es = init_execution_state(fcache, args); + fcache->func_state = (char *) es; } - - while (es && es->status == F_EXEC_DONE) - es = es->next; - - Assert(es); - /* - * Execute each command in the function one after another until we're - * executing the final command and get a result or we run out of - * commands. - */ - while (es != (execution_state *)NULL) + + while (es && es->status == F_EXEC_DONE) + es = es->next; + + Assert(es); + + /* + * Execute each command in the function one after another until we're + * executing the final command and get a result or we run out of + * commands. + */ + while (es != (execution_state *) NULL) { - result = postquel_execute(es, - fcache, - funcNode->func_tlist, - args, - isNull); - if (es->status != F_EXEC_DONE) - break; - es = es->next; + result = postquel_execute(es, + fcache, + funcNode->func_tlist, + args, + isNull); + if (es->status != F_EXEC_DONE) + break; + es = es->next; } - - /* - * If we've gone through every command in this function, we are done. - */ - if (es == (execution_state *)NULL) - { + /* - * Reset the execution states to start over again + * If we've gone through every command in this function, we are done. */ - es = (execution_state *)fcache->func_state; - while (es) + if (es == (execution_state *) NULL) { - es->status = F_EXEC_START; - es = es->next; + + /* + * Reset the execution states to start over again + */ + es = (execution_state *) fcache->func_state; + while (es) + { + es->status = F_EXEC_START; + es = es->next; + } + + /* + * Let caller know we're finished. + */ + *isDone = true; + SetScanCommandId(savedId); + return (fcache->oneResult) ? result : (Datum) NULL; } + /* - * Let caller know we're finished. + * If we got a result from a command within the function it has to be + * the final command. All others shouldn't be returing anything. */ - *isDone = true; - SetScanCommandId (savedId); - return (fcache->oneResult) ? result : (Datum)NULL; - } - /* - * If we got a result from a command within the function it has - * to be the final command. All others shouldn't be returing - * anything. - */ - Assert ( LAST_POSTQUEL_COMMAND(es) ); - *isDone = false; - - SetScanCommandId (savedId); - return result; + Assert(LAST_POSTQUEL_COMMAND(es)); + *isDone = false; + + SetScanCommandId(savedId); + return result; } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index c6e5b269cde..ee03f6854d9 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * nodeAgg.c-- - * Routines to handle aggregate nodes. + * Routines to handle aggregate nodes. * * Copyright (c) 1994, Regents of the University of California * * * NOTE - * The implementation of Agg node has been reworked to handle legal - * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95 + * The implementation of Agg node has been reworked to handle legal + * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95 * * IDENTIFICATION - * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp + * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp * *------------------------------------------------------------------------- */ @@ -32,570 +32,625 @@ /* * AggFuncInfo - - * keeps the transition functions information around + * keeps the transition functions information around */ -typedef struct AggFuncInfo { - Oid xfn1_oid; - Oid xfn2_oid; - Oid finalfn_oid; - func_ptr xfn1; - func_ptr xfn2; - func_ptr finalfn; - int xfn1_nargs; - int xfn2_nargs; - int finalfn_nargs; -} AggFuncInfo; +typedef struct AggFuncInfo +{ + Oid xfn1_oid; + Oid xfn2_oid; + Oid finalfn_oid; + func_ptr xfn1; + func_ptr xfn2; + func_ptr finalfn; + int xfn1_nargs; + int xfn2_nargs; + int finalfn_nargs; +} AggFuncInfo; -static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull); +static Datum aggGetAttr(TupleTableSlot * tuple, Aggreg * agg, bool * isNull); /* --------------------------------------- * * ExecAgg - * - * ExecAgg receives tuples from its outer subplan and aggregates over - * the appropriate attribute for each (unique) aggregate in the target - * list. (The number of tuples to aggregate over depends on whether a - * GROUP BY clause is present. It might be the number of tuples in a - * group or all the tuples that satisfy the qualifications.) The value of - * each aggregate is stored in the expression context for ExecProject to - * evaluate the result tuple. - * - * ExecAgg evaluates each aggregate in the following steps: (initcond1, - * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are - * the transition functions.) + * ExecAgg receives tuples from its outer subplan and aggregates over + * the appropriate attribute for each (unique) aggregate in the target + * list. (The number of tuples to aggregate over depends on whether a + * GROUP BY clause is present. It might be the number of tuples in a + * group or all the tuples that satisfy the qualifications.) The value of + * each aggregate is stored in the expression context for ExecProject to + * evaluate the result tuple. + * + * ExecAgg evaluates each aggregate in the following steps: (initcond1, + * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are + * the transition functions.) * - * value1[i] = initcond1 - * value2[i] = initcond2 - * forall tuples do - * value1[i] = sfunc1(aggregate_attribute, value1[i]) - * value2[i] = sfunc2(value2[i]) - * value1[i] = finalfunc(value1[i], value2[i]) + * value1[i] = initcond1 + * value2[i] = initcond2 + * forall tuples do + * value1[i] = sfunc1(aggregate_attribute, value1[i]) + * value2[i] = sfunc2(value2[i]) + * value1[i] = finalfunc(value1[i], value2[i]) * - * If the outer subplan is a Group node, ExecAgg returns as many tuples - * as there are groups. + * If the outer subplan is a Group node, ExecAgg returns as many tuples + * as there are groups. * - * XXX handling of NULL doesn't work + * XXX handling of NULL doesn't work * - * OLD COMMENTS + * OLD COMMENTS * - * XXX Aggregates should probably have another option: what to do - * with transfn2 if we hit a null value. "count" (transfn1 = null, - * transfn2 = increment) will want to have transfn2 called; "avg" - * (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93 + * XXX Aggregates should probably have another option: what to do + * with transfn2 if we hit a null value. "count" (transfn1 = null, + * transfn2 = increment) will want to have transfn2 called; "avg" + * (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93 * * ------------------------------------------ */ TupleTableSlot * -ExecAgg(Agg *node) +ExecAgg(Agg * node) { - AggState *aggstate; - EState *estate; - Aggreg **aggregates; - Plan *outerPlan; - int i, nagg; - Datum *value1, *value2; - int *noInitValue; - AggFuncInfo *aggFuncInfo; - long nTuplesAgged = 0; - ExprContext *econtext; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; - HeapTuple oneTuple; - char* nulls; - bool isDone; - bool isNull = FALSE, isNull1 = FALSE, isNull2 = FALSE; - - /* --------------------- - * get state info from node - * --------------------- - */ - aggstate = node->aggstate; - if (aggstate->agg_done) - return NULL; - - estate = node->plan.state; - econtext = aggstate->csstate.cstate.cs_ExprContext; - aggregates = node->aggs; - nagg = node->numAgg; - - value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values; - nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls; - - value2 = (Datum *)palloc(sizeof(Datum) * nagg); - memset(value2, 0, sizeof(Datum) * nagg); - - aggFuncInfo = (AggFuncInfo *)palloc(sizeof(AggFuncInfo) * nagg); - memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg); - - noInitValue = (int *)palloc(sizeof(int) * nagg); - memset(noInitValue, 0, sizeof(noInitValue) * nagg); - - outerPlan = outerPlan(node); - oneTuple = NULL; - - projInfo = aggstate->csstate.cstate.cs_ProjInfo; - - for(i = 0; i < nagg; i++) { - Aggreg *agg; - char *aggname; - HeapTuple aggTuple; - Form_pg_aggregate aggp; - Oid xfn1_oid, xfn2_oid, finalfn_oid; - func_ptr xfn1_ptr, xfn2_ptr, finalfn_ptr; - int xfn1_nargs, xfn2_nargs, finalfn_nargs; - - agg = aggregates[i]; + AggState *aggstate; + EState *estate; + Aggreg **aggregates; + Plan *outerPlan; + int i, + nagg; + Datum *value1, + *value2; + int *noInitValue; + AggFuncInfo *aggFuncInfo; + long nTuplesAgged = 0; + ExprContext *econtext; + ProjectionInfo *projInfo; + TupleTableSlot *resultSlot; + HeapTuple oneTuple; + char *nulls; + bool isDone; + bool isNull = FALSE, + isNull1 = FALSE, + isNull2 = FALSE; /* --------------------- - * find transfer functions of all the aggregates and initialize - * their initial values + * get state info from node * --------------------- */ - aggname = agg->aggname; - aggTuple = SearchSysCacheTuple(AGGNAME, - PointerGetDatum(aggname), - ObjectIdGetDatum(agg->basetype), - 0,0); - if (!HeapTupleIsValid(aggTuple)) - elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)", - aggname, - tname(get_id_type(agg->basetype))); - aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple); - - xfn1_oid = aggp->aggtransfn1; - xfn2_oid = aggp->aggtransfn2; - finalfn_oid = aggp->aggfinalfn; - - if (OidIsValid(finalfn_oid)) { - fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs); - aggFuncInfo[i].finalfn_oid = finalfn_oid; - aggFuncInfo[i].finalfn = finalfn_ptr; - aggFuncInfo[i].finalfn_nargs = finalfn_nargs; - } + aggstate = node->aggstate; + if (aggstate->agg_done) + return NULL; + + estate = node->plan.state; + econtext = aggstate->csstate.cstate.cs_ExprContext; + aggregates = node->aggs; + nagg = node->numAgg; + + value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values; + nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls; + + value2 = (Datum *) palloc(sizeof(Datum) * nagg); + memset(value2, 0, sizeof(Datum) * nagg); + + aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * nagg); + memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg); + + noInitValue = (int *) palloc(sizeof(int) * nagg); + memset(noInitValue, 0, sizeof(noInitValue) * nagg); + + outerPlan = outerPlan(node); + oneTuple = NULL; + + projInfo = aggstate->csstate.cstate.cs_ProjInfo; + + for (i = 0; i < nagg; i++) + { + Aggreg *agg; + char *aggname; + HeapTuple aggTuple; + Form_pg_aggregate aggp; + Oid xfn1_oid, + xfn2_oid, + finalfn_oid; + func_ptr xfn1_ptr, + xfn2_ptr, + finalfn_ptr; + int xfn1_nargs, + xfn2_nargs, + finalfn_nargs; + + agg = aggregates[i]; + + /* --------------------- + * find transfer functions of all the aggregates and initialize + * their initial values + * --------------------- + */ + aggname = agg->aggname; + aggTuple = SearchSysCacheTuple(AGGNAME, + PointerGetDatum(aggname), + ObjectIdGetDatum(agg->basetype), + 0, 0); + if (!HeapTupleIsValid(aggTuple)) + elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)", + aggname, + tname(get_id_type(agg->basetype))); + aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple); + + xfn1_oid = aggp->aggtransfn1; + xfn2_oid = aggp->aggtransfn2; + finalfn_oid = aggp->aggfinalfn; + + if (OidIsValid(finalfn_oid)) + { + fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs); + aggFuncInfo[i].finalfn_oid = finalfn_oid; + aggFuncInfo[i].finalfn = finalfn_ptr; + aggFuncInfo[i].finalfn_nargs = finalfn_nargs; + } - if (OidIsValid(xfn2_oid)) { - fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs); - aggFuncInfo[i].xfn2_oid = xfn2_oid; - aggFuncInfo[i].xfn2 = xfn2_ptr; - aggFuncInfo[i].xfn2_nargs = xfn2_nargs; - value2[i] = (Datum)AggNameGetInitVal((char*)aggname, - aggp->aggbasetype, - 2, - &isNull2); - /* ------------------------------------------ - * If there is a second transition function, its initial - * value must exist -- as it does not depend on data values, - * we have no other way of determining an initial value. - * ------------------------------------------ - */ - if (isNull2) - elog(WARN, "ExecAgg: agginitval2 is null"); - } + if (OidIsValid(xfn2_oid)) + { + fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs); + aggFuncInfo[i].xfn2_oid = xfn2_oid; + aggFuncInfo[i].xfn2 = xfn2_ptr; + aggFuncInfo[i].xfn2_nargs = xfn2_nargs; + value2[i] = (Datum) AggNameGetInitVal((char *) aggname, + aggp->aggbasetype, + 2, + &isNull2); + /* ------------------------------------------ + * If there is a second transition function, its initial + * value must exist -- as it does not depend on data values, + * we have no other way of determining an initial value. + * ------------------------------------------ + */ + if (isNull2) + elog(WARN, "ExecAgg: agginitval2 is null"); + } - if (OidIsValid(xfn1_oid)) { - fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs); - aggFuncInfo[i].xfn1_oid = xfn1_oid; - aggFuncInfo[i].xfn1 = xfn1_ptr; - aggFuncInfo[i].xfn1_nargs = xfn1_nargs; - value1[i] = (Datum)AggNameGetInitVal((char*)aggname, - aggp->aggbasetype, - 1, - &isNull1); - - /* ------------------------------------------ - * If the initial value for the first transition function - * doesn't exist in the pg_aggregate table then we let - * the first value returned from the outer procNode become - * the initial value. (This is useful for aggregates like - * max{} and min{}.) - * ------------------------------------------ - */ - if (isNull1) { - noInitValue[i] = 1; - nulls[i] = 1; - } - } - } - - /* ---------------- - * for each tuple from the the outer plan, apply all the aggregates - * ---------------- - */ - for (;;) { - HeapTuple outerTuple = NULL; - TupleTableSlot *outerslot; - - isNull = isNull1 = isNull2 = 0; - outerslot = ExecProcNode(outerPlan, (Plan*)node); - if (outerslot) outerTuple = outerslot->val; - if (!HeapTupleIsValid(outerTuple)) { - /* when the outerplan doesn't return a single tuple, - create a dummy heaptuple anyway - because we still need to return a valid aggregate value. - The value returned will be the initial values of the - transition functions */ - if (nTuplesAgged == 0) { - TupleDesc tupType; - Datum *tupValue; - char* null_array; - - tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor; - tupValue = projInfo->pi_tupValue; - - /* initially, set all the values to NULL */ - null_array = malloc(tupType->natts); - for (i=0;i<tupType->natts;i++) - null_array[i] = 'n'; - oneTuple = heap_formtuple(tupType, tupValue, null_array); - free(null_array); - } - break; + if (OidIsValid(xfn1_oid)) + { + fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs); + aggFuncInfo[i].xfn1_oid = xfn1_oid; + aggFuncInfo[i].xfn1 = xfn1_ptr; + aggFuncInfo[i].xfn1_nargs = xfn1_nargs; + value1[i] = (Datum) AggNameGetInitVal((char *) aggname, + aggp->aggbasetype, + 1, + &isNull1); + + /* ------------------------------------------ + * If the initial value for the first transition function + * doesn't exist in the pg_aggregate table then we let + * the first value returned from the outer procNode become + * the initial value. (This is useful for aggregates like + * max{} and min{}.) + * ------------------------------------------ + */ + if (isNull1) + { + noInitValue[i] = 1; + nulls[i] = 1; + } + } } - for(i = 0; i < nagg; i++) { - AttrNumber attnum; - int2 attlen; - Datum newVal = (Datum) NULL; - AggFuncInfo *aggfns = &aggFuncInfo[i]; - Datum args[2]; - Node *tagnode = NULL; - - switch(nodeTag(aggregates[i]->target)) - { - case T_Var: - tagnode = NULL; - newVal = aggGetAttr(outerslot, - aggregates[i], - &isNull); - break; - case T_Expr: - tagnode = ((Expr*)aggregates[i]->target)->oper; - econtext->ecxt_scantuple = outerslot; - newVal = ExecEvalExpr (aggregates[i]->target, econtext, - &isNull, NULL); + /* ---------------- + * for each tuple from the the outer plan, apply all the aggregates + * ---------------- + */ + for (;;) + { + HeapTuple outerTuple = NULL; + TupleTableSlot *outerslot; + + isNull = isNull1 = isNull2 = 0; + outerslot = ExecProcNode(outerPlan, (Plan *) node); + if (outerslot) + outerTuple = outerslot->val; + if (!HeapTupleIsValid(outerTuple)) + { + + /* + * when the outerplan doesn't return a single tuple, create a + * dummy heaptuple anyway because we still need to return a + * valid aggregate value. The value returned will be the + * initial values of the transition functions + */ + if (nTuplesAgged == 0) + { + TupleDesc tupType; + Datum *tupValue; + char *null_array; + + tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor; + tupValue = projInfo->pi_tupValue; + + /* initially, set all the values to NULL */ + null_array = malloc(tupType->natts); + for (i = 0; i < tupType->natts; i++) + null_array[i] = 'n'; + oneTuple = heap_formtuple(tupType, tupValue, null_array); + free(null_array); + } break; - default: - elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i); - } - - if (isNull) - continue; /* ignore this tuple for this agg */ - - if (aggfns->xfn1) { - if (noInitValue[i]) { - int byVal; - - /* - * value1 and value2 has not been initialized. This - * is the first non-NULL value. We use it as the - * initial value. - */ - /* but we can't just use it straight, we have - to make a copy of it since the tuple from which - it came will be freed on the next iteration - of the scan */ - if ( tagnode != NULL ) - { - FunctionCachePtr fcache_ptr; - - if ( nodeTag(tagnode) == T_Func ) - fcache_ptr = ((Func*)tagnode)->func_fcache; - else - fcache_ptr = ((Oper*)tagnode)->op_fcache; - attlen = fcache_ptr->typlen; - byVal = fcache_ptr->typbyval; - } - else - { - attnum = ((Var*)aggregates[i]->target)->varattno; - attlen = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attlen; - byVal = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attbyval; - } - if (attlen == -1) { - /* variable length */ - attlen = VARSIZE((struct varlena*) newVal); - } - value1[i] = (Datum)palloc(attlen); - if ( byVal ) - value1[i] = newVal; - else - memmove((char*)(value1[i]), (char*)newVal, attlen); - /* value1[i] = newVal; */ - noInitValue[i] = 0; - nulls[i] = 0; - } else { - /* - * apply the transition functions. - */ - args[0] = value1[i]; - args[1] = newVal; - value1[i] = - (Datum)fmgr_c(aggfns->xfn1, aggfns->xfn1_oid, - aggfns->xfn1_nargs, (FmgrValues *)args, - &isNull1); - Assert(!isNull1); } - } - - if (aggfns->xfn2) { - Datum xfn2_val = value2[i]; - - value2[i] = - (Datum)fmgr_c(aggfns->xfn2, aggfns->xfn2_oid, - aggfns->xfn2_nargs, - (FmgrValues *)&xfn2_val, &isNull2); - Assert(!isNull2); - } + + for (i = 0; i < nagg; i++) + { + AttrNumber attnum; + int2 attlen; + Datum newVal = (Datum) NULL; + AggFuncInfo *aggfns = &aggFuncInfo[i]; + Datum args[2]; + Node *tagnode = NULL; + + switch (nodeTag(aggregates[i]->target)) + { + case T_Var: + tagnode = NULL; + newVal = aggGetAttr(outerslot, + aggregates[i], + &isNull); + break; + case T_Expr: + tagnode = ((Expr *) aggregates[i]->target)->oper; + econtext->ecxt_scantuple = outerslot; + newVal = ExecEvalExpr(aggregates[i]->target, econtext, + &isNull, NULL); + break; + default: + elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i); + } + + if (isNull) + continue; /* ignore this tuple for this agg */ + + if (aggfns->xfn1) + { + if (noInitValue[i]) + { + int byVal; + + /* + * value1 and value2 has not been initialized. This is + * the first non-NULL value. We use it as the initial + * value. + */ + + /* + * but we can't just use it straight, we have to make + * a copy of it since the tuple from which it came + * will be freed on the next iteration of the scan + */ + if (tagnode != NULL) + { + FunctionCachePtr fcache_ptr; + + if (nodeTag(tagnode) == T_Func) + fcache_ptr = ((Func *) tagnode)->func_fcache; + else + fcache_ptr = ((Oper *) tagnode)->op_fcache; + attlen = fcache_ptr->typlen; + byVal = fcache_ptr->typbyval; + } + else + { + attnum = ((Var *) aggregates[i]->target)->varattno; + attlen = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attlen; + byVal = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attbyval; + } + if (attlen == -1) + { + /* variable length */ + attlen = VARSIZE((struct varlena *) newVal); + } + value1[i] = (Datum) palloc(attlen); + if (byVal) + value1[i] = newVal; + else + memmove((char *) (value1[i]), (char *) newVal, attlen); + /* value1[i] = newVal; */ + noInitValue[i] = 0; + nulls[i] = 0; + } + else + { + + /* + * apply the transition functions. + */ + args[0] = value1[i]; + args[1] = newVal; + value1[i] = + (Datum) fmgr_c(aggfns->xfn1, aggfns->xfn1_oid, + aggfns->xfn1_nargs, (FmgrValues *) args, + &isNull1); + Assert(!isNull1); + } + } + + if (aggfns->xfn2) + { + Datum xfn2_val = value2[i]; + + value2[i] = + (Datum) fmgr_c(aggfns->xfn2, aggfns->xfn2_oid, + aggfns->xfn2_nargs, + (FmgrValues *) & xfn2_val, &isNull2); + Assert(!isNull2); + } + } + + /* + * keep this for the projection (we only need one of these - all + * the tuples we aggregate over share the same group column) + */ + if (!oneTuple) + { + oneTuple = heap_copytuple(outerslot->val); + } + + nTuplesAgged++; + } + + /* -------------- + * finalize the aggregate (if necessary), and get the resultant value + * -------------- + */ + for (i = 0; i < nagg; i++) + { + char *args[2]; + AggFuncInfo *aggfns = &aggFuncInfo[i]; + + if (noInitValue[i]) + { + + /* + * No values found for this agg; return current state. This + * seems to fix behavior for avg() aggregate. -tgl 12/96 + */ + } + else if (aggfns->finalfn && nTuplesAgged > 0) + { + if (aggfns->finalfn_nargs > 1) + { + args[0] = (char *) value1[i]; + args[1] = (char *) value2[i]; + } + else if (aggfns->xfn1) + { + args[0] = (char *) value1[i]; + } + else if (aggfns->xfn2) + { + args[0] = (char *) value2[i]; + } + else + elog(WARN, "ExecAgg: no valid transition functions??"); + value1[i] = + (Datum) fmgr_c(aggfns->finalfn, aggfns->finalfn_oid, + aggfns->finalfn_nargs, (FmgrValues *) args, + &(nulls[i])); + } + else if (aggfns->xfn1) + { + + /* + * value in the right place, ignore. (If you remove this case, + * fix the else part. -ay 2/95) + */ + } + else if (aggfns->xfn2) + { + value1[i] = value2[i]; + } + else + elog(WARN, "ExecAgg: no valid transition functions??"); } /* - * keep this for the projection (we only need one of these - - * all the tuples we aggregate over share the same group column) + * whether the aggregation is done depends on whether we are doing + * aggregation over groups or the entire table */ - if (!oneTuple) { - oneTuple = heap_copytuple(outerslot->val); + if (nodeTag(outerPlan) == T_Group) + { + /* aggregation over groups */ + aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done; + } + else + { + aggstate->agg_done = TRUE; } - nTuplesAgged++; - } - - /* -------------- - * finalize the aggregate (if necessary), and get the resultant value - * -------------- - */ - for(i = 0; i < nagg; i++) { - char *args[2]; - AggFuncInfo *aggfns = &aggFuncInfo[i]; - - if (noInitValue[i]) { - /* - * No values found for this agg; return current state. - * This seems to fix behavior for avg() aggregate. -tgl 12/96 - */ - } else if (aggfns->finalfn && nTuplesAgged > 0) { - if (aggfns->finalfn_nargs > 1) { - args[0] = (char*)value1[i]; - args[1] = (char*)value2[i]; - } else if (aggfns->xfn1) { - args[0] = (char*)value1[i]; - } else if (aggfns->xfn2) { - args[0] = (char*)value2[i]; - } else - elog(WARN, "ExecAgg: no valid transition functions??"); - value1[i] = - (Datum)fmgr_c(aggfns->finalfn, aggfns->finalfn_oid, - aggfns->finalfn_nargs, (FmgrValues *) args, - &(nulls[i])); - } else if (aggfns->xfn1) { - /* - * value in the right place, ignore. (If you remove this - * case, fix the else part. -ay 2/95) - */ - } else if (aggfns->xfn2) { - value1[i] = value2[i]; - } else - elog(WARN, "ExecAgg: no valid transition functions??"); - } - - /* - * whether the aggregation is done depends on whether we are doing - * aggregation over groups or the entire table - */ - if (nodeTag(outerPlan)==T_Group) { - /* aggregation over groups */ - aggstate->agg_done = ((Group*)outerPlan)->grpstate->grp_done; - } else { - aggstate->agg_done = TRUE; - } - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - ExecStoreTuple(oneTuple, - aggstate->csstate.css_ScanTupleSlot, - InvalidBuffer, - false); - econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot; - resultSlot = ExecProject(projInfo, &isDone); - - if (oneTuple) - pfree(oneTuple); - - return resultSlot; + /* ---------------- + * form a projection tuple, store it in the result tuple + * slot and return it. + * ---------------- + */ + ExecStoreTuple(oneTuple, + aggstate->csstate.css_ScanTupleSlot, + InvalidBuffer, + false); + econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot; + resultSlot = ExecProject(projInfo, &isDone); + + if (oneTuple) + pfree(oneTuple); + + return resultSlot; } /* ----------------- * ExecInitAgg * - * Creates the run-time information for the agg node produced by the - * planner and initializes its outer subtree + * Creates the run-time information for the agg node produced by the + * planner and initializes its outer subtree * ----------------- */ bool -ExecInitAgg(Agg *node, EState *estate, Plan *parent) +ExecInitAgg(Agg * node, EState * estate, Plan * parent) { - AggState *aggstate; - Plan *outerPlan; - ExprContext *econtext; - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create state structure - */ - aggstate = makeNode(AggState); - node->aggstate = aggstate; - aggstate->agg_done = FALSE; - - /* - * assign node's base id and create expression context - */ - ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, - (Plan*) parent); - ExecAssignExprContext(estate, &aggstate->csstate.cstate); - + AggState *aggstate; + Plan *outerPlan; + ExprContext *econtext; + + /* + * assign the node's execution state + */ + node->plan.state = estate; + + /* + * create state structure + */ + aggstate = makeNode(AggState); + node->aggstate = aggstate; + aggstate->agg_done = FALSE; + + /* + * assign node's base id and create expression context + */ + ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, + (Plan *) parent); + ExecAssignExprContext(estate, &aggstate->csstate.cstate); + #define AGG_NSLOTS 2 - /* - * tuple table initialization - */ - ExecInitScanTupleSlot(estate, &aggstate->csstate); - ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate); - - econtext = aggstate->csstate.cstate.cs_ExprContext; - econtext->ecxt_values = - (Datum *)palloc(sizeof(Datum) * node->numAgg); - memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg); - econtext->ecxt_nulls = (char *)palloc(node->numAgg); - memset(econtext->ecxt_nulls, 0, node->numAgg); - - /* - * initializes child nodes - */ - outerPlan = outerPlan(node); - ExecInitNode(outerPlan, estate, (Plan *)node); - - /* ---------------- - * initialize tuple type. - * ---------------- - */ - ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate); - - /* - * Initialize tuple type for both result and scan. - * This node does no projection - */ - ExecAssignResultTypeFromTL((Plan*) node, &aggstate->csstate.cstate); - ExecAssignProjectionInfo((Plan*)node, &aggstate->csstate.cstate); - - return TRUE; + + /* + * tuple table initialization + */ + ExecInitScanTupleSlot(estate, &aggstate->csstate); + ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate); + + econtext = aggstate->csstate.cstate.cs_ExprContext; + econtext->ecxt_values = + (Datum *) palloc(sizeof(Datum) * node->numAgg); + memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg); + econtext->ecxt_nulls = (char *) palloc(node->numAgg); + memset(econtext->ecxt_nulls, 0, node->numAgg); + + /* + * initializes child nodes + */ + outerPlan = outerPlan(node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize tuple type. + * ---------------- + */ + ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate); + + /* + * Initialize tuple type for both result and scan. This node does no + * projection + */ + ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate); + ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate); + + return TRUE; } int -ExecCountSlotsAgg(Agg *node) +ExecCountSlotsAgg(Agg * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - AGG_NSLOTS; + AGG_NSLOTS; } /* ------------------------ - * ExecEndAgg(node) + * ExecEndAgg(node) * * ----------------------- */ void -ExecEndAgg(Agg *node) +ExecEndAgg(Agg * node) { - AggState *aggstate; - Plan *outerPlan; + AggState *aggstate; + Plan *outerPlan; - aggstate = node->aggstate; + aggstate = node->aggstate; - ExecFreeProjectionInfo(&aggstate->csstate.cstate); + ExecFreeProjectionInfo(&aggstate->csstate.cstate); - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan*)node); - - /* clean up tuple table */ - ExecClearTuple(aggstate->csstate.css_ScanTupleSlot); + outerPlan = outerPlan(node); + ExecEndNode(outerPlan, (Plan *) node); + + /* clean up tuple table */ + ExecClearTuple(aggstate->csstate.css_ScanTupleSlot); } /***************************************************************************** - * Support Routines + * Support Routines *****************************************************************************/ /* * aggGetAttr - - * get the attribute (specified in the Var node in agg) to aggregate - * over from the tuple + * get the attribute (specified in the Var node in agg) to aggregate + * over from the tuple */ -static Datum -aggGetAttr(TupleTableSlot *slot, - Aggreg *agg, - bool *isNull) +static Datum +aggGetAttr(TupleTableSlot * slot, + Aggreg * agg, + bool * isNull) { - Datum result; - AttrNumber attnum; - HeapTuple heapTuple; - TupleDesc tuple_type; - Buffer buffer; - - /* ---------------- - * extract tuple information from the slot - * ---------------- - */ - heapTuple = slot->val; - tuple_type = slot->ttc_tupleDescriptor; - buffer = slot->ttc_buffer; - - attnum = ((Var*)agg->target)->varattno; - - /* - * If the attribute number is invalid, then we are supposed to - * return the entire tuple, we give back a whole slot so that - * callers know what the tuple looks like. - */ - if (attnum == InvalidAttrNumber) { - TupleTableSlot *tempSlot; - TupleDesc td; - HeapTuple tup; - - tempSlot = makeNode(TupleTableSlot); - tempSlot->ttc_shouldFree = false; - tempSlot->ttc_descIsNew = true; - tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL, - tempSlot->ttc_buffer = InvalidBuffer; - tempSlot->ttc_whichplan = -1; - - tup = heap_copytuple(slot->val); - td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); - - ExecSetSlotDescriptor(tempSlot, td); - - ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); - return (Datum) tempSlot; - } - - result = (Datum) - heap_getattr(heapTuple, /* tuple containing attribute */ - buffer, /* buffer associated with tuple */ - attnum, /* attribute number of desired attribute */ - tuple_type, /* tuple descriptor of tuple */ - isNull); /* return: is attribute null? */ - - /* ---------------- - * return null if att is null - * ---------------- - */ - if (*isNull) - return (Datum) NULL; - - return result; + Datum result; + AttrNumber attnum; + HeapTuple heapTuple; + TupleDesc tuple_type; + Buffer buffer; + + /* ---------------- + * extract tuple information from the slot + * ---------------- + */ + heapTuple = slot->val; + tuple_type = slot->ttc_tupleDescriptor; + buffer = slot->ttc_buffer; + + attnum = ((Var *) agg->target)->varattno; + + /* + * If the attribute number is invalid, then we are supposed to return + * the entire tuple, we give back a whole slot so that callers know + * what the tuple looks like. + */ + if (attnum == InvalidAttrNumber) + { + TupleTableSlot *tempSlot; + TupleDesc td; + HeapTuple tup; + + tempSlot = makeNode(TupleTableSlot); + tempSlot->ttc_shouldFree = false; + tempSlot->ttc_descIsNew = true; + tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL, + tempSlot->ttc_buffer = InvalidBuffer; + tempSlot->ttc_whichplan = -1; + + tup = heap_copytuple(slot->val); + td = CreateTupleDescCopy(slot->ttc_tupleDescriptor); + + ExecSetSlotDescriptor(tempSlot, td); + + ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); + return (Datum) tempSlot; + } + + result = (Datum) + heap_getattr(heapTuple, /* tuple containing attribute */ + buffer, /* buffer associated with tuple */ + attnum, /* attribute number of desired attribute */ + tuple_type,/* tuple descriptor of tuple */ + isNull); /* return: is attribute null? */ + + /* ---------------- + * return null if att is null + * ---------------- + */ + if (*isNull) + return (Datum) NULL; + + return result; } diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 7abc6d91744..043ad5d9743 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -1,56 +1,56 @@ /*------------------------------------------------------------------------- * * nodeAppend.c-- - * routines to handle append nodes. + * routines to handle append nodes. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.5 1997/08/19 21:31:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.6 1997/09/07 04:41:30 momjian Exp $ * *------------------------------------------------------------------------- */ /* INTERFACE ROUTINES - * ExecInitAppend - initialize the append node - * ExecProcAppend - retrieve the next tuple from the node - * ExecEndAppend - shut down the append node + * ExecInitAppend - initialize the append node + * ExecProcAppend - retrieve the next tuple from the node + * ExecEndAppend - shut down the append node * - * NOTES - * Each append node contains a list of one or more subplans which - * must be iteratively processed (forwards or backwards). - * Tuples are retrieved by executing the 'whichplan'th subplan - * until the subplan stops returning tuples, at which point that - * plan is shut down and the next started up. + * NOTES + * Each append node contains a list of one or more subplans which + * must be iteratively processed (forwards or backwards). + * Tuples are retrieved by executing the 'whichplan'th subplan + * until the subplan stops returning tuples, at which point that + * plan is shut down and the next started up. * - * Append nodes don't make use of their left and right - * subtrees, rather they maintain a list of subplans so - * a typical append node looks like this in the plan tree: + * Append nodes don't make use of their left and right + * subtrees, rather they maintain a list of subplans so + * a typical append node looks like this in the plan tree: * - * ... - * / - * Append -------+------+------+--- nil - * / \ | | | - * nil nil ... ... ... - * subplans + * ... + * / + * Append -------+------+------+--- nil + * / \ | | | + * nil nil ... ... ... + * subplans * - * Append nodes are currently used to support inheritance - * queries, where several relations need to be scanned. - * For example, in our standard person/student/employee/student-emp - * example, where student and employee inherit from person - * and student-emp inherits from student and employee, the - * query: + * Append nodes are currently used to support inheritance + * queries, where several relations need to be scanned. + * For example, in our standard person/student/employee/student-emp + * example, where student and employee inherit from person + * and student-emp inherits from student and employee, the + * query: * - * retrieve (e.name) from e in person* + * retrieve (e.name) from e in person* * - * generates the plan: + * generates the plan: * - * | - * Append -------+-------+--------+--------+ - * / \ | | | | - * nil nil Scan Scan Scan Scan - * | | | | - * person employee student student-emp + * | + * Append -------+-------+--------+--------+ + * / \ | | | | + * nil nil Scan Scan Scan Scan + * | | | | + * person employee student student-emp */ #include "postgres.h" @@ -62,429 +62,451 @@ #include "executor/nodeIndexscan.h" #include "utils/palloc.h" #include "utils/mcxt.h" -#include "parser/parsetree.h" /* for rt_store() macro */ +#include "parser/parsetree.h" /* for rt_store() macro */ -static bool exec_append_initialize_next(Append *node); +static bool exec_append_initialize_next(Append * node); /* ---------------------------------------------------------------- - * exec-append-initialize-next - * - * Sets up the append node state (i.e. the append state node) - * for the "next" scan. - * - * Returns t iff there is a "next" scan to process. + * exec-append-initialize-next + * + * Sets up the append node state (i.e. the append state node) + * for the "next" scan. + * + * Returns t iff there is a "next" scan to process. * ---------------------------------------------------------------- */ -static bool -exec_append_initialize_next(Append *node) +static bool +exec_append_initialize_next(Append * node) { - EState *estate; - AppendState *unionstate; - TupleTableSlot *result_slot; - List *rangeTable; - - int whichplan; - int nplans; - List *rtentries; - ResTarget *rtentry; - - Index unionrelid; - - /* ---------------- - * get information from the append node - * ---------------- - */ - estate = node->plan.state; - unionstate = node->unionstate; - result_slot = unionstate->cstate.cs_ResultTupleSlot; - rangeTable = estate->es_range_table; - - whichplan = unionstate->as_whichplan; - nplans = unionstate->as_nplans; - rtentries = node->unionrtentries; - - if (whichplan < 0) { - /* ---------------- - * if scanning in reverse, we start at - * the last scan in the list and then - * proceed back to the first.. in any case - * we inform ExecProcAppend that we are - * at the end of the line by returning FALSE - * ---------------- - */ - unionstate->as_whichplan = 0; - return FALSE; - - } else if (whichplan >= nplans) { - /* ---------------- - * as above, end the scan if we go beyond - * the last scan in our list.. - * ---------------- - */ - unionstate->as_whichplan = nplans - 1; - return FALSE; - - } else { - /* ---------------- - * initialize the scan - * (and update the range table appropriately) - * (doesn't this leave the range table hosed for anybody upstream - * of the Append node??? - jolly ) - * ---------------- - */ - if (node->unionrelid > 0) { - rtentry = nth(whichplan, rtentries); - if (rtentry == NULL) - elog(DEBUG, "exec_append_initialize_next: rtentry is nil"); - - unionrelid = node->unionrelid; - - rt_store(unionrelid, rangeTable, rtentry); - - if (unionstate->as_junkFilter_list) { - estate->es_junkFilter = - (JunkFilter*)nth(whichplan, - unionstate->as_junkFilter_list); - } - if (unionstate->as_result_relation_info_list) { - estate->es_result_relation_info = - (RelationInfo*) nth(whichplan, - unionstate->as_result_relation_info_list); - } - result_slot->ttc_whichplan = whichplan; + EState *estate; + AppendState *unionstate; + TupleTableSlot *result_slot; + List *rangeTable; + + int whichplan; + int nplans; + List *rtentries; + ResTarget *rtentry; + + Index unionrelid; + + /* ---------------- + * get information from the append node + * ---------------- + */ + estate = node->plan.state; + unionstate = node->unionstate; + result_slot = unionstate->cstate.cs_ResultTupleSlot; + rangeTable = estate->es_range_table; + + whichplan = unionstate->as_whichplan; + nplans = unionstate->as_nplans; + rtentries = node->unionrtentries; + + if (whichplan < 0) + { + /* ---------------- + * if scanning in reverse, we start at + * the last scan in the list and then + * proceed back to the first.. in any case + * we inform ExecProcAppend that we are + * at the end of the line by returning FALSE + * ---------------- + */ + unionstate->as_whichplan = 0; + return FALSE; + + } + else if (whichplan >= nplans) + { + /* ---------------- + * as above, end the scan if we go beyond + * the last scan in our list.. + * ---------------- + */ + unionstate->as_whichplan = nplans - 1; + return FALSE; + + } + else + { + /* ---------------- + * initialize the scan + * (and update the range table appropriately) + * (doesn't this leave the range table hosed for anybody upstream + * of the Append node??? - jolly ) + * ---------------- + */ + if (node->unionrelid > 0) + { + rtentry = nth(whichplan, rtentries); + if (rtentry == NULL) + elog(DEBUG, "exec_append_initialize_next: rtentry is nil"); + + unionrelid = node->unionrelid; + + rt_store(unionrelid, rangeTable, rtentry); + + if (unionstate->as_junkFilter_list) + { + estate->es_junkFilter = + (JunkFilter *) nth(whichplan, + unionstate->as_junkFilter_list); + } + if (unionstate->as_result_relation_info_list) + { + estate->es_result_relation_info = + (RelationInfo *) nth(whichplan, + unionstate->as_result_relation_info_list); + } + result_slot->ttc_whichplan = whichplan; + } + + return TRUE; } - - return TRUE; - } } /* ---------------------------------------------------------------- - * ExecInitAppend - * - * Begins all of the subscans of the append node, storing the - * scan structures in the 'initialized' vector of the append-state - * structure. + * ExecInitAppend + * + * Begins all of the subscans of the append node, storing the + * scan structures in the 'initialized' vector of the append-state + * structure. * - * (This is potentially wasteful, since the entire result of the - * append node may not be scanned, but this way all of the - * structures get allocated in the executor's top level memory - * block instead of that of the call to ExecProcAppend.) - * - * Returns the scan result of the first scan. + * (This is potentially wasteful, since the entire result of the + * append node may not be scanned, but this way all of the + * structures get allocated in the executor's top level memory + * block instead of that of the call to ExecProcAppend.) + * + * Returns the scan result of the first scan. * ---------------------------------------------------------------- */ bool -ExecInitAppend(Append *node, EState *estate, Plan *parent) +ExecInitAppend(Append * node, EState * estate, Plan * parent) { - AppendState *unionstate; - int nplans; - List *resultList = NULL; - List *rtentries; - List *unionplans; - bool *initialized; - int i; - Plan *initNode; - List *junkList; - RelationInfo *es_rri = estate->es_result_relation_info; - - /* ---------------- - * assign execution state to node and get information - * for append state - * ---------------- - */ - node->plan.state = estate; - - unionplans = node->unionplans; - nplans = length(unionplans); - rtentries = node->unionrtentries; - - CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext); - initialized = (bool *)palloc(nplans * sizeof(bool)); - - /* ---------------- - * create new AppendState for our append node - * ---------------- - */ - unionstate = makeNode(AppendState); - unionstate->as_whichplan = 0; - unionstate->as_nplans = nplans; - unionstate->as_initialized = initialized; - unionstate->as_rtentries = rtentries; - - node->unionstate = unionstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks - * - * Append plans don't have expression contexts because they - * never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent); - + AppendState *unionstate; + int nplans; + List *resultList = NULL; + List *rtentries; + List *unionplans; + bool *initialized; + int i; + Plan *initNode; + List *junkList; + RelationInfo *es_rri = estate->es_result_relation_info; + + /* ---------------- + * assign execution state to node and get information + * for append state + * ---------------- + */ + node->plan.state = estate; + + unionplans = node->unionplans; + nplans = length(unionplans); + rtentries = node->unionrtentries; + + CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext); + initialized = (bool *) palloc(nplans * sizeof(bool)); + + /* ---------------- + * create new AppendState for our append node + * ---------------- + */ + unionstate = makeNode(AppendState); + unionstate->as_whichplan = 0; + unionstate->as_nplans = nplans; + unionstate->as_initialized = initialized; + unionstate->as_rtentries = rtentries; + + node->unionstate = unionstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks + * + * Append plans don't have expression contexts because they + * never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent); + #define APPEND_NSLOTS 1 - /* ---------------- - * append nodes still have Result slots, which hold pointers - * to tuples, so we have to initialize them.. - * ---------------- - */ - ExecInitResultTupleSlot(estate, &unionstate->cstate); - - /* - * If the inherits rtentry is the result relation, we have to make - * a result relation info list for all inheritors so we can update - * their indices and put the result tuples in the right place etc. - * - * e.g. replace p (age = p.age + 1) from p in person* - */ - if ((es_rri != (RelationInfo*)NULL) && - (node->unionrelid == es_rri->ri_RangeTableIndex)) + /* ---------------- + * append nodes still have Result slots, which hold pointers + * to tuples, so we have to initialize them.. + * ---------------- + */ + ExecInitResultTupleSlot(estate, &unionstate->cstate); + + /* + * If the inherits rtentry is the result relation, we have to make a + * result relation info list for all inheritors so we can update their + * indices and put the result tuples in the right place etc. + * + * e.g. replace p (age = p.age + 1) from p in person* + */ + if ((es_rri != (RelationInfo *) NULL) && + (node->unionrelid == es_rri->ri_RangeTableIndex)) { - RelationInfo *rri; - List *rtentryP; - - foreach(rtentryP,rtentries) + RelationInfo *rri; + List *rtentryP; + + foreach(rtentryP, rtentries) { - Oid reloid; - RangeTblEntry *rtentry = lfirst(rtentryP); - - reloid = rtentry->relid; - rri = makeNode(RelationInfo); - rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex; - rri->ri_RelationDesc = heap_open(reloid); - rri->ri_NumIndices = 0; - rri->ri_IndexRelationDescs = NULL; /* index descs */ - rri->ri_IndexRelationInfo = NULL; /* index key info */ - - resultList = lcons(rri,resultList); - ExecOpenIndices(reloid, rri); + Oid reloid; + RangeTblEntry *rtentry = lfirst(rtentryP); + + reloid = rtentry->relid; + rri = makeNode(RelationInfo); + rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex; + rri->ri_RelationDesc = heap_open(reloid); + rri->ri_NumIndices = 0; + rri->ri_IndexRelationDescs = NULL; /* index descs */ + rri->ri_IndexRelationInfo = NULL; /* index key info */ + + resultList = lcons(rri, resultList); + ExecOpenIndices(reloid, rri); } - unionstate->as_result_relation_info_list = resultList; + unionstate->as_result_relation_info_list = resultList; } - /* ---------------- - * call ExecInitNode on each of the plans in our list - * and save the results into the array "initialized" - * ---------------- - */ - junkList = NIL; - - for(i = 0; i < nplans ; i++ ) { - JunkFilter *j; - List *targetList; - /* ---------------- - * NOTE: we first modify range table in - * exec_append_initialize_next() and - * then initialize the subnode, - * since it may use the range table. - * ---------------- - */ - unionstate->as_whichplan = i; - exec_append_initialize_next(node); - - initNode = (Plan *) nth(i, unionplans); - initialized[i] = ExecInitNode(initNode, estate, (Plan*) node); - - /* --------------- - * Each targetlist in the subplan may need its own junk filter - * - * This is true only when the reln being replaced/deleted is - * the one that we're looking at the subclasses of - * --------------- + /* ---------------- + * call ExecInitNode on each of the plans in our list + * and save the results into the array "initialized" + * ---------------- */ - if ((es_rri != (RelationInfo*)NULL) && - (node->unionrelid == es_rri->ri_RangeTableIndex)) { - - targetList = initNode->targetlist; - j = (JunkFilter *) ExecInitJunkFilter(targetList); - junkList = lappend(junkList, j); + junkList = NIL; + + for (i = 0; i < nplans; i++) + { + JunkFilter *j; + List *targetList; + + /* ---------------- + * NOTE: we first modify range table in + * exec_append_initialize_next() and + * then initialize the subnode, + * since it may use the range table. + * ---------------- + */ + unionstate->as_whichplan = i; + exec_append_initialize_next(node); + + initNode = (Plan *) nth(i, unionplans); + initialized[i] = ExecInitNode(initNode, estate, (Plan *) node); + + /* --------------- + * Each targetlist in the subplan may need its own junk filter + * + * This is true only when the reln being replaced/deleted is + * the one that we're looking at the subclasses of + * --------------- + */ + if ((es_rri != (RelationInfo *) NULL) && + (node->unionrelid == es_rri->ri_RangeTableIndex)) + { + + targetList = initNode->targetlist; + j = (JunkFilter *) ExecInitJunkFilter(targetList); + junkList = lappend(junkList, j); + } + } - - } - unionstate->as_junkFilter_list = junkList; - if (junkList != NIL) - estate->es_junkFilter = (JunkFilter *)lfirst(junkList); - - /* ---------------- - * initialize the return type from the appropriate subplan. - * ---------------- - */ - initNode = (Plan *) nth(0, unionplans); - ExecAssignResultType(&unionstate->cstate, -/* ExecGetExecTupDesc(initNode), */ - ExecGetTupType(initNode)); - unionstate->cstate.cs_ProjInfo = NULL; - - /* ---------------- - * return the result from the first subplan's initialization - * ---------------- - */ - unionstate->as_whichplan = 0; - exec_append_initialize_next(node); + unionstate->as_junkFilter_list = junkList; + if (junkList != NIL) + estate->es_junkFilter = (JunkFilter *) lfirst(junkList); + + /* ---------------- + * initialize the return type from the appropriate subplan. + * ---------------- + */ + initNode = (Plan *) nth(0, unionplans); + ExecAssignResultType(&unionstate->cstate, +/* ExecGetExecTupDesc(initNode), */ + ExecGetTupType(initNode)); + unionstate->cstate.cs_ProjInfo = NULL; + + /* ---------------- + * return the result from the first subplan's initialization + * ---------------- + */ + unionstate->as_whichplan = 0; + exec_append_initialize_next(node); #if 0 - result = (List *) initialized[0]; -#endif - return TRUE; + result = (List *) initialized[0]; +#endif + return TRUE; } int -ExecCountSlotsAppend(Append *node) +ExecCountSlotsAppend(Append * node) { - List *plan; - List *unionplans = node->unionplans; - int nSlots = 0; - - foreach (plan,unionplans) { - nSlots += ExecCountSlotsNode((Plan *)lfirst(plan)); - } - return nSlots + APPEND_NSLOTS; + List *plan; + List *unionplans = node->unionplans; + int nSlots = 0; + + foreach(plan, unionplans) + { + nSlots += ExecCountSlotsNode((Plan *) lfirst(plan)); + } + return nSlots + APPEND_NSLOTS; } /* ---------------------------------------------------------------- - * ExecProcAppend - * - * Handles the iteration over the multiple scans. - * - * NOTE: Can't call this ExecAppend, that name is used in execMain.l + * ExecProcAppend + * + * Handles the iteration over the multiple scans. + * + * NOTE: Can't call this ExecAppend, that name is used in execMain.l * ---------------------------------------------------------------- */ TupleTableSlot * -ExecProcAppend(Append *node) +ExecProcAppend(Append * node) { - EState *estate; - AppendState *unionstate; - - int whichplan; - List *unionplans; - Plan *subnode; - TupleTableSlot *result; - TupleTableSlot *result_slot; - ScanDirection direction; - - /* ---------------- - * get information from the node - * ---------------- - */ - unionstate = node->unionstate; - estate = node->plan.state; - direction = estate->es_direction; - - unionplans = node->unionplans; - whichplan = unionstate->as_whichplan; - result_slot = unionstate->cstate.cs_ResultTupleSlot; - - /* ---------------- - * figure out which subplan we are currently processing - * ---------------- - */ - subnode = (Plan *) nth(whichplan, unionplans); - - if (subnode == NULL) - elog(DEBUG, "ExecProcAppend: subnode is NULL"); - - /* ---------------- - * get a tuple from the subplan - * ---------------- - */ - result = ExecProcNode(subnode, (Plan*)node); - - if (! TupIsNull(result)) { - /* ---------------- - * if the subplan gave us something then place a copy of - * whatever we get into our result slot and return it, else.. - * ---------------- - */ - return ExecStoreTuple(result->val, - result_slot, result->ttc_buffer, false); - - } else { - /* ---------------- - * .. go on to the "next" subplan in the appropriate - * direction and try processing again (recursively) - * ---------------- - */ - whichplan = unionstate->as_whichplan; - - if (ScanDirectionIsForward(direction)) - { - unionstate->as_whichplan = whichplan + 1; - } - else - { - unionstate->as_whichplan = whichplan - 1; - } - + EState *estate; + AppendState *unionstate; + + int whichplan; + List *unionplans; + Plan *subnode; + TupleTableSlot *result; + TupleTableSlot *result_slot; + ScanDirection direction; + /* ---------------- - * return something from next node or an empty slot - * all of our subplans have been exhausted. + * get information from the node * ---------------- */ - if (exec_append_initialize_next(node)) { - ExecSetSlotDescriptorIsNew(result_slot, true); - return - ExecProcAppend(node); - } else - return ExecClearTuple(result_slot); - } + unionstate = node->unionstate; + estate = node->plan.state; + direction = estate->es_direction; + + unionplans = node->unionplans; + whichplan = unionstate->as_whichplan; + result_slot = unionstate->cstate.cs_ResultTupleSlot; + + /* ---------------- + * figure out which subplan we are currently processing + * ---------------- + */ + subnode = (Plan *) nth(whichplan, unionplans); + + if (subnode == NULL) + elog(DEBUG, "ExecProcAppend: subnode is NULL"); + + /* ---------------- + * get a tuple from the subplan + * ---------------- + */ + result = ExecProcNode(subnode, (Plan *) node); + + if (!TupIsNull(result)) + { + /* ---------------- + * if the subplan gave us something then place a copy of + * whatever we get into our result slot and return it, else.. + * ---------------- + */ + return ExecStoreTuple(result->val, + result_slot, result->ttc_buffer, false); + + } + else + { + /* ---------------- + * .. go on to the "next" subplan in the appropriate + * direction and try processing again (recursively) + * ---------------- + */ + whichplan = unionstate->as_whichplan; + + if (ScanDirectionIsForward(direction)) + { + unionstate->as_whichplan = whichplan + 1; + } + else + { + unionstate->as_whichplan = whichplan - 1; + } + + /* ---------------- + * return something from next node or an empty slot + * all of our subplans have been exhausted. + * ---------------- + */ + if (exec_append_initialize_next(node)) + { + ExecSetSlotDescriptorIsNew(result_slot, true); + return + ExecProcAppend(node); + } + else + return ExecClearTuple(result_slot); + } } /* ---------------------------------------------------------------- - * ExecEndAppend - * - * Shuts down the subscans of the append node. - * - * Returns nothing of interest. + * ExecEndAppend + * + * Shuts down the subscans of the append node. + * + * Returns nothing of interest. * ---------------------------------------------------------------- */ void -ExecEndAppend(Append *node) +ExecEndAppend(Append * node) { - AppendState *unionstate; - int nplans; - List *unionplans; - bool *initialized; - int i; - List *resultRelationInfoList; - RelationInfo *resultRelationInfo; - - /* ---------------- - * get information from the node - * ---------------- - */ - unionstate = node->unionstate; - unionplans = node->unionplans; - nplans = unionstate->as_nplans; - initialized = unionstate->as_initialized; - - /* ---------------- - * shut down each of the subscans - * ---------------- - */ - for(i = 0; i < nplans; i++) { - if (initialized[i]==TRUE) { - ExecEndNode( (Plan *) nth(i, unionplans), (Plan*)node ); - } - } - - /* ---------------- - * close out the different result relations - * ---------------- - */ - resultRelationInfoList = unionstate->as_result_relation_info_list; - while (resultRelationInfoList != NIL) { - Relation resultRelationDesc; - - resultRelationInfo = (RelationInfo*) lfirst(resultRelationInfoList); - resultRelationDesc = resultRelationInfo->ri_RelationDesc; - heap_close(resultRelationDesc); - pfree(resultRelationInfo); - resultRelationInfoList = lnext(resultRelationInfoList); - } - if (unionstate->as_result_relation_info_list) - pfree(unionstate->as_result_relation_info_list); - - /* XXX should free unionstate->as_rtentries and unionstate->as_junkfilter_list here */ -} + AppendState *unionstate; + int nplans; + List *unionplans; + bool *initialized; + int i; + List *resultRelationInfoList; + RelationInfo *resultRelationInfo; + + /* ---------------- + * get information from the node + * ---------------- + */ + unionstate = node->unionstate; + unionplans = node->unionplans; + nplans = unionstate->as_nplans; + initialized = unionstate->as_initialized; + + /* ---------------- + * shut down each of the subscans + * ---------------- + */ + for (i = 0; i < nplans; i++) + { + if (initialized[i] == TRUE) + { + ExecEndNode((Plan *) nth(i, unionplans), (Plan *) node); + } + } + + /* ---------------- + * close out the different result relations + * ---------------- + */ + resultRelationInfoList = unionstate->as_result_relation_info_list; + while (resultRelationInfoList != NIL) + { + Relation resultRelationDesc; + resultRelationInfo = (RelationInfo *) lfirst(resultRelationInfoList); + resultRelationDesc = resultRelationInfo->ri_RelationDesc; + heap_close(resultRelationDesc); + pfree(resultRelationInfo); + resultRelationInfoList = lnext(resultRelationInfoList); + } + if (unionstate->as_result_relation_info_list) + pfree(unionstate->as_result_relation_info_list); + + /* + * XXX should free unionstate->as_rtentries and + * unionstate->as_junkfilter_list here + */ +} diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 0637a8dd282..1a96a1ee911 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -1,19 +1,19 @@ /*------------------------------------------------------------------------- * * nodeGroup.c-- - * Routines to handle group nodes (used for queries with GROUP BY clause). + * Routines to handle group nodes (used for queries with GROUP BY clause). * * Copyright (c) 1994, Regents of the University of California * * * DESCRIPTION - * The Group node is designed for handling queries with a GROUP BY clause. - * It's outer plan must be a sort node. It assumes that the tuples it gets - * back from the outer plan is sorted in the order specified by the group - * columns. (ie. tuples from the same group are consecutive) + * The Group node is designed for handling queries with a GROUP BY clause. + * It's outer plan must be a sort node. It assumes that the tuples it gets + * back from the outer plan is sorted in the order specified by the group + * columns. (ie. tuples from the same group are consecutive) * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.5 1997/01/10 20:17:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.6 1997/09/07 04:41:31 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,329 +28,348 @@ #include "executor/executor.h" #include "executor/nodeGroup.h" -static TupleTableSlot *ExecGroupEveryTuple(Group *node); -static TupleTableSlot *ExecGroupOneTuple(Group *node); -static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot, - int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc); +static TupleTableSlot *ExecGroupEveryTuple(Group * node); +static TupleTableSlot *ExecGroupOneTuple(Group * node); +static bool +sameGroup(TupleTableSlot * oldslot, TupleTableSlot * newslot, + int numCols, AttrNumber * grpColIdx, TupleDesc tupdesc); /* --------------------------------------- - * ExecGroup - + * ExecGroup - * - * There are two modes in which tuples are returned by ExecGroup. If - * tuplePerGroup is TRUE, every tuple from the same group will be - * returned, followed by a NULL at the end of each group. This is - * useful for Agg node which needs to aggregate over tuples of the same - * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary) + * There are two modes in which tuples are returned by ExecGroup. If + * tuplePerGroup is TRUE, every tuple from the same group will be + * returned, followed by a NULL at the end of each group. This is + * useful for Agg node which needs to aggregate over tuples of the same + * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary) * - * If tuplePerGroup is FALSE, only one tuple per group is returned. The - * tuple returned contains only the group columns. NULL is returned only - * at the end when no more groups is present. This is useful when - * the query does not involve aggregates. (eg. SELECT salary FROM emp - * GROUP BY salary) + * If tuplePerGroup is FALSE, only one tuple per group is returned. The + * tuple returned contains only the group columns. NULL is returned only + * at the end when no more groups is present. This is useful when + * the query does not involve aggregates. (eg. SELECT salary FROM emp + * GROUP BY salary) * ------------------------------------------ */ TupleTableSlot * -ExecGroup(Group *node) +ExecGroup(Group * node) { - if (node->tuplePerGroup) - return ExecGroupEveryTuple(node); - else - return ExecGroupOneTuple(node); + if (node->tuplePerGroup) + return ExecGroupEveryTuple(node); + else + return ExecGroupOneTuple(node); } /* * ExecGroupEveryTuple - - * return every tuple with a NULL between each group + * return every tuple with a NULL between each group */ static TupleTableSlot * -ExecGroupEveryTuple(Group *node) +ExecGroupEveryTuple(Group * node) { - GroupState *grpstate; - EState *estate; - ExprContext *econtext; + GroupState *grpstate; + EState *estate; + ExprContext *econtext; - HeapTuple outerTuple = NULL; - TupleTableSlot *outerslot, *lastslot; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; + HeapTuple outerTuple = NULL; + TupleTableSlot *outerslot, + *lastslot; + ProjectionInfo *projInfo; + TupleTableSlot *resultSlot; - bool isDone; + bool isDone; - /* --------------------- - * get state info from node - * --------------------- - */ - grpstate = node->grpstate; - if (grpstate->grp_done) - return NULL; + /* --------------------- + * get state info from node + * --------------------- + */ + grpstate = node->grpstate; + if (grpstate->grp_done) + return NULL; - estate = node->plan.state; + estate = node->plan.state; - econtext = grpstate->csstate.cstate.cs_ExprContext; + econtext = grpstate->csstate.cstate.cs_ExprContext; - if (grpstate->grp_useLastTuple) { - /* - * we haven't returned last tuple yet because it is not of the - * same group - */ - grpstate->grp_useLastTuple = FALSE; + if (grpstate->grp_useLastTuple) + { + + /* + * we haven't returned last tuple yet because it is not of the + * same group + */ + grpstate->grp_useLastTuple = FALSE; - ExecStoreTuple(grpstate->grp_lastSlot->val, - grpstate->csstate.css_ScanTupleSlot, - grpstate->grp_lastSlot->ttc_buffer, - false); - } else { - outerslot = ExecProcNode(outerPlan(node), (Plan*)node); - if (outerslot) - outerTuple = outerslot->val; - if (!HeapTupleIsValid(outerTuple)) { - grpstate->grp_done = TRUE; - return NULL; + ExecStoreTuple(grpstate->grp_lastSlot->val, + grpstate->csstate.css_ScanTupleSlot, + grpstate->grp_lastSlot->ttc_buffer, + false); + } + else + { + outerslot = ExecProcNode(outerPlan(node), (Plan *) node); + if (outerslot) + outerTuple = outerslot->val; + if (!HeapTupleIsValid(outerTuple)) + { + grpstate->grp_done = TRUE; + return NULL; + } + + /* ---------------- + * Compare with last tuple and see if this tuple is of + * the same group. + * ---------------- + */ + lastslot = grpstate->csstate.css_ScanTupleSlot; + + if (lastslot->val != NULL && + (!sameGroup(lastslot, outerslot, + node->numCols, node->grpColIdx, + ExecGetScanType(&grpstate->csstate)))) + { +/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ + + grpstate->grp_useLastTuple = TRUE; + + /* save it for next time */ + grpstate->grp_lastSlot = outerslot; + + /* + * signifies the end of the group + */ + return NULL; + } + + ExecStoreTuple(outerTuple, + grpstate->csstate.css_ScanTupleSlot, + outerslot->ttc_buffer, + false); } /* ---------------- - * Compare with last tuple and see if this tuple is of - * the same group. + * form a projection tuple, store it in the result tuple + * slot and return it. * ---------------- */ - lastslot = grpstate->csstate.css_ScanTupleSlot; - - if (lastslot->val != NULL && - (!sameGroup(lastslot, outerslot, - node->numCols, node->grpColIdx, - ExecGetScanType(&grpstate->csstate)))) { -/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ - - grpstate->grp_useLastTuple = TRUE; + projInfo = grpstate->csstate.cstate.cs_ProjInfo; - /* save it for next time */ - grpstate->grp_lastSlot = outerslot; + econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot; + resultSlot = ExecProject(projInfo, &isDone); - /* - * signifies the end of the group - */ - return NULL; - } - - ExecStoreTuple(outerTuple, - grpstate->csstate.css_ScanTupleSlot, - outerslot->ttc_buffer, - false); - } - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - projInfo = grpstate->csstate.cstate.cs_ProjInfo; - - econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot; - resultSlot = ExecProject(projInfo, &isDone); - - return resultSlot; + return resultSlot; } /* * ExecGroupOneTuple - - * returns one tuple per group, a NULL at the end when there are no more - * tuples. + * returns one tuple per group, a NULL at the end when there are no more + * tuples. */ static TupleTableSlot * -ExecGroupOneTuple(Group *node) +ExecGroupOneTuple(Group * node) { - GroupState *grpstate; - EState *estate; - ExprContext *econtext; + GroupState *grpstate; + EState *estate; + ExprContext *econtext; - HeapTuple outerTuple = NULL; - TupleTableSlot *outerslot, *lastslot; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; + HeapTuple outerTuple = NULL; + TupleTableSlot *outerslot, + *lastslot; + ProjectionInfo *projInfo; + TupleTableSlot *resultSlot; - bool isDone; + bool isDone; - /* --------------------- - * get state info from node - * --------------------- - */ - grpstate = node->grpstate; - if (grpstate->grp_done) - return NULL; + /* --------------------- + * get state info from node + * --------------------- + */ + grpstate = node->grpstate; + if (grpstate->grp_done) + return NULL; - estate = node->plan.state; + estate = node->plan.state; - econtext = node->grpstate->csstate.cstate.cs_ExprContext; + econtext = node->grpstate->csstate.cstate.cs_ExprContext; - if (grpstate->grp_useLastTuple) { - grpstate->grp_useLastTuple = FALSE; - ExecStoreTuple(grpstate->grp_lastSlot->val, - grpstate->csstate.css_ScanTupleSlot, - grpstate->grp_lastSlot->ttc_buffer, - false); - } else { - outerslot = ExecProcNode(outerPlan(node), (Plan*)node); - if (outerslot) outerTuple = outerslot->val; - if (!HeapTupleIsValid(outerTuple)) { - grpstate->grp_done = TRUE; - return NULL; + if (grpstate->grp_useLastTuple) + { + grpstate->grp_useLastTuple = FALSE; + ExecStoreTuple(grpstate->grp_lastSlot->val, + grpstate->csstate.css_ScanTupleSlot, + grpstate->grp_lastSlot->ttc_buffer, + false); } - ExecStoreTuple(outerTuple, - grpstate->csstate.css_ScanTupleSlot, - outerslot->ttc_buffer, - false); - } - lastslot = grpstate->csstate.css_ScanTupleSlot; - - /* - * find all tuples that belong to a group - */ - for(;;) { - outerslot = ExecProcNode(outerPlan(node), (Plan*)node); - outerTuple = (outerslot) ? outerslot->val : NULL; - if (!HeapTupleIsValid(outerTuple)) { - /* - * we have at least one tuple (lastslot) if we reach here - */ - grpstate->grp_done = TRUE; - - /* return lastslot */ - break; + else + { + outerslot = ExecProcNode(outerPlan(node), (Plan *) node); + if (outerslot) + outerTuple = outerslot->val; + if (!HeapTupleIsValid(outerTuple)) + { + grpstate->grp_done = TRUE; + return NULL; + } + ExecStoreTuple(outerTuple, + grpstate->csstate.css_ScanTupleSlot, + outerslot->ttc_buffer, + false); } + lastslot = grpstate->csstate.css_ScanTupleSlot; - /* ---------------- - * Compare with last tuple and see if this tuple is of - * the same group. - * ---------------- + /* + * find all tuples that belong to a group */ - if ((!sameGroup(lastslot, outerslot, - node->numCols, node->grpColIdx, - ExecGetScanType(&grpstate->csstate)))) { -/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ + for (;;) + { + outerslot = ExecProcNode(outerPlan(node), (Plan *) node); + outerTuple = (outerslot) ? outerslot->val : NULL; + if (!HeapTupleIsValid(outerTuple)) + { + + /* + * we have at least one tuple (lastslot) if we reach here + */ + grpstate->grp_done = TRUE; + + /* return lastslot */ + break; + } + + /* ---------------- + * Compare with last tuple and see if this tuple is of + * the same group. + * ---------------- + */ + if ((!sameGroup(lastslot, outerslot, + node->numCols, node->grpColIdx, + ExecGetScanType(&grpstate->csstate)))) + { +/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/ + + grpstate->grp_useLastTuple = TRUE; + + /* save it for next time */ + grpstate->grp_lastSlot = outerslot; + + /* return lastslot */ + break; + } + + ExecStoreTuple(outerTuple, + grpstate->csstate.css_ScanTupleSlot, + outerslot->ttc_buffer, + false); + + lastslot = grpstate->csstate.css_ScanTupleSlot; + } - grpstate->grp_useLastTuple = TRUE; + ExecStoreTuple(lastslot->val, + grpstate->csstate.css_ScanTupleSlot, + lastslot->ttc_buffer, + false); - /* save it for next time */ - grpstate->grp_lastSlot = outerslot; + /* ---------------- + * form a projection tuple, store it in the result tuple + * slot and return it. + * ---------------- + */ + projInfo = grpstate->csstate.cstate.cs_ProjInfo; - /* return lastslot */ - break; - } - - ExecStoreTuple(outerTuple, - grpstate->csstate.css_ScanTupleSlot, - outerslot->ttc_buffer, - false); + econtext->ecxt_scantuple = lastslot; + resultSlot = ExecProject(projInfo, &isDone); - lastslot = grpstate->csstate.css_ScanTupleSlot; - } - - ExecStoreTuple(lastslot->val, - grpstate->csstate.css_ScanTupleSlot, - lastslot->ttc_buffer, - false); - - /* ---------------- - * form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - projInfo = grpstate->csstate.cstate.cs_ProjInfo; - - econtext->ecxt_scantuple = lastslot; - resultSlot = ExecProject(projInfo, &isDone); - - return resultSlot; + return resultSlot; } /* ----------------- - * ExecInitGroup + * ExecInitGroup * - * Creates the run-time information for the group node produced by the - * planner and initializes its outer subtree + * Creates the run-time information for the group node produced by the + * planner and initializes its outer subtree * ----------------- */ bool -ExecInitGroup(Group *node, EState *estate, Plan *parent) +ExecInitGroup(Group * node, EState * estate, Plan * parent) { - GroupState *grpstate; - Plan *outerPlan; - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create state structure - */ - grpstate = makeNode(GroupState); - node->grpstate = grpstate; - grpstate->grp_useLastTuple = FALSE; - grpstate->grp_done = FALSE; - - /* - * assign node's base id and create expression context - */ - ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate, - (Plan*) parent); - ExecAssignExprContext(estate, &grpstate->csstate.cstate); - + GroupState *grpstate; + Plan *outerPlan; + + /* + * assign the node's execution state + */ + node->plan.state = estate; + + /* + * create state structure + */ + grpstate = makeNode(GroupState); + node->grpstate = grpstate; + grpstate->grp_useLastTuple = FALSE; + grpstate->grp_done = FALSE; + + /* + * assign node's base id and create expression context + */ + ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate, + (Plan *) parent); + ExecAssignExprContext(estate, &grpstate->csstate.cstate); + #define GROUP_NSLOTS 2 - /* - * tuple table initialization - */ - ExecInitScanTupleSlot(estate, &grpstate->csstate); - ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate); - - /* - * initializes child nodes - */ - outerPlan = outerPlan(node); - ExecInitNode(outerPlan, estate, (Plan *)node); - - /* ---------------- - * initialize tuple type. - * ---------------- - */ - ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate); - - /* - * Initialize tuple type for both result and scan. - * This node does no projection - */ - ExecAssignResultTypeFromTL((Plan*) node, &grpstate->csstate.cstate); - ExecAssignProjectionInfo((Plan*)node, &grpstate->csstate.cstate); - - return TRUE; + + /* + * tuple table initialization + */ + ExecInitScanTupleSlot(estate, &grpstate->csstate); + ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate); + + /* + * initializes child nodes + */ + outerPlan = outerPlan(node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize tuple type. + * ---------------- + */ + ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate); + + /* + * Initialize tuple type for both result and scan. This node does no + * projection + */ + ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate); + ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate); + + return TRUE; } int -ExecCountSlotsGroup(Group *node) +ExecCountSlotsGroup(Group * node) { - return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS; + return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS; } /* ------------------------ - * ExecEndGroup(node) + * ExecEndGroup(node) * * ----------------------- */ void -ExecEndGroup(Group *node) +ExecEndGroup(Group * node) { - GroupState *grpstate; - Plan *outerPlan; + GroupState *grpstate; + Plan *outerPlan; + + grpstate = node->grpstate; - grpstate = node->grpstate; + ExecFreeProjectionInfo(&grpstate->csstate.cstate); - ExecFreeProjectionInfo(&grpstate->csstate.cstate); + outerPlan = outerPlan(node); + ExecEndNode(outerPlan, (Plan *) node); - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan*)node); - - /* clean up tuple table */ - ExecClearTuple(grpstate->csstate.css_ScanTupleSlot); + /* clean up tuple table */ + ExecClearTuple(grpstate->csstate.css_ScanTupleSlot); } /***************************************************************************** @@ -360,54 +379,63 @@ ExecEndGroup(Group *node) /* * code swiped from nodeUnique.c */ -static bool -sameGroup(TupleTableSlot *oldslot, - TupleTableSlot *newslot, - int numCols, - AttrNumber *grpColIdx, - TupleDesc tupdesc) +static bool +sameGroup(TupleTableSlot * oldslot, + TupleTableSlot * newslot, + int numCols, + AttrNumber * grpColIdx, + TupleDesc tupdesc) { - bool isNull1,isNull2; - char *attr1, *attr2; - char *val1, *val2; - int i; - AttrNumber att; - Oid typoutput; - - for(i = 0; i < numCols; i++) { - att = grpColIdx[i]; - typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid); - - attr1 = heap_getattr(oldslot->val, - InvalidBuffer, - att, - tupdesc, - &isNull1); - - attr2 = heap_getattr(newslot->val, - InvalidBuffer, - att, - tupdesc, - &isNull2); - - if (isNull1 == isNull2) { - if (isNull1) /* both are null, they are equal */ - continue; - - val1 = fmgr(typoutput, attr1, - gettypelem(tupdesc->attrs[att-1]->atttypid)); - val2 = fmgr(typoutput, attr2, - gettypelem(tupdesc->attrs[att-1]->atttypid)); - - /* now, val1 and val2 are ascii representations so we can - use strcmp for comparison */ - if (strcmp(val1,val2) != 0) - return FALSE; - } else { - /* one is null and the other isn't, they aren't equal */ - return FALSE; + bool isNull1, + isNull2; + char *attr1, + *attr2; + char *val1, + *val2; + int i; + AttrNumber att; + Oid typoutput; + + for (i = 0; i < numCols; i++) + { + att = grpColIdx[i]; + typoutput = typtoout((Oid) tupdesc->attrs[att - 1]->atttypid); + + attr1 = heap_getattr(oldslot->val, + InvalidBuffer, + att, + tupdesc, + &isNull1); + + attr2 = heap_getattr(newslot->val, + InvalidBuffer, + att, + tupdesc, + &isNull2); + + if (isNull1 == isNull2) + { + if (isNull1) /* both are null, they are equal */ + continue; + + val1 = fmgr(typoutput, attr1, + gettypelem(tupdesc->attrs[att - 1]->atttypid)); + val2 = fmgr(typoutput, attr2, + gettypelem(tupdesc->attrs[att - 1]->atttypid)); + + /* + * now, val1 and val2 are ascii representations so we can use + * strcmp for comparison + */ + if (strcmp(val1, val2) != 0) + return FALSE; + } + else + { + /* one is null and the other isn't, they aren't equal */ + return FALSE; + } } - } - return TRUE; + return TRUE; } diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 10bfe9842cf..b25939fa832 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -1,26 +1,26 @@ /*------------------------------------------------------------------------- * * nodeHash.c-- - * Routines to hash relations for hashjoin + * Routines to hash relations for hashjoin * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.10 1997/08/19 21:31:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.11 1997/09/07 04:41:32 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecHash - generate an in-memory hash table of the relation - * ExecInitHash - initialize node and subnodes.. - * ExecEndHash - shutdown node and subnodes + * ExecHash - generate an in-memory hash table of the relation + * ExecInitHash - initialize node and subnodes.. + * ExecEndHash - shutdown node and subnodes * */ #include <sys/types.h> -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include <math.h> #include <string.h> #include <sys/file.h> @@ -32,9 +32,9 @@ #include "postgres.h" -#include "storage/fd.h" /* for SEEK_ */ +#include "storage/fd.h" /* for SEEK_ */ #include "storage/ipc.h" -#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "storage/bufmgr.h" /* for BLCKSZ */ #include "executor/executor.h" #include "executor/nodeHash.h" #include "executor/nodeHashjoin.h" @@ -42,827 +42,855 @@ #include "utils/palloc.h" #include "utils/hsearch.h" -extern int NBuffers; -static int HashTBSize; +extern int NBuffers; +static int HashTBSize; -static void mk_hj_temp(char *tempname); -static int hashFunc(char *key, int len); -static int ExecHashPartition(Hash *node); +static void mk_hj_temp(char *tempname); +static int hashFunc(char *key, int len); +static int ExecHashPartition(Hash * node); static RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable); -static void ExecHashOverflowInsert(HashJoinTable hashtable, - HashBucket bucket, - HeapTuple heapTuple); +static void +ExecHashOverflowInsert(HashJoinTable hashtable, + HashBucket bucket, + HeapTuple heapTuple); /* ---------------------------------------------------------------- - * ExecHash + * ExecHash * - * build hash table for hashjoin, all do partitioning if more - * than one batches are required. + * build hash table for hashjoin, all do partitioning if more + * than one batches are required. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecHash(Hash *node) +ExecHash(Hash * node) { - EState *estate; - HashState *hashstate; - Plan *outerNode; - Var *hashkey; - HashJoinTable hashtable; - TupleTableSlot *slot; - ExprContext *econtext; - - int nbatch; - File *batches = NULL; - RelativeAddr *batchPos; - int *batchSizes; - int i; - RelativeAddr *innerbatchNames; - - /* ---------------- - * get state info from node - * ---------------- - */ - - hashstate = node->hashstate; - estate = node->plan.state; - outerNode = outerPlan(node); - - hashtable = node->hashtable; - if (hashtable == NULL) - elog(WARN, "ExecHash: hash table is NULL."); - - nbatch = hashtable->nbatch; - - if (nbatch > 0) { /* if needs hash partition */ - innerbatchNames = (RelativeAddr *) ABSADDR(hashtable->innerbatchNames); - - /* -------------- - * allocate space for the file descriptors of batch files - * then open the batch files in the current processes. - * -------------- - */ - batches = (File*)palloc(nbatch * sizeof(File)); - for (i=0; i<nbatch; i++) { - batches[i] = FileNameOpenFile(ABSADDR(innerbatchNames[i]), - O_CREAT | O_RDWR, 0600); + EState *estate; + HashState *hashstate; + Plan *outerNode; + Var *hashkey; + HashJoinTable hashtable; + TupleTableSlot *slot; + ExprContext *econtext; + + int nbatch; + File *batches = NULL; + RelativeAddr *batchPos; + int *batchSizes; + int i; + RelativeAddr *innerbatchNames; + + /* ---------------- + * get state info from node + * ---------------- + */ + + hashstate = node->hashstate; + estate = node->plan.state; + outerNode = outerPlan(node); + + hashtable = node->hashtable; + if (hashtable == NULL) + elog(WARN, "ExecHash: hash table is NULL."); + + nbatch = hashtable->nbatch; + + if (nbatch > 0) + { /* if needs hash partition */ + innerbatchNames = (RelativeAddr *) ABSADDR(hashtable->innerbatchNames); + + /* -------------- + * allocate space for the file descriptors of batch files + * then open the batch files in the current processes. + * -------------- + */ + batches = (File *) palloc(nbatch * sizeof(File)); + for (i = 0; i < nbatch; i++) + { + batches[i] = FileNameOpenFile(ABSADDR(innerbatchNames[i]), + O_CREAT | O_RDWR, 0600); + } + hashstate->hashBatches = batches; + batchPos = (RelativeAddr *) ABSADDR(hashtable->innerbatchPos); + batchSizes = (int *) ABSADDR(hashtable->innerbatchSizes); + } + + /* ---------------- + * set expression context + * ---------------- + */ + hashkey = node->hashkey; + econtext = hashstate->cstate.cs_ExprContext; + + /* ---------------- + * get tuple and insert into the hash table + * ---------------- + */ + for (;;) + { + slot = ExecProcNode(outerNode, (Plan *) node); + if (TupIsNull(slot)) + break; + + econtext->ecxt_innertuple = slot; + ExecHashTableInsert(hashtable, econtext, hashkey, + hashstate->hashBatches); + + ExecClearTuple(slot); + } + + /* + * end of build phase, flush all the last pages of the batches. + */ + for (i = 0; i < nbatch; i++) + { + if (FileSeek(batches[i], 0L, SEEK_END) < 0) + perror("FileSeek"); + if (FileWrite(batches[i], ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ) < 0) + perror("FileWrite"); + NDirectFileWrite++; } - hashstate->hashBatches = batches; - batchPos = (RelativeAddr*) ABSADDR(hashtable->innerbatchPos); - batchSizes = (int*) ABSADDR(hashtable->innerbatchSizes); - } - - /* ---------------- - * set expression context - * ---------------- - */ - hashkey = node->hashkey; - econtext = hashstate->cstate.cs_ExprContext; - - /* ---------------- - * get tuple and insert into the hash table - * ---------------- - */ - for (;;) { - slot = ExecProcNode(outerNode, (Plan*)node); - if (TupIsNull(slot)) - break; - - econtext->ecxt_innertuple = slot; - ExecHashTableInsert(hashtable, econtext, hashkey, - hashstate->hashBatches); - - ExecClearTuple(slot); - } - - /* - * end of build phase, flush all the last pages of the batches. - */ - for (i=0; i<nbatch; i++) { - if (FileSeek(batches[i], 0L, SEEK_END) < 0) - perror("FileSeek"); - if (FileWrite(batches[i],ABSADDR(hashtable->batch)+i*BLCKSZ,BLCKSZ) < 0) - perror("FileWrite"); - NDirectFileWrite++; - } - - /* --------------------- - * Return the slot so that we have the tuple descriptor - * when we need to save/restore them. -Jeff 11 July 1991 - * --------------------- - */ - return slot; + + /* --------------------- + * Return the slot so that we have the tuple descriptor + * when we need to save/restore them. -Jeff 11 July 1991 + * --------------------- + */ + return slot; } /* ---------------------------------------------------------------- - * ExecInitHash + * ExecInitHash * - * Init routine for Hash node + * Init routine for Hash node * ---------------------------------------------------------------- */ bool -ExecInitHash(Hash *node, EState *estate, Plan *parent) +ExecInitHash(Hash * node, EState * estate, Plan * parent) { - HashState *hashstate; - Plan *outerPlan; - - SO1_printf("ExecInitHash: %s\n", - "initializing hash node"); - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - hashstate = makeNode(HashState); - node->hashstate = hashstate; - hashstate->hashBatches = NULL; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent); - ExecAssignExprContext(estate, &hashstate->cstate); - + HashState *hashstate; + Plan *outerPlan; + + SO1_printf("ExecInitHash: %s\n", + "initializing hash node"); + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create state structure + * ---------------- + */ + hashstate = makeNode(HashState); + node->hashstate = hashstate; + hashstate->hashBatches = NULL; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent); + ExecAssignExprContext(estate, &hashstate->cstate); + #define HASH_NSLOTS 1 - /* ---------------- - * initialize our result slot - * ---------------- - */ - ExecInitResultTupleSlot(estate, &hashstate->cstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerPlan = outerPlan(node); - ExecInitNode(outerPlan, estate, (Plan *)node); - - /* ---------------- - * initialize tuple type. no need to initialize projection - * info because this node doesn't do projections - * ---------------- - */ - ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate); - hashstate->cstate.cs_ProjInfo = NULL; - - return TRUE; + /* ---------------- + * initialize our result slot + * ---------------- + */ + ExecInitResultTupleSlot(estate, &hashstate->cstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerPlan = outerPlan(node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize tuple type. no need to initialize projection + * info because this node doesn't do projections + * ---------------- + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate); + hashstate->cstate.cs_ProjInfo = NULL; + + return TRUE; } int -ExecCountSlotsHash(Hash *node) +ExecCountSlotsHash(Hash * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - HASH_NSLOTS; + HASH_NSLOTS; } /* --------------------------------------------------------------- - * ExecEndHash + * ExecEndHash * - * clean up routine for Hash node + * clean up routine for Hash node * ---------------------------------------------------------------- */ void -ExecEndHash(Hash *node) +ExecEndHash(Hash * node) { - HashState *hashstate; - Plan *outerPlan; - File *batches; - - /* ---------------- - * get info from the hash state - * ---------------- - */ - hashstate = node->hashstate; - batches = hashstate->hashBatches; - if (batches != NULL) - pfree(batches); - - /* ---------------- - * free projection info. no need to free result type info - * because that came from the outer plan... - * ---------------- - */ - ExecFreeProjectionInfo(&hashstate->cstate); - - /* ---------------- - * shut down the subplan - * ---------------- - */ - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan*)node); -} - -static RelativeAddr + HashState *hashstate; + Plan *outerPlan; + File *batches; + + /* ---------------- + * get info from the hash state + * ---------------- + */ + hashstate = node->hashstate; + batches = hashstate->hashBatches; + if (batches != NULL) + pfree(batches); + + /* ---------------- + * free projection info. no need to free result type info + * because that came from the outer plan... + * ---------------- + */ + ExecFreeProjectionInfo(&hashstate->cstate); + + /* ---------------- + * shut down the subplan + * ---------------- + */ + outerPlan = outerPlan(node); + ExecEndNode(outerPlan, (Plan *) node); +} + +static RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable) { - RelativeAddr p; - p = hashtable->top; - hashtable->top += size; - return p; + RelativeAddr p; + + p = hashtable->top; + hashtable->top += size; + return p; } /* ---------------------------------------------------------------- - * ExecHashTableCreate + * ExecHashTableCreate * - * create a hashtable in shared memory for hashjoin. + * create a hashtable in shared memory for hashjoin. * ---------------------------------------------------------------- */ -#define NTUP_PER_BUCKET 10 -#define FUDGE_FAC 1.5 +#define NTUP_PER_BUCKET 10 +#define FUDGE_FAC 1.5 HashJoinTable -ExecHashTableCreate(Hash *node) +ExecHashTableCreate(Hash * node) { - Plan *outerNode; - int nbatch; - int ntuples; - int tupsize; - IpcMemoryId shmid; - HashJoinTable hashtable; - HashBucket bucket; - int nbuckets; - int totalbuckets; - int bucketsize; - int i; - RelativeAddr *outerbatchNames; - RelativeAddr *outerbatchPos; - RelativeAddr *innerbatchNames; - RelativeAddr *innerbatchPos; - int *innerbatchSizes; - RelativeAddr tempname; - - nbatch = -1; - HashTBSize = NBuffers/2; - while (nbatch < 0) { + Plan *outerNode; + int nbatch; + int ntuples; + int tupsize; + IpcMemoryId shmid; + HashJoinTable hashtable; + HashBucket bucket; + int nbuckets; + int totalbuckets; + int bucketsize; + int i; + RelativeAddr *outerbatchNames; + RelativeAddr *outerbatchPos; + RelativeAddr *innerbatchNames; + RelativeAddr *innerbatchPos; + int *innerbatchSizes; + RelativeAddr tempname; + + nbatch = -1; + HashTBSize = NBuffers / 2; + while (nbatch < 0) + { + + /* + * determine number of batches for the hashjoin + */ + HashTBSize *= 2; + nbatch = ExecHashPartition(node); + } + /* ---------------- + * get information about the size of the relation + * ---------------- + */ + outerNode = outerPlan(node); + ntuples = outerNode->plan_size; + if (ntuples <= 0) + ntuples = 1000; /* XXX just a hack */ + tupsize = outerNode->plan_width + sizeof(HeapTupleData); + + /* + * totalbuckets is the total number of hash buckets needed for the + * entire relation + */ + totalbuckets = ceil((double) ntuples / NTUP_PER_BUCKET); + bucketsize = LONGALIGN(NTUP_PER_BUCKET * tupsize + sizeof(*bucket)); + /* - * determine number of batches for the hashjoin - */ - HashTBSize *= 2; - nbatch = ExecHashPartition(node); - } - /* ---------------- - * get information about the size of the relation - * ---------------- - */ - outerNode = outerPlan(node); - ntuples = outerNode->plan_size; - if (ntuples <= 0) - ntuples = 1000; /* XXX just a hack */ - tupsize = outerNode->plan_width + sizeof(HeapTupleData); - - /* - * totalbuckets is the total number of hash buckets needed for - * the entire relation - */ - totalbuckets = ceil((double)ntuples/NTUP_PER_BUCKET); - bucketsize = LONGALIGN (NTUP_PER_BUCKET * tupsize + sizeof(*bucket)); - - /* - * nbuckets is the number of hash buckets for the first pass - * of hybrid hashjoin - */ - nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC); - if (totalbuckets < nbuckets) - totalbuckets = nbuckets; - if (nbatch == 0) - nbuckets = totalbuckets; + * nbuckets is the number of hash buckets for the first pass of hybrid + * hashjoin + */ + nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC); + if (totalbuckets < nbuckets) + totalbuckets = nbuckets; + if (nbatch == 0) + nbuckets = totalbuckets; #ifdef HJDEBUG - printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets); + printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets); #endif - - /* ---------------- - * in non-parallel machines, we don't need to put the hash table - * in the shared memory. We just palloc it. - * ---------------- - */ - hashtable = (HashJoinTable)palloc((HashTBSize+1)*BLCKSZ); - shmid = 0; - - if (hashtable == NULL) { - elog(WARN, "not enough memory for hashjoin."); - } - /* ---------------- - * initialize the hash table header - * ---------------- - */ - hashtable->nbuckets = nbuckets; - hashtable->totalbuckets = totalbuckets; - hashtable->bucketsize = bucketsize; - hashtable->shmid = shmid; - hashtable->top = sizeof(HashTableData); - hashtable->bottom = HashTBSize * BLCKSZ; - /* - * hashtable->readbuf has to be long aligned!!! - */ - hashtable->readbuf = hashtable->bottom; - hashtable->nbatch = nbatch; - hashtable->curbatch = 0; - hashtable->pcount = hashtable->nprocess = 0; - if (nbatch > 0) { - /* --------------- - * allocate and initialize the outer batches - * --------------- - */ - outerbatchNames = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - outerbatchPos = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - for (i=0; i<nbatch; i++) { - tempname = hashTableAlloc(12, hashtable); - mk_hj_temp(ABSADDR(tempname)); - outerbatchNames[i] = tempname; - outerbatchPos[i] = -1; + + /* ---------------- + * in non-parallel machines, we don't need to put the hash table + * in the shared memory. We just palloc it. + * ---------------- + */ + hashtable = (HashJoinTable) palloc((HashTBSize + 1) * BLCKSZ); + shmid = 0; + + if (hashtable == NULL) + { + elog(WARN, "not enough memory for hashjoin."); + } + /* ---------------- + * initialize the hash table header + * ---------------- + */ + hashtable->nbuckets = nbuckets; + hashtable->totalbuckets = totalbuckets; + hashtable->bucketsize = bucketsize; + hashtable->shmid = shmid; + hashtable->top = sizeof(HashTableData); + hashtable->bottom = HashTBSize * BLCKSZ; + + /* + * hashtable->readbuf has to be long aligned!!! + */ + hashtable->readbuf = hashtable->bottom; + hashtable->nbatch = nbatch; + hashtable->curbatch = 0; + hashtable->pcount = hashtable->nprocess = 0; + if (nbatch > 0) + { + /* --------------- + * allocate and initialize the outer batches + * --------------- + */ + outerbatchNames = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + outerbatchPos = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + for (i = 0; i < nbatch; i++) + { + tempname = hashTableAlloc(12, hashtable); + mk_hj_temp(ABSADDR(tempname)); + outerbatchNames[i] = tempname; + outerbatchPos[i] = -1; + } + hashtable->outerbatchNames = RELADDR(outerbatchNames); + hashtable->outerbatchPos = RELADDR(outerbatchPos); + /* --------------- + * allocate and initialize the inner batches + * --------------- + */ + innerbatchNames = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + innerbatchPos = (RelativeAddr *) ABSADDR( + hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); + innerbatchSizes = (int *) ABSADDR( + hashTableAlloc(nbatch * sizeof(int), hashtable)); + for (i = 0; i < nbatch; i++) + { + tempname = hashTableAlloc(12, hashtable); + mk_hj_temp(ABSADDR(tempname)); + innerbatchNames[i] = tempname; + innerbatchPos[i] = -1; + innerbatchSizes[i] = 0; + } + hashtable->innerbatchNames = RELADDR(innerbatchNames); + hashtable->innerbatchPos = RELADDR(innerbatchPos); + hashtable->innerbatchSizes = RELADDR(innerbatchSizes); } - hashtable->outerbatchNames = RELADDR(outerbatchNames); - hashtable->outerbatchPos = RELADDR(outerbatchPos); - /* --------------- - * allocate and initialize the inner batches - * --------------- - */ - innerbatchNames = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - innerbatchPos = (RelativeAddr*)ABSADDR( - hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable)); - innerbatchSizes = (int*)ABSADDR( - hashTableAlloc(nbatch * sizeof(int), hashtable)); - for (i=0; i<nbatch; i++) { - tempname = hashTableAlloc(12, hashtable); - mk_hj_temp(ABSADDR(tempname)); - innerbatchNames[i] = tempname; - innerbatchPos[i] = -1; - innerbatchSizes[i] = 0; + else + { + hashtable->outerbatchNames = (RelativeAddr) NULL; + hashtable->outerbatchPos = (RelativeAddr) NULL; + hashtable->innerbatchNames = (RelativeAddr) NULL; + hashtable->innerbatchPos = (RelativeAddr) NULL; + hashtable->innerbatchSizes = (RelativeAddr) NULL; } - hashtable->innerbatchNames = RELADDR(innerbatchNames); - hashtable->innerbatchPos = RELADDR(innerbatchPos); - hashtable->innerbatchSizes = RELADDR(innerbatchSizes); - } - else { - hashtable->outerbatchNames = (RelativeAddr)NULL; - hashtable->outerbatchPos = (RelativeAddr)NULL; - hashtable->innerbatchNames = (RelativeAddr)NULL; - hashtable->innerbatchPos = (RelativeAddr)NULL; - hashtable->innerbatchSizes = (RelativeAddr)NULL; - } - - hashtable->batch = (RelativeAddr)LONGALIGN(hashtable->top + - bucketsize * nbuckets); - hashtable->overflownext=hashtable->batch + nbatch * BLCKSZ; - /* ---------------- - * initialize each hash bucket - * ---------------- - */ - bucket = (HashBucket)ABSADDR(hashtable->top); - for (i=0; i<nbuckets; i++) { - bucket->top = RELADDR((char*)bucket + sizeof(*bucket)); - bucket->bottom = bucket->top; - bucket->firstotuple = bucket->lastotuple = -1; - bucket = (HashBucket)LONGALIGN(((char*)bucket + bucketsize)); - } - return(hashtable); + + hashtable->batch = (RelativeAddr) LONGALIGN(hashtable->top + + bucketsize * nbuckets); + hashtable->overflownext = hashtable->batch + nbatch * BLCKSZ; + /* ---------------- + * initialize each hash bucket + * ---------------- + */ + bucket = (HashBucket) ABSADDR(hashtable->top); + for (i = 0; i < nbuckets; i++) + { + bucket->top = RELADDR((char *) bucket + sizeof(*bucket)); + bucket->bottom = bucket->top; + bucket->firstotuple = bucket->lastotuple = -1; + bucket = (HashBucket) LONGALIGN(((char *) bucket + bucketsize)); + } + return (hashtable); } /* ---------------------------------------------------------------- - * ExecHashTableInsert + * ExecHashTableInsert * - * insert a tuple into the hash table depending on the hash value - * it may just go to a tmp file for other batches + * insert a tuple into the hash table depending on the hash value + * it may just go to a tmp file for other batches * ---------------------------------------------------------------- */ void ExecHashTableInsert(HashJoinTable hashtable, - ExprContext *econtext, - Var *hashkey, - File *batches) + ExprContext * econtext, + Var * hashkey, + File * batches) { - TupleTableSlot *slot; - HeapTuple heapTuple; - HashBucket bucket; - int bucketno; - int nbatch; - int batchno; - char *buffer; - RelativeAddr *batchPos; - int *batchSizes; - char *pos; - - nbatch = hashtable->nbatch; - batchPos = (RelativeAddr*)ABSADDR(hashtable->innerbatchPos); - batchSizes = (int*)ABSADDR(hashtable->innerbatchSizes); - - slot = econtext->ecxt_innertuple; - heapTuple = slot->val; - + TupleTableSlot *slot; + HeapTuple heapTuple; + HashBucket bucket; + int bucketno; + int nbatch; + int batchno; + char *buffer; + RelativeAddr *batchPos; + int *batchSizes; + char *pos; + + nbatch = hashtable->nbatch; + batchPos = (RelativeAddr *) ABSADDR(hashtable->innerbatchPos); + batchSizes = (int *) ABSADDR(hashtable->innerbatchSizes); + + slot = econtext->ecxt_innertuple; + heapTuple = slot->val; + #ifdef HJDEBUG - printf("Inserting "); + printf("Inserting "); #endif - - bucketno = ExecHashGetBucket(hashtable, econtext, hashkey); - - /* ---------------- - * decide whether to put the tuple in the hash table or a tmp file - * ---------------- - */ - if (bucketno < hashtable->nbuckets) { - /* --------------- - * put the tuple in hash table - * --------------- - */ - bucket = (HashBucket) - (ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize); - if ((char*)LONGALIGN(ABSADDR(bucket->bottom)) - -(char*)bucket+heapTuple->t_len > hashtable->bucketsize) - ExecHashOverflowInsert(hashtable, bucket, heapTuple); - else { - memmove((char*)LONGALIGN(ABSADDR(bucket->bottom)), - heapTuple, - heapTuple->t_len); - bucket->bottom = - ((RelativeAddr)LONGALIGN(bucket->bottom) + heapTuple->t_len); + + bucketno = ExecHashGetBucket(hashtable, econtext, hashkey); + + /* ---------------- + * decide whether to put the tuple in the hash table or a tmp file + * ---------------- + */ + if (bucketno < hashtable->nbuckets) + { + /* --------------- + * put the tuple in hash table + * --------------- + */ + bucket = (HashBucket) + (ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize); + if ((char *) LONGALIGN(ABSADDR(bucket->bottom)) + - (char *) bucket + heapTuple->t_len > hashtable->bucketsize) + ExecHashOverflowInsert(hashtable, bucket, heapTuple); + else + { + memmove((char *) LONGALIGN(ABSADDR(bucket->bottom)), + heapTuple, + heapTuple->t_len); + bucket->bottom = + ((RelativeAddr) LONGALIGN(bucket->bottom) + heapTuple->t_len); + } + } + else + { + /* ----------------- + * put the tuple into a tmp file for other batches + * ----------------- + */ + batchno = (float) (bucketno - hashtable->nbuckets) / + (float) (hashtable->totalbuckets - hashtable->nbuckets) + * nbatch; + buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ; + batchSizes[batchno]++; + pos = (char *) + ExecHashJoinSaveTuple(heapTuple, + buffer, + batches[batchno], + (char *) ABSADDR(batchPos[batchno])); + batchPos[batchno] = RELADDR(pos); } - } - else { - /* ----------------- - * put the tuple into a tmp file for other batches - * ----------------- - */ - batchno = (float)(bucketno - hashtable->nbuckets)/ - (float)(hashtable->totalbuckets - hashtable->nbuckets) - * nbatch; - buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ; - batchSizes[batchno]++; - pos= (char *) - ExecHashJoinSaveTuple(heapTuple, - buffer, - batches[batchno], - (char*)ABSADDR(batchPos[batchno])); - batchPos[batchno] = RELADDR(pos); - } } /* ---------------------------------------------------------------- - * ExecHashTableDestroy + * ExecHashTableDestroy * - * destroy a hash table + * destroy a hash table * ---------------------------------------------------------------- */ void ExecHashTableDestroy(HashJoinTable hashtable) { - pfree(hashtable); + pfree(hashtable); } /* ---------------------------------------------------------------- - * ExecHashGetBucket + * ExecHashGetBucket * - * Get the hash value for a tuple + * Get the hash value for a tuple * ---------------------------------------------------------------- */ int ExecHashGetBucket(HashJoinTable hashtable, - ExprContext *econtext, - Var *hashkey) + ExprContext * econtext, + Var * hashkey) { - int bucketno; - Datum keyval; - bool isNull; - - - /* ---------------- - * Get the join attribute value of the tuple - * ---------------- - * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar: - * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97 - */ - keyval = ExecEvalExpr((Node*)hashkey, econtext, &isNull, NULL); - - /* - * keyval could be null, so we better point it to something - * valid before trying to run hashFunc on it. --djm 8/17/96 - */ - if(isNull) { - execConstByVal = 0; - execConstLen = 0; - keyval = (Datum)""; - } - - /* ------------------ - * compute the hash function - * ------------------ - */ - if (execConstByVal) - bucketno = - hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets; - else - bucketno = - hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets; + int bucketno; + Datum keyval; + bool isNull; + + + /* ---------------- + * Get the join attribute value of the tuple + * ---------------- + * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar: + * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97 + */ + keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL); + + /* + * keyval could be null, so we better point it to something valid + * before trying to run hashFunc on it. --djm 8/17/96 + */ + if (isNull) + { + execConstByVal = 0; + execConstLen = 0; + keyval = (Datum) ""; + } + + /* ------------------ + * compute the hash function + * ------------------ + */ + if (execConstByVal) + bucketno = + hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets; + else + bucketno = + hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets; #ifdef HJDEBUG - if (bucketno >= hashtable->nbuckets) - printf("hash(%d) = %d SAVED\n", keyval, bucketno); - else - printf("hash(%d) = %d\n", keyval, bucketno); + if (bucketno >= hashtable->nbuckets) + printf("hash(%d) = %d SAVED\n", keyval, bucketno); + else + printf("hash(%d) = %d\n", keyval, bucketno); #endif - - return(bucketno); + + return (bucketno); } /* ---------------------------------------------------------------- - * ExecHashOverflowInsert + * ExecHashOverflowInsert * - * insert into the overflow area of a hash bucket + * insert into the overflow area of a hash bucket * ---------------------------------------------------------------- */ static void ExecHashOverflowInsert(HashJoinTable hashtable, - HashBucket bucket, - HeapTuple heapTuple) + HashBucket bucket, + HeapTuple heapTuple) { - OverflowTuple otuple; - RelativeAddr newend; - OverflowTuple firstotuple; - OverflowTuple lastotuple; - - firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple); - lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple); - /* ---------------- - * see if we run out of overflow space - * ---------------- - */ - newend = (RelativeAddr)LONGALIGN(hashtable->overflownext + sizeof(*otuple) - + heapTuple->t_len); - if (newend > hashtable->bottom) { -#if 0 - elog(DEBUG, "hash table out of memory. expanding."); - /* ------------------ - * XXX this is a temporary hack - * eventually, recursive hash partitioning will be - * implemented - * ------------------ + OverflowTuple otuple; + RelativeAddr newend; + OverflowTuple firstotuple; + OverflowTuple lastotuple; + + firstotuple = (OverflowTuple) ABSADDR(bucket->firstotuple); + lastotuple = (OverflowTuple) ABSADDR(bucket->lastotuple); + /* ---------------- + * see if we run out of overflow space + * ---------------- */ - hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom; - hashtable = - (HashJoinTable)repalloc(hashtable, hashtable->bottom+BLCKSZ); - if (hashtable == NULL) { - perror("repalloc"); - elog(WARN, "can't expand hashtable."); - } + newend = (RelativeAddr) LONGALIGN(hashtable->overflownext + sizeof(*otuple) + + heapTuple->t_len); + if (newend > hashtable->bottom) + { +#if 0 + elog(DEBUG, "hash table out of memory. expanding."); + /* ------------------ + * XXX this is a temporary hack + * eventually, recursive hash partitioning will be + * implemented + * ------------------ + */ + hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom; + hashtable = + (HashJoinTable) repalloc(hashtable, hashtable->bottom + BLCKSZ); + if (hashtable == NULL) + { + perror("repalloc"); + elog(WARN, "can't expand hashtable."); + } #else - /* ------------------ - * XXX the temporary hack above doesn't work because things - * above us don't know that we've moved the hash table! - * - Chris Dunlop, <[email protected]> - * ------------------ - */ - elog(WARN, "hash table out of memory. Use -B parameter to increase buffers."); + /* ------------------ + * XXX the temporary hack above doesn't work because things + * above us don't know that we've moved the hash table! + * - Chris Dunlop, <[email protected]> + * ------------------ + */ + elog(WARN, "hash table out of memory. Use -B parameter to increase buffers."); #endif - } - - /* ---------------- - * establish the overflow chain - * ---------------- - */ - otuple = (OverflowTuple)ABSADDR(hashtable->overflownext); - hashtable->overflownext = newend; - if (firstotuple == NULL) - bucket->firstotuple = bucket->lastotuple = RELADDR(otuple); - else { - lastotuple->next = RELADDR(otuple); - bucket->lastotuple = RELADDR(otuple); - } - - /* ---------------- - * copy the tuple into the overflow area - * ---------------- - */ - otuple->next = -1; - otuple->tuple = RELADDR(LONGALIGN(((char*)otuple + sizeof(*otuple)))); - memmove(ABSADDR(otuple->tuple), - heapTuple, - heapTuple->t_len); + } + + /* ---------------- + * establish the overflow chain + * ---------------- + */ + otuple = (OverflowTuple) ABSADDR(hashtable->overflownext); + hashtable->overflownext = newend; + if (firstotuple == NULL) + bucket->firstotuple = bucket->lastotuple = RELADDR(otuple); + else + { + lastotuple->next = RELADDR(otuple); + bucket->lastotuple = RELADDR(otuple); + } + + /* ---------------- + * copy the tuple into the overflow area + * ---------------- + */ + otuple->next = -1; + otuple->tuple = RELADDR(LONGALIGN(((char *) otuple + sizeof(*otuple)))); + memmove(ABSADDR(otuple->tuple), + heapTuple, + heapTuple->t_len); } /* ---------------------------------------------------------------- - * ExecScanHashBucket + * ExecScanHashBucket * - * scan a hash bucket of matches + * scan a hash bucket of matches * ---------------------------------------------------------------- */ HeapTuple -ExecScanHashBucket(HashJoinState *hjstate, - HashBucket bucket, - HeapTuple curtuple, - List *hjclauses, - ExprContext *econtext) +ExecScanHashBucket(HashJoinState * hjstate, + HashBucket bucket, + HeapTuple curtuple, + List * hjclauses, + ExprContext * econtext) { - HeapTuple heapTuple; - bool qualResult; - OverflowTuple otuple = NULL; - OverflowTuple curotuple; - TupleTableSlot *inntuple; - OverflowTuple firstotuple; - OverflowTuple lastotuple; - HashJoinTable hashtable; - - hashtable = hjstate->hj_HashTable; - firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple); - lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple); - - /* ---------------- - * search the hash bucket - * ---------------- - */ - if (curtuple == NULL || curtuple < (HeapTuple)ABSADDR(bucket->bottom)) { - if (curtuple == NULL) - heapTuple = (HeapTuple) - LONGALIGN(ABSADDR(bucket->top)); - else - heapTuple = (HeapTuple) - LONGALIGN(((char*)curtuple+curtuple->t_len)); - - while (heapTuple < (HeapTuple)ABSADDR(bucket->bottom)) { - - inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ - hjstate->hj_HashTupleSlot, /* slot */ - InvalidBuffer,/* tuple has no buffer */ - false); /* do not pfree this tuple */ - - econtext->ecxt_innertuple = inntuple; - qualResult = ExecQual((List*)hjclauses, econtext); - - if (qualResult) - return heapTuple; - - heapTuple = (HeapTuple) - LONGALIGN(((char*)heapTuple+heapTuple->t_len)); + HeapTuple heapTuple; + bool qualResult; + OverflowTuple otuple = NULL; + OverflowTuple curotuple; + TupleTableSlot *inntuple; + OverflowTuple firstotuple; + OverflowTuple lastotuple; + HashJoinTable hashtable; + + hashtable = hjstate->hj_HashTable; + firstotuple = (OverflowTuple) ABSADDR(bucket->firstotuple); + lastotuple = (OverflowTuple) ABSADDR(bucket->lastotuple); + + /* ---------------- + * search the hash bucket + * ---------------- + */ + if (curtuple == NULL || curtuple < (HeapTuple) ABSADDR(bucket->bottom)) + { + if (curtuple == NULL) + heapTuple = (HeapTuple) + LONGALIGN(ABSADDR(bucket->top)); + else + heapTuple = (HeapTuple) + LONGALIGN(((char *) curtuple + curtuple->t_len)); + + while (heapTuple < (HeapTuple) ABSADDR(bucket->bottom)) + { + + inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ + hjstate->hj_HashTupleSlot, /* slot */ + InvalidBuffer, /* tuple has no buffer */ + false); /* do not pfree this tuple */ + + econtext->ecxt_innertuple = inntuple; + qualResult = ExecQual((List *) hjclauses, econtext); + + if (qualResult) + return heapTuple; + + heapTuple = (HeapTuple) + LONGALIGN(((char *) heapTuple + heapTuple->t_len)); + } + + if (firstotuple == NULL) + return NULL; + otuple = firstotuple; } - - if (firstotuple == NULL) - return NULL; - otuple = firstotuple; - } - - /* ---------------- - * search the overflow area of the hash bucket - * ---------------- - */ - if (otuple == NULL) { - curotuple = hjstate->hj_CurOTuple; - otuple = (OverflowTuple)ABSADDR(curotuple->next); - } - - while (otuple != NULL) { - heapTuple = (HeapTuple)ABSADDR(otuple->tuple); - - inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ - hjstate->hj_HashTupleSlot, /* slot */ - InvalidBuffer, /* SP?? this tuple has no buffer */ - false); /* do not pfree this tuple */ - - econtext->ecxt_innertuple = inntuple; - qualResult = ExecQual((List*)hjclauses, econtext); - - if (qualResult) { - hjstate->hj_CurOTuple = otuple; - return heapTuple; + + /* ---------------- + * search the overflow area of the hash bucket + * ---------------- + */ + if (otuple == NULL) + { + curotuple = hjstate->hj_CurOTuple; + otuple = (OverflowTuple) ABSADDR(curotuple->next); + } + + while (otuple != NULL) + { + heapTuple = (HeapTuple) ABSADDR(otuple->tuple); + + inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ + hjstate->hj_HashTupleSlot, /* slot */ + InvalidBuffer, /* SP?? this tuple has + * no buffer */ + false); /* do not pfree this tuple */ + + econtext->ecxt_innertuple = inntuple; + qualResult = ExecQual((List *) hjclauses, econtext); + + if (qualResult) + { + hjstate->hj_CurOTuple = otuple; + return heapTuple; + } + + otuple = (OverflowTuple) ABSADDR(otuple->next); } - - otuple = (OverflowTuple)ABSADDR(otuple->next); - } - - /* ---------------- - * no match - * ---------------- - */ - return NULL; + + /* ---------------- + * no match + * ---------------- + */ + return NULL; } /* ---------------------------------------------------------------- - * hashFunc + * hashFunc * - * the hash function, copied from Margo + * the hash function, copied from Margo * ---------------------------------------------------------------- */ static int hashFunc(char *key, int len) { - register unsigned int h; - register int l; - register unsigned char *k; - - /* - * If this is a variable length type, then 'k' points - * to a "struct varlena" and len == -1. - * NOTE: - * VARSIZE returns the "real" data length plus the sizeof the - * "vl_len" attribute of varlena (the length information). - * 'k' points to the beginning of the varlena struct, so - * we have to use "VARDATA" to find the beginning of the "real" - * data. - */ - if (len == -1) { - l = VARSIZE(key) - VARHDRSZ; - k = (unsigned char*) VARDATA(key); - } else { - l = len; - k = (unsigned char *) key; - } - - h = 0; - - /* - * Convert string to integer - */ - while (l--) h = h * PRIME1 ^ (*k++); - h %= PRIME2; - - return (h); + register unsigned int h; + register int l; + register unsigned char *k; + + /* + * If this is a variable length type, then 'k' points to a "struct + * varlena" and len == -1. NOTE: VARSIZE returns the "real" data + * length plus the sizeof the "vl_len" attribute of varlena (the + * length information). 'k' points to the beginning of the varlena + * struct, so we have to use "VARDATA" to find the beginning of the + * "real" data. + */ + if (len == -1) + { + l = VARSIZE(key) - VARHDRSZ; + k = (unsigned char *) VARDATA(key); + } + else + { + l = len; + k = (unsigned char *) key; + } + + h = 0; + + /* + * Convert string to integer + */ + while (l--) + h = h * PRIME1 ^ (*k++); + h %= PRIME2; + + return (h); } /* ---------------------------------------------------------------- - * ExecHashPartition + * ExecHashPartition * - * determine the number of batches needed for a hashjoin + * determine the number of batches needed for a hashjoin * ---------------------------------------------------------------- */ static int -ExecHashPartition(Hash *node) +ExecHashPartition(Hash * node) { - Plan *outerNode; - int b; - int pages; - int ntuples; - int tupsize; - - /* - * get size information for plan node - */ - outerNode = outerPlan(node); - ntuples = outerNode->plan_size; - if (ntuples == 0) ntuples = 1000; - tupsize = outerNode->plan_width + sizeof(HeapTupleData); - pages = ceil((double)ntuples * tupsize * FUDGE_FAC / BLCKSZ); - - /* - * if amount of buffer space below hashjoin threshold, - * return negative - */ - if (ceil(sqrt((double)pages)) > HashTBSize) - return -1; - if (pages <= HashTBSize) - b = 0; /* fit in memory, no partitioning */ - else - b = ceil((double)(pages - HashTBSize)/(double)(HashTBSize - 1)); - - return b; + Plan *outerNode; + int b; + int pages; + int ntuples; + int tupsize; + + /* + * get size information for plan node + */ + outerNode = outerPlan(node); + ntuples = outerNode->plan_size; + if (ntuples == 0) + ntuples = 1000; + tupsize = outerNode->plan_width + sizeof(HeapTupleData); + pages = ceil((double) ntuples * tupsize * FUDGE_FAC / BLCKSZ); + + /* + * if amount of buffer space below hashjoin threshold, return negative + */ + if (ceil(sqrt((double) pages)) > HashTBSize) + return -1; + if (pages <= HashTBSize) + b = 0; /* fit in memory, no partitioning */ + else + b = ceil((double) (pages - HashTBSize) / (double) (HashTBSize - 1)); + + return b; } /* ---------------------------------------------------------------- - * ExecHashTableReset + * ExecHashTableReset * - * reset hash table header for new batch + * reset hash table header for new batch * ---------------------------------------------------------------- */ void ExecHashTableReset(HashJoinTable hashtable, int ntuples) { - int i; - HashBucket bucket; - - hashtable->nbuckets = hashtable->totalbuckets - = ceil((double)ntuples/NTUP_PER_BUCKET); - - hashtable->overflownext = hashtable->top + hashtable->bucketsize * - hashtable->nbuckets; - - bucket = (HashBucket)ABSADDR(hashtable->top); - for (i=0; i<hashtable->nbuckets; i++) { - bucket->top = RELADDR((char*)bucket + sizeof(*bucket)); - bucket->bottom = bucket->top; - bucket->firstotuple = bucket->lastotuple = -1; - bucket = (HashBucket)((char*)bucket + hashtable->bucketsize); - } - hashtable->pcount = hashtable->nprocess; + int i; + HashBucket bucket; + + hashtable->nbuckets = hashtable->totalbuckets + = ceil((double) ntuples / NTUP_PER_BUCKET); + + hashtable->overflownext = hashtable->top + hashtable->bucketsize * + hashtable->nbuckets; + + bucket = (HashBucket) ABSADDR(hashtable->top); + for (i = 0; i < hashtable->nbuckets; i++) + { + bucket->top = RELADDR((char *) bucket + sizeof(*bucket)); + bucket->bottom = bucket->top; + bucket->firstotuple = bucket->lastotuple = -1; + bucket = (HashBucket) ((char *) bucket + hashtable->bucketsize); + } + hashtable->pcount = hashtable->nprocess; } -static int hjtmpcnt = 0; +static int hjtmpcnt = 0; static void mk_hj_temp(char *tempname) { - sprintf(tempname, "HJ%d.%d", (int)getpid(), hjtmpcnt); - hjtmpcnt = (hjtmpcnt + 1) % 1000; + sprintf(tempname, "HJ%d.%d", (int) getpid(), hjtmpcnt); + hjtmpcnt = (hjtmpcnt + 1) % 1000; } - - - diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index c9f24efe193..3548e38cc86 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * nodeHashjoin.c-- - * Routines to handle hash join nodes + * Routines to handle hash join nodes * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.5 1997/08/19 21:31:09 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.6 1997/09/07 04:41:33 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,8 +20,8 @@ #include "postgres.h" -#include "storage/bufmgr.h" /* for BLCKSZ */ -#include "storage/fd.h" /* for SEEK_ */ +#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "storage/fd.h" /* for SEEK_ */ #include "executor/executor.h" #include "executor/execdebug.h" #include "executor/nodeHash.h" @@ -33,775 +33,818 @@ #include "utils/palloc.h" static TupleTableSlot * -ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate); + ExecHashJoinOuterGetTuple(Plan * node, Plan * parent, HashJoinState * hjstate); static TupleTableSlot * -ExecHashJoinGetSavedTuple(HashJoinState *hjstate, char *buffer, - File file, TupleTableSlot *tupleSlot, int *block, char **position); +ExecHashJoinGetSavedTuple(HashJoinState * hjstate, char *buffer, + File file, TupleTableSlot * tupleSlot, int *block, char **position); -static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, - int nbatch); +static int +ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, + int nbatch); -static int ExecHashJoinNewBatch(HashJoinState *hjstate); +static int ExecHashJoinNewBatch(HashJoinState * hjstate); /* ---------------------------------------------------------------- - * ExecHashJoin + * ExecHashJoin * - * This function implements the Hybrid Hashjoin algorithm. - * recursive partitioning remains to be added. - * Note: the relation we build hash table on is the inner - * the other one is outer. + * This function implements the Hybrid Hashjoin algorithm. + * recursive partitioning remains to be added. + * Note: the relation we build hash table on is the inner + * the other one is outer. * ---------------------------------------------------------------- */ -TupleTableSlot * /* return: a tuple or NULL */ -ExecHashJoin(HashJoin *node) +TupleTableSlot * /* return: a tuple or NULL */ +ExecHashJoin(HashJoin * node) { - HashJoinState *hjstate; - EState *estate; - Plan *outerNode; - Hash *hashNode; - List *hjclauses; - Expr *clause; - List *qual; - ScanDirection dir; - TupleTableSlot *inntuple; - Var *outerVar; - ExprContext *econtext; - - HashJoinTable hashtable; - int bucketno; - HashBucket bucket; - HeapTuple curtuple; - - bool qualResult; - - TupleTableSlot *outerTupleSlot; - TupleTableSlot *innerTupleSlot; - int nbatch; - int curbatch; - File *outerbatches; - RelativeAddr *outerbatchNames; - RelativeAddr *outerbatchPos; - Var *innerhashkey; - int batch; - int batchno; - char *buffer; - int i; - bool hashPhaseDone; - char *pos; - - /* ---------------- - * get information from HashJoin node - * ---------------- - */ - hjstate = node->hashjoinstate; - hjclauses = node->hashclauses; - clause = lfirst(hjclauses); - estate = node->join.state; - qual = node->join.qual; - hashNode = (Hash *)innerPlan(node); - outerNode = outerPlan(node); - hashPhaseDone = node->hashdone; - - dir = estate->es_direction; - - /* ----------------- - * get information from HashJoin state - * ----------------- - */ - hashtable = hjstate->hj_HashTable; - bucket = hjstate->hj_CurBucket; - curtuple = hjstate->hj_CurTuple; - - /* -------------------- - * initialize expression context - * -------------------- - */ - econtext = hjstate->jstate.cs_ExprContext; - - if (hjstate->jstate.cs_TupFromTlist) { - TupleTableSlot *result; - bool isDone; - - result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); - if (!isDone) - return result; - } - /* ---------------- - * if this is the first call, build the hash table for inner relation - * ---------------- - */ - if (!hashPhaseDone) { /* if the hash phase not completed */ - hashtable = node->hashjointable; - if (hashtable == NULL) { /* if the hash table has not been created */ - /* ---------------- - * create the hash table - * ---------------- - */ - hashtable = ExecHashTableCreate(hashNode); - hjstate->hj_HashTable = hashtable; - innerhashkey = hashNode->hashkey; - hjstate->hj_InnerHashKey = innerhashkey; - - /* ---------------- - * execute the Hash node, to build the hash table - * ---------------- - */ - hashNode->hashtable = hashtable; - innerTupleSlot = ExecProcNode((Plan *)hashNode, (Plan*) node); - } - bucket = NULL; - curtuple = NULL; - curbatch = 0; - node->hashdone = true; - } - nbatch = hashtable->nbatch; - outerbatches = hjstate->hj_OuterBatches; - if (nbatch > 0 && outerbatches == NULL) { /* if needs hash partition */ + HashJoinState *hjstate; + EState *estate; + Plan *outerNode; + Hash *hashNode; + List *hjclauses; + Expr *clause; + List *qual; + ScanDirection dir; + TupleTableSlot *inntuple; + Var *outerVar; + ExprContext *econtext; + + HashJoinTable hashtable; + int bucketno; + HashBucket bucket; + HeapTuple curtuple; + + bool qualResult; + + TupleTableSlot *outerTupleSlot; + TupleTableSlot *innerTupleSlot; + int nbatch; + int curbatch; + File *outerbatches; + RelativeAddr *outerbatchNames; + RelativeAddr *outerbatchPos; + Var *innerhashkey; + int batch; + int batchno; + char *buffer; + int i; + bool hashPhaseDone; + char *pos; + + /* ---------------- + * get information from HashJoin node + * ---------------- + */ + hjstate = node->hashjoinstate; + hjclauses = node->hashclauses; + clause = lfirst(hjclauses); + estate = node->join.state; + qual = node->join.qual; + hashNode = (Hash *) innerPlan(node); + outerNode = outerPlan(node); + hashPhaseDone = node->hashdone; + + dir = estate->es_direction; + /* ----------------- - * allocate space for file descriptors of outer batch files - * then open the batch files in the current process + * get information from HashJoin state * ----------------- */ - innerhashkey = hashNode->hashkey; - hjstate->hj_InnerHashKey = innerhashkey; - outerbatchNames = (RelativeAddr*) - ABSADDR(hashtable->outerbatchNames); - outerbatches = (File*) - palloc(nbatch * sizeof(File)); - for (i=0; i<nbatch; i++) { - outerbatches[i] = FileNameOpenFile( - ABSADDR(outerbatchNames[i]), - O_CREAT | O_RDWR, 0600); - } - hjstate->hj_OuterBatches = outerbatches; + hashtable = hjstate->hj_HashTable; + bucket = hjstate->hj_CurBucket; + curtuple = hjstate->hj_CurTuple; - /* ------------------ - * get the inner batch file descriptors from the - * hash node - * ------------------ - */ - hjstate->hj_InnerBatches = - hashNode->hashstate->hashBatches; - } - outerbatchPos = (RelativeAddr*)ABSADDR(hashtable->outerbatchPos); - curbatch = hashtable->curbatch; - outerbatchNames = (RelativeAddr*)ABSADDR(hashtable->outerbatchNames); - - /* ---------------- - * Now get an outer tuple and probe into the hash table for matches - * ---------------- - */ - outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot; - outerVar = get_leftop(clause); - - bucketno = -1; /* if bucketno remains -1, means use old outer tuple */ - if (TupIsNull(outerTupleSlot)) { - /* - * if the current outer tuple is nil, get a new one + /* -------------------- + * initialize expression context + * -------------------- */ - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate); - - while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) { - /* - * if the current batch runs out, switch to new batch - */ - curbatch = ExecHashJoinNewBatch(hjstate); - if (curbatch > nbatch) { - /* - * when the last batch runs out, clean up - */ - ExecHashTableDestroy(hashtable); - hjstate->hj_HashTable = NULL; - return NULL; - } - else - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate); + econtext = hjstate->jstate.cs_ExprContext; + + if (hjstate->jstate.cs_TupFromTlist) + { + TupleTableSlot *result; + bool isDone; + + result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); + if (!isDone) + return result; } - /* - * now we get an outer tuple, find the corresponding bucket for - * this tuple from the hash table - */ - econtext->ecxt_outertuple = outerTupleSlot; - -#ifdef HJDEBUG - printf("Probing "); -#endif - bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); - bucket=(HashBucket)(ABSADDR(hashtable->top) - + bucketno * hashtable->bucketsize); - } - - for (;;) { /* ---------------- - * Now we've got an outer tuple and the corresponding hash bucket, - * but this tuple may not belong to the current batch. + * if this is the first call, build the hash table for inner relation * ---------------- */ - if (curbatch == 0 && bucketno != -1) /* if this is the first pass */ - batch = ExecHashJoinGetBatch(bucketno, hashtable, nbatch); - else - batch = 0; - if (batch > 0) { - /* - * if the current outer tuple does not belong to - * the current batch, save to the tmp file for - * the corresponding batch. - */ - buffer = ABSADDR(hashtable->batch) + (batch - 1) * BLCKSZ; - batchno = batch - 1; - pos = ExecHashJoinSaveTuple(outerTupleSlot->val, - buffer, - outerbatches[batchno], - ABSADDR(outerbatchPos[batchno])); - - outerbatchPos[batchno] = RELADDR(pos); + if (!hashPhaseDone) + { /* if the hash phase not completed */ + hashtable = node->hashjointable; + if (hashtable == NULL) + { /* if the hash table has not been created */ + /* ---------------- + * create the hash table + * ---------------- + */ + hashtable = ExecHashTableCreate(hashNode); + hjstate->hj_HashTable = hashtable; + innerhashkey = hashNode->hashkey; + hjstate->hj_InnerHashKey = innerhashkey; + + /* ---------------- + * execute the Hash node, to build the hash table + * ---------------- + */ + hashNode->hashtable = hashtable; + innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node); + } + bucket = NULL; + curtuple = NULL; + curbatch = 0; + node->hashdone = true; } - else if (bucket != NULL) { - do { - /* - * scan the hash bucket for matches + nbatch = hashtable->nbatch; + outerbatches = hjstate->hj_OuterBatches; + if (nbatch > 0 && outerbatches == NULL) + { /* if needs hash partition */ + /* ----------------- + * allocate space for file descriptors of outer batch files + * then open the batch files in the current process + * ----------------- */ - curtuple = ExecScanHashBucket(hjstate, - bucket, - curtuple, - hjclauses, - econtext); - - if (curtuple != NULL) { - /* - * we've got a match, but still need to test qpqual - */ - inntuple = ExecStoreTuple(curtuple, - hjstate->hj_HashTupleSlot, - InvalidBuffer, - false); /* don't pfree this tuple */ - - econtext->ecxt_innertuple = inntuple; - - /* ---------------- - * test to see if we pass the qualification - * ---------------- - */ - qualResult = ExecQual((List*)qual, econtext); - - /* ---------------- - * if we pass the qual, then save state for next call and - * have ExecProject form the projection, store it - * in the tuple table, and return the slot. - * ---------------- - */ - if (qualResult) { - ProjectionInfo *projInfo; - TupleTableSlot *result; - bool isDone; - - hjstate->hj_CurBucket = bucket; - hjstate->hj_CurTuple = curtuple; - hashtable->curbatch = curbatch; - hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot; - - projInfo = hjstate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - hjstate->jstate.cs_TupFromTlist = !isDone; - return result; - } + innerhashkey = hashNode->hashkey; + hjstate->hj_InnerHashKey = innerhashkey; + outerbatchNames = (RelativeAddr *) + ABSADDR(hashtable->outerbatchNames); + outerbatches = (File *) + palloc(nbatch * sizeof(File)); + for (i = 0; i < nbatch; i++) + { + outerbatches[i] = FileNameOpenFile( + ABSADDR(outerbatchNames[i]), + O_CREAT | O_RDWR, 0600); } - } - while (curtuple != NULL); + hjstate->hj_OuterBatches = outerbatches; + + /* ------------------ + * get the inner batch file descriptors from the + * hash node + * ------------------ + */ + hjstate->hj_InnerBatches = + hashNode->hashstate->hashBatches; } - + outerbatchPos = (RelativeAddr *) ABSADDR(hashtable->outerbatchPos); + curbatch = hashtable->curbatch; + outerbatchNames = (RelativeAddr *) ABSADDR(hashtable->outerbatchNames); + /* ---------------- - * Now the current outer tuple has run out of matches, - * so we free it and get a new outer tuple. + * Now get an outer tuple and probe into the hash table for matches * ---------------- */ - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*) node, hjstate); - - while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) { - /* - * if the current batch runs out, switch to new batch - */ - curbatch = ExecHashJoinNewBatch(hjstate); - if (curbatch > nbatch) { + outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot; + outerVar = get_leftop(clause); + + bucketno = -1; /* if bucketno remains -1, means use old + * outer tuple */ + if (TupIsNull(outerTupleSlot)) + { + /* - * when the last batch runs out, clean up + * if the current outer tuple is nil, get a new one */ - ExecHashTableDestroy(hashtable); - hjstate->hj_HashTable = NULL; - return NULL; - } - else - outerTupleSlot = (TupleTableSlot*) - ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate); + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + + while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) + { + + /* + * if the current batch runs out, switch to new batch + */ + curbatch = ExecHashJoinNewBatch(hjstate); + if (curbatch > nbatch) + { + + /* + * when the last batch runs out, clean up + */ + ExecHashTableDestroy(hashtable); + hjstate->hj_HashTable = NULL; + return NULL; + } + else + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + } + + /* + * now we get an outer tuple, find the corresponding bucket for + * this tuple from the hash table + */ + econtext->ecxt_outertuple = outerTupleSlot; + +#ifdef HJDEBUG + printf("Probing "); +#endif + bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); + bucket = (HashBucket) (ABSADDR(hashtable->top) + + bucketno * hashtable->bucketsize); } - - /* ---------------- - * Now get the corresponding hash bucket for the new - * outer tuple. - * ---------------- - */ - econtext->ecxt_outertuple = outerTupleSlot; + + for (;;) + { + /* ---------------- + * Now we've got an outer tuple and the corresponding hash bucket, + * but this tuple may not belong to the current batch. + * ---------------- + */ + if (curbatch == 0 && bucketno != -1) /* if this is the first + * pass */ + batch = ExecHashJoinGetBatch(bucketno, hashtable, nbatch); + else + batch = 0; + if (batch > 0) + { + + /* + * if the current outer tuple does not belong to the current + * batch, save to the tmp file for the corresponding batch. + */ + buffer = ABSADDR(hashtable->batch) + (batch - 1) * BLCKSZ; + batchno = batch - 1; + pos = ExecHashJoinSaveTuple(outerTupleSlot->val, + buffer, + outerbatches[batchno], + ABSADDR(outerbatchPos[batchno])); + + outerbatchPos[batchno] = RELADDR(pos); + } + else if (bucket != NULL) + { + do + { + + /* + * scan the hash bucket for matches + */ + curtuple = ExecScanHashBucket(hjstate, + bucket, + curtuple, + hjclauses, + econtext); + + if (curtuple != NULL) + { + + /* + * we've got a match, but still need to test qpqual + */ + inntuple = ExecStoreTuple(curtuple, + hjstate->hj_HashTupleSlot, + InvalidBuffer, + false); /* don't pfree this + * tuple */ + + econtext->ecxt_innertuple = inntuple; + + /* ---------------- + * test to see if we pass the qualification + * ---------------- + */ + qualResult = ExecQual((List *) qual, econtext); + + /* ---------------- + * if we pass the qual, then save state for next call and + * have ExecProject form the projection, store it + * in the tuple table, and return the slot. + * ---------------- + */ + if (qualResult) + { + ProjectionInfo *projInfo; + TupleTableSlot *result; + bool isDone; + + hjstate->hj_CurBucket = bucket; + hjstate->hj_CurTuple = curtuple; + hashtable->curbatch = curbatch; + hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot; + + projInfo = hjstate->jstate.cs_ProjInfo; + result = ExecProject(projInfo, &isDone); + hjstate->jstate.cs_TupFromTlist = !isDone; + return result; + } + } + } + while (curtuple != NULL); + } + + /* ---------------- + * Now the current outer tuple has run out of matches, + * so we free it and get a new outer tuple. + * ---------------- + */ + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + + while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) + { + + /* + * if the current batch runs out, switch to new batch + */ + curbatch = ExecHashJoinNewBatch(hjstate); + if (curbatch > nbatch) + { + + /* + * when the last batch runs out, clean up + */ + ExecHashTableDestroy(hashtable); + hjstate->hj_HashTable = NULL; + return NULL; + } + else + outerTupleSlot = (TupleTableSlot *) + ExecHashJoinOuterGetTuple(outerNode, (Plan *) node, hjstate); + } + + /* ---------------- + * Now get the corresponding hash bucket for the new + * outer tuple. + * ---------------- + */ + econtext->ecxt_outertuple = outerTupleSlot; #ifdef HJDEBUG - printf("Probing "); + printf("Probing "); #endif - bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); - bucket=(HashBucket)(ABSADDR(hashtable->top) - + bucketno * hashtable->bucketsize); - curtuple = NULL; - } + bucketno = ExecHashGetBucket(hashtable, econtext, outerVar); + bucket = (HashBucket) (ABSADDR(hashtable->top) + + bucketno * hashtable->bucketsize); + curtuple = NULL; + } } /* ---------------------------------------------------------------- - * ExecInitHashJoin + * ExecInitHashJoin * - * Init routine for HashJoin node. + * Init routine for HashJoin node. * ---------------------------------------------------------------- */ -bool /* return: initialization status */ -ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) +bool /* return: initialization status */ +ExecInitHashJoin(HashJoin * node, EState * estate, Plan * parent) { - HashJoinState *hjstate; - Plan *outerNode; - Hash *hashNode; - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->join.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - hjstate = makeNode(HashJoinState); - - node->hashjoinstate = hjstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent); - ExecAssignExprContext(estate, &hjstate->jstate); - + HashJoinState *hjstate; + Plan *outerNode; + Hash *hashNode; + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->join.state = estate; + + /* ---------------- + * create state structure + * ---------------- + */ + hjstate = makeNode(HashJoinState); + + node->hashjoinstate = hjstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent); + ExecAssignExprContext(estate, &hjstate->jstate); + #define HASHJOIN_NSLOTS 2 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &hjstate->jstate); - ExecInitOuterTupleSlot(estate, hjstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerNode = outerPlan((Plan *)node); - hashNode = (Hash*)innerPlan((Plan *)node); - - ExecInitNode(outerNode, estate, (Plan *) node); - ExecInitNode((Plan*)hashNode, estate, (Plan *) node); - - /* ---------------- - * now for some voodoo. our temporary tuple slot - * is actually the result tuple slot of the Hash node - * (which is our inner plan). we do this because Hash - * nodes don't return tuples via ExecProcNode() -- instead - * the hash join node uses ExecScanHashBucket() to get - * at the contents of the hash table. -cim 6/9/91 - * ---------------- - */ - { - HashState *hashstate = hashNode->hashstate; - TupleTableSlot *slot = - hashstate->cstate.cs_ResultTupleSlot; - hjstate->hj_HashTupleSlot = slot; - } - hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = - ExecGetTupType(outerNode); - + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &hjstate->jstate); + ExecInitOuterTupleSlot(estate, hjstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerNode = outerPlan((Plan *) node); + hashNode = (Hash *) innerPlan((Plan *) node); + + ExecInitNode(outerNode, estate, (Plan *) node); + ExecInitNode((Plan *) hashNode, estate, (Plan *) node); + + /* ---------------- + * now for some voodoo. our temporary tuple slot + * is actually the result tuple slot of the Hash node + * (which is our inner plan). we do this because Hash + * nodes don't return tuples via ExecProcNode() -- instead + * the hash join node uses ExecScanHashBucket() to get + * at the contents of the hash table. -cim 6/9/91 + * ---------------- + */ + { + HashState *hashstate = hashNode->hashstate; + TupleTableSlot *slot = + hashstate->cstate.cs_ResultTupleSlot; + + hjstate->hj_HashTupleSlot = slot; + } + hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = + ExecGetTupType(outerNode); + /* - hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = - ExecGetExecTupDesc(outerNode); + hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = + ExecGetExecTupDesc(outerNode); */ - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan*) node, &hjstate->jstate); - ExecAssignProjectionInfo((Plan*) node, &hjstate->jstate); - - /* ---------------- - * XXX comment me - * ---------------- - */ - - node->hashdone = false; - - hjstate->hj_HashTable = (HashJoinTable)NULL; - hjstate->hj_HashTableShmId = (IpcMemoryId)0; - hjstate->hj_CurBucket = (HashBucket )NULL; - hjstate->hj_CurTuple = (HeapTuple )NULL; - hjstate->hj_CurOTuple = (OverflowTuple )NULL; - hjstate->hj_InnerHashKey = (Var*)NULL; - hjstate->hj_OuterBatches = (File*)NULL; - hjstate->hj_InnerBatches = (File*)NULL; - hjstate->hj_OuterReadPos = (char*)NULL; - hjstate->hj_OuterReadBlk = (int)0; - - hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot*) NULL; - hjstate->jstate.cs_TupFromTlist = (bool) false; - - return TRUE; + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate); + ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate); + + /* ---------------- + * XXX comment me + * ---------------- + */ + + node->hashdone = false; + + hjstate->hj_HashTable = (HashJoinTable) NULL; + hjstate->hj_HashTableShmId = (IpcMemoryId) 0; + hjstate->hj_CurBucket = (HashBucket) NULL; + hjstate->hj_CurTuple = (HeapTuple) NULL; + hjstate->hj_CurOTuple = (OverflowTuple) NULL; + hjstate->hj_InnerHashKey = (Var *) NULL; + hjstate->hj_OuterBatches = (File *) NULL; + hjstate->hj_InnerBatches = (File *) NULL; + hjstate->hj_OuterReadPos = (char *) NULL; + hjstate->hj_OuterReadBlk = (int) 0; + + hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL; + hjstate->jstate.cs_TupFromTlist = (bool) false; + + return TRUE; } int -ExecCountSlotsHashJoin(HashJoin *node) +ExecCountSlotsHashJoin(HashJoin * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - HASHJOIN_NSLOTS; + HASHJOIN_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndHashJoin + * ExecEndHashJoin * - * clean up routine for HashJoin node + * clean up routine for HashJoin node * ---------------------------------------------------------------- */ void -ExecEndHashJoin(HashJoin *node) +ExecEndHashJoin(HashJoin * node) { - HashJoinState *hjstate; - - /* ---------------- - * get info from the HashJoin state - * ---------------- - */ - hjstate = node->hashjoinstate; - - /* ---------------- - * free hash table in case we end plan before all tuples are retrieved - * --------------- - */ - if (hjstate->hj_HashTable) { - ExecHashTableDestroy(hjstate->hj_HashTable); - hjstate->hj_HashTable = NULL; - } - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(hjstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&hjstate->jstate); - - /* ---------------- - * clean up subtrees - * ---------------- - */ - ExecEndNode(outerPlan((Plan *) node), (Plan*)node); - ExecEndNode(innerPlan((Plan *) node), (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot); - ExecClearTuple(hjstate->hj_OuterTupleSlot); - ExecClearTuple(hjstate->hj_HashTupleSlot); - + HashJoinState *hjstate; + + /* ---------------- + * get info from the HashJoin state + * ---------------- + */ + hjstate = node->hashjoinstate; + + /* ---------------- + * free hash table in case we end plan before all tuples are retrieved + * --------------- + */ + if (hjstate->hj_HashTable) + { + ExecHashTableDestroy(hjstate->hj_HashTable); + hjstate->hj_HashTable = NULL; + } + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(hjstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&hjstate->jstate); + + /* ---------------- + * clean up subtrees + * ---------------- + */ + ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + ExecEndNode(innerPlan((Plan *) node), (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot); + ExecClearTuple(hjstate->hj_OuterTupleSlot); + ExecClearTuple(hjstate->hj_HashTupleSlot); + } /* ---------------------------------------------------------------- - * ExecHashJoinOuterGetTuple + * ExecHashJoinOuterGetTuple * - * get the next outer tuple for hashjoin: either by - * executing a plan node as in the first pass, or from - * the tmp files for the hashjoin batches. + * get the next outer tuple for hashjoin: either by + * executing a plan node as in the first pass, or from + * the tmp files for the hashjoin batches. * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate) +ExecHashJoinOuterGetTuple(Plan * node, Plan * parent, HashJoinState * hjstate) { - TupleTableSlot *slot; - HashJoinTable hashtable; - int curbatch; - File *outerbatches; - char *outerreadPos; - int batchno; - char *outerreadBuf; - int outerreadBlk; - - hashtable = hjstate->hj_HashTable; - curbatch = hashtable->curbatch; - - if (curbatch == 0) { /* if it is the first pass */ - slot = ExecProcNode(node, parent); + TupleTableSlot *slot; + HashJoinTable hashtable; + int curbatch; + File *outerbatches; + char *outerreadPos; + int batchno; + char *outerreadBuf; + int outerreadBlk; + + hashtable = hjstate->hj_HashTable; + curbatch = hashtable->curbatch; + + if (curbatch == 0) + { /* if it is the first pass */ + slot = ExecProcNode(node, parent); + return slot; + } + + /* + * otherwise, read from the tmp files + */ + outerbatches = hjstate->hj_OuterBatches; + outerreadPos = hjstate->hj_OuterReadPos; + outerreadBlk = hjstate->hj_OuterReadBlk; + outerreadBuf = ABSADDR(hashtable->readbuf); + batchno = curbatch - 1; + + slot = ExecHashJoinGetSavedTuple(hjstate, + outerreadBuf, + outerbatches[batchno], + hjstate->hj_OuterTupleSlot, + &outerreadBlk, + &outerreadPos); + + hjstate->hj_OuterReadPos = outerreadPos; + hjstate->hj_OuterReadBlk = outerreadBlk; + return slot; - } - - /* - * otherwise, read from the tmp files - */ - outerbatches = hjstate->hj_OuterBatches; - outerreadPos = hjstate->hj_OuterReadPos; - outerreadBlk = hjstate->hj_OuterReadBlk; - outerreadBuf = ABSADDR(hashtable->readbuf); - batchno = curbatch - 1; - - slot = ExecHashJoinGetSavedTuple(hjstate, - outerreadBuf, - outerbatches[batchno], - hjstate->hj_OuterTupleSlot, - &outerreadBlk, - &outerreadPos); - - hjstate->hj_OuterReadPos = outerreadPos; - hjstate->hj_OuterReadBlk = outerreadBlk; - - return slot; } /* ---------------------------------------------------------------- - * ExecHashJoinGetSavedTuple + * ExecHashJoinGetSavedTuple * - * read the next tuple from a tmp file using a certain buffer + * read the next tuple from a tmp file using a certain buffer * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecHashJoinGetSavedTuple(HashJoinState *hjstate, - char *buffer, - File file, - TupleTableSlot *tupleSlot, - int *block, /* return parameter */ - char **position) /* return parameter */ +ExecHashJoinGetSavedTuple(HashJoinState * hjstate, + char *buffer, + File file, + TupleTableSlot * tupleSlot, + int *block, /* return parameter */ + char **position) /* return parameter */ { - char *bufstart; - char *bufend; - int cc; - HeapTuple heapTuple; - HashJoinTable hashtable; - - hashtable = hjstate->hj_HashTable; - bufend = buffer + *(long*)buffer; - bufstart = (char*)(buffer + sizeof(long)); - if ((*position == NULL) || (*position >= bufend)) { - if (*position == NULL) - (*block) = 0; - else - (*block)++; - FileSeek(file, *block * BLCKSZ, SEEK_SET); - cc = FileRead(file, buffer, BLCKSZ); - NDirectFileRead++; - if (cc < 0) - perror("FileRead"); - if (cc == 0) /* end of file */ - return NULL; - else - (*position) = bufstart; - } - heapTuple = (HeapTuple) (*position); - (*position) = (char*)LONGALIGN(*position + heapTuple->t_len); - - return ExecStoreTuple(heapTuple,tupleSlot,InvalidBuffer,false); + char *bufstart; + char *bufend; + int cc; + HeapTuple heapTuple; + HashJoinTable hashtable; + + hashtable = hjstate->hj_HashTable; + bufend = buffer + *(long *) buffer; + bufstart = (char *) (buffer + sizeof(long)); + if ((*position == NULL) || (*position >= bufend)) + { + if (*position == NULL) + (*block) = 0; + else + (*block)++; + FileSeek(file, *block * BLCKSZ, SEEK_SET); + cc = FileRead(file, buffer, BLCKSZ); + NDirectFileRead++; + if (cc < 0) + perror("FileRead"); + if (cc == 0) /* end of file */ + return NULL; + else + (*position) = bufstart; + } + heapTuple = (HeapTuple) (*position); + (*position) = (char *) LONGALIGN(*position + heapTuple->t_len); + + return ExecStoreTuple(heapTuple, tupleSlot, InvalidBuffer, false); } /* ---------------------------------------------------------------- - * ExecHashJoinNewBatch + * ExecHashJoinNewBatch * - * switch to a new hashjoin batch + * switch to a new hashjoin batch * ---------------------------------------------------------------- */ static int -ExecHashJoinNewBatch(HashJoinState *hjstate) +ExecHashJoinNewBatch(HashJoinState * hjstate) { - File *innerBatches; - File *outerBatches; - int *innerBatchSizes; - Var *innerhashkey; - HashJoinTable hashtable; - int nbatch; - char *readPos; - int readBlk; - char *readBuf; - TupleTableSlot *slot; - ExprContext *econtext; - int i; - int cc; - int newbatch; - - hashtable = hjstate->hj_HashTable; - outerBatches = hjstate->hj_OuterBatches; - innerBatches = hjstate->hj_InnerBatches; - nbatch = hashtable->nbatch; - newbatch = hashtable->curbatch + 1; - - /* ------------------ - * this is the last process, so it will do the cleanup and - * batch-switching. - * ------------------ - */ - if (newbatch == 1) { - /* - * if it is end of the first pass, flush all the last pages for - * the batches. - */ - outerBatches = hjstate->hj_OuterBatches; - for (i=0; i<nbatch; i++) { - cc = FileSeek(outerBatches[i], 0L, SEEK_END); - if (cc < 0) - perror("FileSeek"); - cc = FileWrite(outerBatches[i], - ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ); - NDirectFileWrite++; - if (cc < 0) - perror("FileWrite"); - } + File *innerBatches; + File *outerBatches; + int *innerBatchSizes; + Var *innerhashkey; + HashJoinTable hashtable; + int nbatch; + char *readPos; + int readBlk; + char *readBuf; + TupleTableSlot *slot; + ExprContext *econtext; + int i; + int cc; + int newbatch; + + hashtable = hjstate->hj_HashTable; + outerBatches = hjstate->hj_OuterBatches; + innerBatches = hjstate->hj_InnerBatches; + nbatch = hashtable->nbatch; + newbatch = hashtable->curbatch + 1; + + /* ------------------ + * this is the last process, so it will do the cleanup and + * batch-switching. + * ------------------ + */ + if (newbatch == 1) + { + + /* + * if it is end of the first pass, flush all the last pages for + * the batches. + */ + outerBatches = hjstate->hj_OuterBatches; + for (i = 0; i < nbatch; i++) + { + cc = FileSeek(outerBatches[i], 0L, SEEK_END); + if (cc < 0) + perror("FileSeek"); + cc = FileWrite(outerBatches[i], + ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ); + NDirectFileWrite++; + if (cc < 0) + perror("FileWrite"); + } } - if (newbatch > 1) { + if (newbatch > 1) + { + + /* + * remove the previous outer batch + */ + FileUnlink(outerBatches[newbatch - 2]); + } + /* - * remove the previous outer batch + * rebuild the hash table for the new inner batch + */ + innerBatchSizes = (int *) ABSADDR(hashtable->innerbatchSizes); + /* -------------- + * skip over empty inner batches + * -------------- */ - FileUnlink(outerBatches[newbatch - 2]); - } - /* - * rebuild the hash table for the new inner batch - */ - innerBatchSizes = (int*)ABSADDR(hashtable->innerbatchSizes); - /* -------------- - * skip over empty inner batches - * -------------- - */ - while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0) { - FileUnlink(outerBatches[newbatch-1]); - FileUnlink(innerBatches[newbatch-1]); - newbatch++; + while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0) + { + FileUnlink(outerBatches[newbatch - 1]); + FileUnlink(innerBatches[newbatch - 1]); + newbatch++; } - if (newbatch > nbatch) { + if (newbatch > nbatch) + { + hashtable->pcount = hashtable->nprocess; + + return newbatch; + } + ExecHashTableReset(hashtable, innerBatchSizes[newbatch - 1]); + + + econtext = hjstate->jstate.cs_ExprContext; + innerhashkey = hjstate->hj_InnerHashKey; + readPos = NULL; + readBlk = 0; + readBuf = ABSADDR(hashtable->readbuf); + + while ((slot = ExecHashJoinGetSavedTuple(hjstate, + readBuf, + innerBatches[newbatch - 1], + hjstate->hj_HashTupleSlot, + &readBlk, + &readPos)) + && !TupIsNull(slot)) + { + econtext->ecxt_innertuple = slot; + ExecHashTableInsert(hashtable, econtext, innerhashkey, NULL); + /* possible bug - glass */ + } + + + /* ----------------- + * only the last process comes to this branch + * now all the processes have finished the build phase + * ---------------- + */ + + /* + * after we build the hash table, the inner batch is no longer needed + */ + FileUnlink(innerBatches[newbatch - 1]); + hjstate->hj_OuterReadPos = NULL; hashtable->pcount = hashtable->nprocess; + hashtable->curbatch = newbatch; return newbatch; - } - ExecHashTableReset(hashtable, innerBatchSizes[newbatch - 1]); - - - econtext = hjstate->jstate.cs_ExprContext; - innerhashkey = hjstate->hj_InnerHashKey; - readPos = NULL; - readBlk = 0; - readBuf = ABSADDR(hashtable->readbuf); - - while ((slot = ExecHashJoinGetSavedTuple(hjstate, - readBuf, - innerBatches[newbatch-1], - hjstate->hj_HashTupleSlot, - &readBlk, - &readPos)) - && ! TupIsNull(slot)) { - econtext->ecxt_innertuple = slot; - ExecHashTableInsert(hashtable, econtext, innerhashkey,NULL); - /* possible bug - glass */ - } - - - /* ----------------- - * only the last process comes to this branch - * now all the processes have finished the build phase - * ---------------- - */ - - /* - * after we build the hash table, the inner batch is no longer needed - */ - FileUnlink(innerBatches[newbatch - 1]); - hjstate->hj_OuterReadPos = NULL; - hashtable->pcount = hashtable->nprocess; - - hashtable->curbatch = newbatch; - return newbatch; } /* ---------------------------------------------------------------- - * ExecHashJoinGetBatch + * ExecHashJoinGetBatch * - * determine the batch number for a bucketno - * +----------------+-------+-------+ ... +-------+ - * 0 nbuckets totalbuckets - * batch 0 1 2 ... + * determine the batch number for a bucketno + * +----------------+-------+-------+ ... +-------+ + * 0 nbuckets totalbuckets + * batch 0 1 2 ... * ---------------------------------------------------------------- */ static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, int nbatch) { - int b; - if (bucketno < hashtable->nbuckets || nbatch == 0) - return 0; - - b = (float)(bucketno - hashtable->nbuckets) / - (float)(hashtable->totalbuckets - hashtable->nbuckets) * - nbatch; - return b+1; + int b; + + if (bucketno < hashtable->nbuckets || nbatch == 0) + return 0; + + b = (float) (bucketno - hashtable->nbuckets) / + (float) (hashtable->totalbuckets - hashtable->nbuckets) * + nbatch; + return b + 1; } /* ---------------------------------------------------------------- - * ExecHashJoinSaveTuple + * ExecHashJoinSaveTuple * - * save a tuple to a tmp file using a buffer. - * the first few bytes in a page is an offset to the end - * of the page. + * save a tuple to a tmp file using a buffer. + * the first few bytes in a page is an offset to the end + * of the page. * ---------------------------------------------------------------- */ -char * +char * ExecHashJoinSaveTuple(HeapTuple heapTuple, - char *buffer, - File file, - char *position) + char *buffer, + File file, + char *position) { - long *pageend; - char *pagestart; - char *pagebound; - int cc; - - pageend = (long*)buffer; - pagestart = (char*)(buffer + sizeof(long)); - pagebound = buffer + BLCKSZ; - if (position == NULL) - position = pagestart; - - if (position + heapTuple->t_len >= pagebound) { - cc = FileSeek(file, 0L, SEEK_END); - if (cc < 0) - perror("FileSeek"); - cc = FileWrite(file, buffer, BLCKSZ); - NDirectFileWrite++; - if (cc < 0) - perror("FileWrite"); - position = pagestart; - *pageend = 0; - } - memmove(position, heapTuple, heapTuple->t_len); - position = (char*)LONGALIGN(position + heapTuple->t_len); - *pageend = position - buffer; - - return position; + long *pageend; + char *pagestart; + char *pagebound; + int cc; + + pageend = (long *) buffer; + pagestart = (char *) (buffer + sizeof(long)); + pagebound = buffer + BLCKSZ; + if (position == NULL) + position = pagestart; + + if (position + heapTuple->t_len >= pagebound) + { + cc = FileSeek(file, 0L, SEEK_END); + if (cc < 0) + perror("FileSeek"); + cc = FileWrite(file, buffer, BLCKSZ); + NDirectFileWrite++; + if (cc < 0) + perror("FileWrite"); + position = pagestart; + *pageend = 0; + } + memmove(position, heapTuple, heapTuple->t_len); + position = (char *) LONGALIGN(position + heapTuple->t_len); + *pageend = position - buffer; + + return position; } diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index bc0c4c3d288..c89a4fcb081 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -1,31 +1,31 @@ /*------------------------------------------------------------------------- * * nodeIndexscan.c-- - * Routines to support indexes and indexed scans of relations + * Routines to support indexes and indexed scans of relations * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.7 1997/03/12 20:58:26 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.8 1997/09/07 04:41:35 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecInsertIndexTuples inserts tuples into indices on result relation + * ExecInsertIndexTuples inserts tuples into indices on result relation * - * ExecIndexScan scans a relation using indices - * ExecIndexNext using index to retrieve next tuple - * ExecInitIndexScan creates and initializes state info. - * ExecIndexReScan rescans the indexed relation. - * ExecEndIndexScan releases all storage. - * ExecIndexMarkPos marks scan position. - * ExecIndexRestrPos restores scan position. + * ExecIndexScan scans a relation using indices + * ExecIndexNext using index to retrieve next tuple + * ExecInitIndexScan creates and initializes state info. + * ExecIndexReScan rescans the indexed relation. + * ExecEndIndexScan releases all storage. + * ExecIndexMarkPos marks scan position. + * ExecIndexRestrPos restores scan position. * - * NOTES - * the code supporting ExecInsertIndexTuples should be - * collected and merged with the genam stuff. + * NOTES + * the code supporting ExecInsertIndexTuples should be + * collected and merged with the genam stuff. * */ #include "postgres.h" @@ -48,894 +48,939 @@ #include "nodes/nodeFuncs.h" /* ---------------- - * Misc stuff to move to executor.h soon -cim 6/5/90 + * Misc stuff to move to executor.h soon -cim 6/5/90 * ---------------- */ -#define NO_OP 0 -#define LEFT_OP 1 -#define RIGHT_OP 2 +#define NO_OP 0 +#define LEFT_OP 1 +#define RIGHT_OP 2 -static TupleTableSlot *IndexNext(IndexScan *node); +static TupleTableSlot *IndexNext(IndexScan * node); /* ---------------------------------------------------------------- - * IndexNext + * IndexNext * - * Retrieve a tuple from the IndexScan node's currentRelation - * using the indices in the IndexScanState information. + * Retrieve a tuple from the IndexScan node's currentRelation + * using the indices in the IndexScanState information. * - * note: the old code mentions 'Primary indices'. to my knowledge - * we only support a single secondary index. -cim 9/11/89 + * note: the old code mentions 'Primary indices'. to my knowledge + * we only support a single secondary index. -cim 9/11/89 * * old comments: - * retrieve a tuple from relation using the indices given. - * The indices are used in the order they appear in 'indices'. - * The indices may be primary or secondary indices: - * * primary index -- scan the relation 'relID' using keys supplied. - * * secondary index -- scan the index relation to get the 'tid' for - * a tuple in the relation 'relID'. - * If the current index(pointed by 'indexPtr') fails to return a - * tuple, the next index in the indices is used. - * - * bug fix so that it should retrieve on a null scan key. + * retrieve a tuple from relation using the indices given. + * The indices are used in the order they appear in 'indices'. + * The indices may be primary or secondary indices: + * * primary index -- scan the relation 'relID' using keys supplied. + * * secondary index -- scan the index relation to get the 'tid' for + * a tuple in the relation 'relID'. + * If the current index(pointed by 'indexPtr') fails to return a + * tuple, the next index in the indices is used. + * + * bug fix so that it should retrieve on a null scan key. * ---------------------------------------------------------------- */ static TupleTableSlot * -IndexNext(IndexScan *node) +IndexNext(IndexScan * node) { - EState *estate; - CommonScanState *scanstate; - IndexScanState *indexstate; - ScanDirection direction; - int indexPtr; - IndexScanDescPtr scanDescs; - IndexScanDesc scandesc; - Relation heapRelation; - RetrieveIndexResult result; - ItemPointer iptr; - HeapTuple tuple; - TupleTableSlot *slot; - Buffer buffer = InvalidBuffer; - - /* ---------------- - * extract necessary information from index scan node - * ---------------- - */ - estate = node->scan.plan.state; - direction = estate->es_direction; - scanstate = node->scan.scanstate; - indexstate = node->indxstate; - indexPtr = indexstate->iss_IndexPtr; - scanDescs = indexstate->iss_ScanDescs; - scandesc = scanDescs[ indexPtr ]; - heapRelation = scanstate->css_currentRelation; - - slot = scanstate->css_ScanTupleSlot; - - /* ---------------- - * ok, now that we have what we need, fetch an index tuple. - * ---------------- - */ - - for(;;) { - result = index_getnext(scandesc, direction); + EState *estate; + CommonScanState *scanstate; + IndexScanState *indexstate; + ScanDirection direction; + int indexPtr; + IndexScanDescPtr scanDescs; + IndexScanDesc scandesc; + Relation heapRelation; + RetrieveIndexResult result; + ItemPointer iptr; + HeapTuple tuple; + TupleTableSlot *slot; + Buffer buffer = InvalidBuffer; + /* ---------------- - * if scanning this index succeeded then return the - * appropriate heap tuple.. else return NULL. + * extract necessary information from index scan node * ---------------- */ - if (result) { - iptr = &result->heap_iptr; - tuple = heap_fetch(heapRelation, - NowTimeQual, - iptr, - &buffer); - /* be tidy */ - pfree(result); - - if (tuple == NULL) { + estate = node->scan.plan.state; + direction = estate->es_direction; + scanstate = node->scan.scanstate; + indexstate = node->indxstate; + indexPtr = indexstate->iss_IndexPtr; + scanDescs = indexstate->iss_ScanDescs; + scandesc = scanDescs[indexPtr]; + heapRelation = scanstate->css_currentRelation; + + slot = scanstate->css_ScanTupleSlot; + + /* ---------------- + * ok, now that we have what we need, fetch an index tuple. + * ---------------- + */ + + for (;;) + { + result = index_getnext(scandesc, direction); + /* ---------------- + * if scanning this index succeeded then return the + * appropriate heap tuple.. else return NULL. + * ---------------- + */ + if (result) + { + iptr = &result->heap_iptr; + tuple = heap_fetch(heapRelation, + NowTimeQual, + iptr, + &buffer); + /* be tidy */ + pfree(result); + + if (tuple == NULL) + { + /* ---------------- + * we found a deleted tuple, so keep on scanning.. + * ---------------- + */ + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + continue; + } + + /* ---------------- + * store the scanned tuple in the scan tuple slot of + * the scan state. Eventually we will only do this and not + * return a tuple. Note: we pass 'false' because tuples + * returned by amgetnext are pointers onto disk pages and + * were not created with palloc() and so should not be pfree()'d. + * ---------------- + */ + ExecStoreTuple(tuple, /* tuple to store */ + slot,/* slot to store in */ + buffer, /* buffer associated with tuple */ + false); /* don't pfree */ + + return slot; + } + /* ---------------- - * we found a deleted tuple, so keep on scanning.. + * if we get here it means the index scan failed so we + * are at the end of the scan.. * ---------------- */ - if (BufferIsValid(buffer)) - ReleaseBuffer(buffer); - continue; - } - - /* ---------------- - * store the scanned tuple in the scan tuple slot of - * the scan state. Eventually we will only do this and not - * return a tuple. Note: we pass 'false' because tuples - * returned by amgetnext are pointers onto disk pages and - * were not created with palloc() and so should not be pfree()'d. - * ---------------- - */ - ExecStoreTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - buffer, /* buffer associated with tuple */ - false); /* don't pfree */ - - return slot; + return ExecClearTuple(slot); } - - /* ---------------- - * if we get here it means the index scan failed so we - * are at the end of the scan.. - * ---------------- - */ - return ExecClearTuple(slot); - } } /* ---------------------------------------------------------------- - * ExecIndexScan(node) + * ExecIndexScan(node) * * old comments: - * Scans the relation using primary or secondary indices and returns - * the next qualifying tuple in the direction specified. - * It calls ExecScan() and passes it the access methods which returns - * the next tuple using the indices. - * - * Conditions: - * -- the "cursor" maintained by the AMI is positioned at the tuple - * returned previously. - * - * Initial States: - * -- the relation indicated is opened for scanning so that the - * "cursor" is positioned before the first qualifying tuple. - * -- all index realtions are opened for scanning. - * -- indexPtr points to the first index. - * -- state variable ruleFlag = nil. + * Scans the relation using primary or secondary indices and returns + * the next qualifying tuple in the direction specified. + * It calls ExecScan() and passes it the access methods which returns + * the next tuple using the indices. + * + * Conditions: + * -- the "cursor" maintained by the AMI is positioned at the tuple + * returned previously. + * + * Initial States: + * -- the relation indicated is opened for scanning so that the + * "cursor" is positioned before the first qualifying tuple. + * -- all index realtions are opened for scanning. + * -- indexPtr points to the first index. + * -- state variable ruleFlag = nil. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecIndexScan(IndexScan *node) +ExecIndexScan(IndexScan * node) { - TupleTableSlot *returnTuple; + TupleTableSlot *returnTuple; - /* ---------------- - * use IndexNext as access method - * ---------------- - */ - returnTuple = ExecScan(&node->scan, IndexNext); - return returnTuple; -} + /* ---------------- + * use IndexNext as access method + * ---------------- + */ + returnTuple = ExecScan(&node->scan, IndexNext); + return returnTuple; +} /* ---------------------------------------------------------------- - * ExecIndexReScan(node) + * ExecIndexReScan(node) * - * Recalculates the value of the scan keys whose value depends on - * information known at runtime and rescans the indexed relation. - * Updating the scan key was formerly done separately in - * ExecUpdateIndexScanKeys. Integrating it into ReScan - * makes rescans of indices and - * relations/general streams more uniform. + * Recalculates the value of the scan keys whose value depends on + * information known at runtime and rescans the indexed relation. + * Updating the scan key was formerly done separately in + * ExecUpdateIndexScanKeys. Integrating it into ReScan + * makes rescans of indices and + * relations/general streams more uniform. * * ---------------------------------------------------------------- */ void -ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan* parent) +ExecIndexReScan(IndexScan * node, ExprContext * exprCtxt, Plan * parent) { - EState *estate; - IndexScanState *indexstate; - ScanDirection direction; - IndexScanDescPtr scanDescs; - ScanKey *scanKeys; - IndexScanDesc sdesc; - ScanKey skey; - int numIndices; - int i; - - Pointer *runtimeKeyInfo; - int indexPtr; - int *numScanKeys; - List *indxqual; - List *qual; - int n_keys; - ScanKey scan_keys; - int *run_keys; - int j; - Expr *clause; - Node *scanexpr; - Datum scanvalue; - bool isNull; - bool isDone; - - indexstate = node->indxstate; - estate = node->scan.plan.state; - direction = estate->es_direction; - indexstate = node->indxstate; - numIndices = indexstate->iss_NumIndices; - scanDescs = indexstate->iss_ScanDescs; - scanKeys = indexstate->iss_ScanKeys; - - runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo; - - if (runtimeKeyInfo != NULL) { - /* - * get the index qualifications and - * recalculate the appropriate values - */ - indexPtr = indexstate->iss_IndexPtr; - indxqual = node->indxqual; - qual = nth(indexPtr, indxqual); - numScanKeys = indexstate->iss_NumScanKeys; - n_keys = numScanKeys[indexPtr]; - run_keys = (int *) runtimeKeyInfo[indexPtr]; - scan_keys = (ScanKey) scanKeys[indexPtr]; - - for (j=0; j < n_keys; j++) { - /* - * If we have a run-time key, then extract the run-time - * expression and evaluate it with respect to the current - * outer tuple. We then stick the result into the scan - * key. - */ - if (run_keys[j] != NO_OP) { - clause = nth(j, qual); - scanexpr = (run_keys[j] == RIGHT_OP) ? - (Node*) get_rightop(clause) : (Node*) get_leftop(clause) ; - /* pass in isDone but ignore it. We don't iterate in quals */ - scanvalue = (Datum) - ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone); - scan_keys[j].sk_argument = scanvalue; - if (isNull) { - scan_keys[j].sk_flags |= SK_ISNULL; - } else { - scan_keys[j].sk_flags &= ~SK_ISNULL; + EState *estate; + IndexScanState *indexstate; + ScanDirection direction; + IndexScanDescPtr scanDescs; + ScanKey *scanKeys; + IndexScanDesc sdesc; + ScanKey skey; + int numIndices; + int i; + + Pointer *runtimeKeyInfo; + int indexPtr; + int *numScanKeys; + List *indxqual; + List *qual; + int n_keys; + ScanKey scan_keys; + int *run_keys; + int j; + Expr *clause; + Node *scanexpr; + Datum scanvalue; + bool isNull; + bool isDone; + + indexstate = node->indxstate; + estate = node->scan.plan.state; + direction = estate->es_direction; + indexstate = node->indxstate; + numIndices = indexstate->iss_NumIndices; + scanDescs = indexstate->iss_ScanDescs; + scanKeys = indexstate->iss_ScanKeys; + + runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo; + + if (runtimeKeyInfo != NULL) + { + + /* + * get the index qualifications and recalculate the appropriate + * values + */ + indexPtr = indexstate->iss_IndexPtr; + indxqual = node->indxqual; + qual = nth(indexPtr, indxqual); + numScanKeys = indexstate->iss_NumScanKeys; + n_keys = numScanKeys[indexPtr]; + run_keys = (int *) runtimeKeyInfo[indexPtr]; + scan_keys = (ScanKey) scanKeys[indexPtr]; + + for (j = 0; j < n_keys; j++) + { + + /* + * If we have a run-time key, then extract the run-time + * expression and evaluate it with respect to the current + * outer tuple. We then stick the result into the scan key. + */ + if (run_keys[j] != NO_OP) + { + clause = nth(j, qual); + scanexpr = (run_keys[j] == RIGHT_OP) ? + (Node *) get_rightop(clause) : (Node *) get_leftop(clause); + + /* + * pass in isDone but ignore it. We don't iterate in + * quals + */ + scanvalue = (Datum) + ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone); + scan_keys[j].sk_argument = scanvalue; + if (isNull) + { + scan_keys[j].sk_flags |= SK_ISNULL; + } + else + { + scan_keys[j].sk_flags &= ~SK_ISNULL; + } + } } - } } - } - - /* - * rescans all indices - * - * note: AMrescan assumes only one scan key. This may have - * to change if we ever decide to support multiple keys. - */ - for (i = 0; i < numIndices; i++) { - sdesc = scanDescs[ i ]; - skey = scanKeys[ i ]; - index_rescan(sdesc, direction, skey); - } - - /* ---------------- - * perhaps return something meaningful - * ---------------- - */ - return; + + /* + * rescans all indices + * + * note: AMrescan assumes only one scan key. This may have to change if + * we ever decide to support multiple keys. + */ + for (i = 0; i < numIndices; i++) + { + sdesc = scanDescs[i]; + skey = scanKeys[i]; + index_rescan(sdesc, direction, skey); + } + + /* ---------------- + * perhaps return something meaningful + * ---------------- + */ + return; } /* ---------------------------------------------------------------- - * ExecEndIndexScan + * ExecEndIndexScan * * old comments - * Releases any storage allocated through C routines. - * Returns nothing. + * Releases any storage allocated through C routines. + * Returns nothing. * ---------------------------------------------------------------- */ void -ExecEndIndexScan(IndexScan *node) +ExecEndIndexScan(IndexScan * node) { - CommonScanState *scanstate; - IndexScanState *indexstate; - ScanKey *scanKeys; - int numIndices; - int i; - - scanstate = node->scan.scanstate; - indexstate = node->indxstate; - - /* ---------------- - * extract information from the node - * ---------------- - */ - numIndices = indexstate->iss_NumIndices; - scanKeys = indexstate->iss_ScanKeys; - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(scanstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&scanstate->cstate); - - /* ---------------- - * close the heap and index relations - * ---------------- - */ - ExecCloseR((Plan *) node); - - /* ---------------- - * free the scan keys used in scanning the indices - * ---------------- - */ - for (i=0; i<numIndices; i++) { - if (scanKeys[i]!=NULL) - pfree(scanKeys[i]); - - } - - /* ---------------- - * clear out tuple table slots - * ---------------- - */ - ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); - ExecClearTuple(scanstate->css_ScanTupleSlot); -/* ExecClearTuple(scanstate->css_RawTupleSlot); */ + CommonScanState *scanstate; + IndexScanState *indexstate; + ScanKey *scanKeys; + int numIndices; + int i; + + scanstate = node->scan.scanstate; + indexstate = node->indxstate; + + /* ---------------- + * extract information from the node + * ---------------- + */ + numIndices = indexstate->iss_NumIndices; + scanKeys = indexstate->iss_ScanKeys; + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(scanstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&scanstate->cstate); + + /* ---------------- + * close the heap and index relations + * ---------------- + */ + ExecCloseR((Plan *) node); + + /* ---------------- + * free the scan keys used in scanning the indices + * ---------------- + */ + for (i = 0; i < numIndices; i++) + { + if (scanKeys[i] != NULL) + pfree(scanKeys[i]); + + } + + /* ---------------- + * clear out tuple table slots + * ---------------- + */ + ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); + ExecClearTuple(scanstate->css_ScanTupleSlot); +/* ExecClearTuple(scanstate->css_RawTupleSlot); */ } /* ---------------------------------------------------------------- - * ExecIndexMarkPos + * ExecIndexMarkPos * * old comments - * Marks scan position by marking the current index. - * Returns nothing. + * Marks scan position by marking the current index. + * Returns nothing. * ---------------------------------------------------------------- */ void -ExecIndexMarkPos(IndexScan *node) +ExecIndexMarkPos(IndexScan * node) { - IndexScanState *indexstate; - IndexScanDescPtr indexScanDescs; - IndexScanDesc scanDesc; - int indexPtr; - - indexstate = node->indxstate; - indexPtr = indexstate->iss_IndexPtr; - indexScanDescs = indexstate->iss_ScanDescs; - scanDesc = indexScanDescs[ indexPtr ]; - - /* ---------------- - * XXX access methods don't return marked positions so - * ---------------- - */ - IndexScanMarkPosition( scanDesc ); - return; + IndexScanState *indexstate; + IndexScanDescPtr indexScanDescs; + IndexScanDesc scanDesc; + int indexPtr; + + indexstate = node->indxstate; + indexPtr = indexstate->iss_IndexPtr; + indexScanDescs = indexstate->iss_ScanDescs; + scanDesc = indexScanDescs[indexPtr]; + + /* ---------------- + * XXX access methods don't return marked positions so + * ---------------- + */ + IndexScanMarkPosition(scanDesc); + return; } /* ---------------------------------------------------------------- - * ExecIndexRestrPos + * ExecIndexRestrPos * * old comments - * Restores scan position by restoring the current index. - * Returns nothing. - * - * XXX Assumes previously marked scan position belongs to current index + * Restores scan position by restoring the current index. + * Returns nothing. + * + * XXX Assumes previously marked scan position belongs to current index * ---------------------------------------------------------------- */ void -ExecIndexRestrPos(IndexScan *node) +ExecIndexRestrPos(IndexScan * node) { - IndexScanState *indexstate; - IndexScanDescPtr indexScanDescs; - IndexScanDesc scanDesc; - int indexPtr; + IndexScanState *indexstate; + IndexScanDescPtr indexScanDescs; + IndexScanDesc scanDesc; + int indexPtr; - indexstate = node->indxstate; - indexPtr = indexstate->iss_IndexPtr; - indexScanDescs = indexstate->iss_ScanDescs; - scanDesc = indexScanDescs[ indexPtr ]; + indexstate = node->indxstate; + indexPtr = indexstate->iss_IndexPtr; + indexScanDescs = indexstate->iss_ScanDescs; + scanDesc = indexScanDescs[indexPtr]; - IndexScanRestorePosition( scanDesc ); + IndexScanRestorePosition(scanDesc); } /* ---------------------------------------------------------------- - * ExecInitIndexScan + * ExecInitIndexScan + * + * Initializes the index scan's state information, creates + * scan keys, and opens the base and index relations. * - * Initializes the index scan's state information, creates - * scan keys, and opens the base and index relations. + * Note: index scans have 2 sets of state information because + * we have to keep track of the base relation and the + * index relations. * - * Note: index scans have 2 sets of state information because - * we have to keep track of the base relation and the - * index relations. - * * old comments - * Creates the run-time state information for the node and - * sets the relation id to contain relevant decriptors. - * - * Parameters: - * node: IndexNode node produced by the planner. - * estate: the execution state initialized in InitPlan. + * Creates the run-time state information for the node and + * sets the relation id to contain relevant decriptors. + * + * Parameters: + * node: IndexNode node produced by the planner. + * estate: the execution state initialized in InitPlan. * ---------------------------------------------------------------- */ bool -ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) +ExecInitIndexScan(IndexScan * node, EState * estate, Plan * parent) { - IndexScanState *indexstate; - CommonScanState *scanstate; - List *indxqual; - List *indxid; - int i; - int numIndices; - int indexPtr; - ScanKey *scanKeys; - int *numScanKeys; - RelationPtr relationDescs; - IndexScanDescPtr scanDescs; - Pointer *runtimeKeyInfo; - bool have_runtime_keys; - List *rangeTable; - RangeTblEntry *rtentry; - Index relid; - Oid reloid; - TimeQual timeQual; - - Relation currentRelation; - HeapScanDesc currentScanDesc; - ScanDirection direction; - int baseid; - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->scan.plan.state = estate; - - /* -------------------------------- - * Part 1) initialize scan state - * - * create new CommonScanState for node - * -------------------------------- - */ - scanstate = makeNode(CommonScanState); + IndexScanState *indexstate; + CommonScanState *scanstate; + List *indxqual; + List *indxid; + int i; + int numIndices; + int indexPtr; + ScanKey *scanKeys; + int *numScanKeys; + RelationPtr relationDescs; + IndexScanDescPtr scanDescs; + Pointer *runtimeKeyInfo; + bool have_runtime_keys; + List *rangeTable; + RangeTblEntry *rtentry; + Index relid; + Oid reloid; + TimeQual timeQual; + + Relation currentRelation; + HeapScanDesc currentScanDesc; + ScanDirection direction; + int baseid; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->scan.plan.state = estate; + + /* -------------------------------- + * Part 1) initialize scan state + * + * create new CommonScanState for node + * -------------------------------- + */ + scanstate = makeNode(CommonScanState); /* - scanstate->ss_ProcOuterFlag = false; - scanstate->ss_OldRelId = 0; + scanstate->ss_ProcOuterFlag = false; + scanstate->ss_OldRelId = 0; */ - node->scan.scanstate = scanstate; + node->scan.scanstate = scanstate; - /* ---------------- - * assign node's base_id .. we don't use AssignNodeBaseid() because - * the increment is done later on after we assign the index scan's - * scanstate. see below. - * ---------------- - */ - baseid = estate->es_BaseId; -/* scanstate->csstate.cstate.bnode.base_id = baseid; */ - scanstate->cstate.cs_base_id = baseid; + /* ---------------- + * assign node's base_id .. we don't use AssignNodeBaseid() because + * the increment is done later on after we assign the index scan's + * scanstate. see below. + * ---------------- + */ + baseid = estate->es_BaseId; +/* scanstate->csstate.cstate.bnode.base_id = baseid; */ + scanstate->cstate.cs_base_id = baseid; - /* ---------------- - * create expression context for node - * ---------------- - */ - ExecAssignExprContext(estate, &scanstate->cstate); + /* ---------------- + * create expression context for node + * ---------------- + */ + ExecAssignExprContext(estate, &scanstate->cstate); #define INDEXSCAN_NSLOTS 3 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &scanstate->cstate); - ExecInitScanTupleSlot(estate, scanstate); -/* ExecInitRawTupleSlot(estate, scanstate); */ - - /* ---------------- - * initialize projection info. result type comes from scan desc - * below.. - * ---------------- - */ - ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); - - /* -------------------------------- - * Part 2) initialize index scan state - * - * create new IndexScanState for node - * -------------------------------- - */ - indexstate = makeNode(IndexScanState); - indexstate->iss_NumIndices = 0; - indexstate->iss_IndexPtr = 0; - indexstate->iss_ScanKeys = NULL; - indexstate->iss_NumScanKeys = NULL; - indexstate->iss_RuntimeKeyInfo = NULL; - indexstate->iss_RelationDescs = NULL; - indexstate->iss_ScanDescs = NULL; - - node->indxstate = indexstate; - - /* ---------------- - * assign base id to index scan state also - * ---------------- - */ - indexstate->cstate.cs_base_id = baseid; - baseid++; - estate->es_BaseId = baseid; - - /* ---------------- - * get the index node information - * ---------------- - */ - indxid = node->indxid; - indxqual = node->indxqual; - numIndices = length(indxid); - indexPtr = 0; - - CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext); - - /* ---------------- - * scanKeys is used to keep track of the ScanKey's. This is needed - * because a single scan may use several indices and each index has - * its own ScanKey. - * ---------------- - */ - numScanKeys = (int *) palloc(numIndices * sizeof(int)); - scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey)); - relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation)); - scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc)); - - /* ---------------- - * initialize runtime key info. - * ---------------- - */ - have_runtime_keys = false; - runtimeKeyInfo = (Pointer *) - palloc(numIndices * sizeof(Pointer)); - - /* ---------------- - * build the index scan keys from the index qualification - * ---------------- - */ - for (i=0; i < numIndices; i++) { - int j; - List *qual; - int n_keys; - ScanKey scan_keys; - int *run_keys; - - qual = nth(i, indxqual); - n_keys = length(qual); - scan_keys = (n_keys <= 0) ? NULL : - (ScanKey)palloc(n_keys * sizeof(ScanKeyData)); - run_keys = (n_keys <= 0) ? NULL : - (int *)palloc(n_keys * sizeof(int)); - - CXT1_printf("ExecInitIndexScan: context is %d\n", - CurrentMemoryContext); - /* ---------------- - * for each opclause in the given qual, - * convert each qual's opclause into a single scan key + * tuple table initialization * ---------------- */ - for (j=0; j < n_keys; j++) { - Expr *clause; /* one part of index qual */ - Oper *op; /* operator used in scan.. */ - Node *leftop; /* expr on lhs of operator */ - Node *rightop; /* expr on rhs ... */ - bits16 flags = 0; - - int scanvar; /* which var identifies varattno */ - AttrNumber varattno = 0; /* att number used in scan */ - Oid opid; /* operator id used in scan */ - Datum scanvalue = 0; /* value used in scan (if const) */ - - /* ---------------- - * extract clause information from the qualification - * ---------------- - */ - clause = nth(j, qual); - - op = (Oper*)clause->oper; - if (!IsA(op,Oper)) - elog(WARN, "ExecInitIndexScan: op not an Oper!"); - - opid = op->opid; - - /* ---------------- - * Here we figure out the contents of the index qual. - * The usual case is (op var const) or (op const var) - * which means we form a scan key for the attribute - * listed in the var node and use the value of the const. - * - * If we don't have a const node, then it means that - * one of the var nodes refers to the "scan" tuple and - * is used to determine which attribute to scan, and the - * other expression is used to calculate the value used in - * scanning the index. - * - * This means our index scan's scan key is a function of - * information obtained during the execution of the plan - * in which case we need to recalculate the index scan key - * at run time. - * - * Hence, we set have_runtime_keys to true and then set - * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP. - * The corresponding scan keys are recomputed at run time. - * ---------------- - */ - - scanvar = NO_OP; - - /* ---------------- - * determine information in leftop - * ---------------- - */ - leftop = (Node*) get_leftop(clause); - - if (IsA(leftop,Var) && var_is_rel((Var*)leftop)) { - /* ---------------- - * if the leftop is a "rel-var", then it means - * that it is a var node which tells us which - * attribute to use for our scan key. - * ---------------- - */ - varattno = ((Var*) leftop)->varattno; - scanvar = LEFT_OP; - } else if (IsA(leftop,Const)) { + ExecInitResultTupleSlot(estate, &scanstate->cstate); + ExecInitScanTupleSlot(estate, scanstate); +/* ExecInitRawTupleSlot(estate, scanstate); */ + + /* ---------------- + * initialize projection info. result type comes from scan desc + * below.. + * ---------------- + */ + ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); + + /* -------------------------------- + * Part 2) initialize index scan state + * + * create new IndexScanState for node + * -------------------------------- + */ + indexstate = makeNode(IndexScanState); + indexstate->iss_NumIndices = 0; + indexstate->iss_IndexPtr = 0; + indexstate->iss_ScanKeys = NULL; + indexstate->iss_NumScanKeys = NULL; + indexstate->iss_RuntimeKeyInfo = NULL; + indexstate->iss_RelationDescs = NULL; + indexstate->iss_ScanDescs = NULL; + + node->indxstate = indexstate; + + /* ---------------- + * assign base id to index scan state also + * ---------------- + */ + indexstate->cstate.cs_base_id = baseid; + baseid++; + estate->es_BaseId = baseid; + + /* ---------------- + * get the index node information + * ---------------- + */ + indxid = node->indxid; + indxqual = node->indxqual; + numIndices = length(indxid); + indexPtr = 0; + + CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext); + + /* ---------------- + * scanKeys is used to keep track of the ScanKey's. This is needed + * because a single scan may use several indices and each index has + * its own ScanKey. + * ---------------- + */ + numScanKeys = (int *) palloc(numIndices * sizeof(int)); + scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey)); + relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation)); + scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc)); + + /* ---------------- + * initialize runtime key info. + * ---------------- + */ + have_runtime_keys = false; + runtimeKeyInfo = (Pointer *) + palloc(numIndices * sizeof(Pointer)); + + /* ---------------- + * build the index scan keys from the index qualification + * ---------------- + */ + for (i = 0; i < numIndices; i++) + { + int j; + List *qual; + int n_keys; + ScanKey scan_keys; + int *run_keys; + + qual = nth(i, indxqual); + n_keys = length(qual); + scan_keys = (n_keys <= 0) ? NULL : + (ScanKey) palloc(n_keys * sizeof(ScanKeyData)); + run_keys = (n_keys <= 0) ? NULL : + (int *) palloc(n_keys * sizeof(int)); + + CXT1_printf("ExecInitIndexScan: context is %d\n", + CurrentMemoryContext); + /* ---------------- - * if the leftop is a const node then it means - * it identifies the value to place in our scan key. + * for each opclause in the given qual, + * convert each qual's opclause into a single scan key * ---------------- */ - run_keys[ j ] = NO_OP; - scanvalue = ((Const*) leftop)->constvalue; + for (j = 0; j < n_keys; j++) + { + Expr *clause; /* one part of index qual */ + Oper *op; /* operator used in scan.. */ + Node *leftop; /* expr on lhs of operator */ + Node *rightop; /* expr on rhs ... */ + bits16 flags = 0; + + int scanvar; /* which var identifies varattno */ + AttrNumber varattno = 0; /* att number used in scan */ + Oid opid; /* operator id used in scan */ + Datum scanvalue = 0; /* value used in scan (if + * const) */ + + /* ---------------- + * extract clause information from the qualification + * ---------------- + */ + clause = nth(j, qual); + + op = (Oper *) clause->oper; + if (!IsA(op, Oper)) + elog(WARN, "ExecInitIndexScan: op not an Oper!"); + + opid = op->opid; + + /* ---------------- + * Here we figure out the contents of the index qual. + * The usual case is (op var const) or (op const var) + * which means we form a scan key for the attribute + * listed in the var node and use the value of the const. + * + * If we don't have a const node, then it means that + * one of the var nodes refers to the "scan" tuple and + * is used to determine which attribute to scan, and the + * other expression is used to calculate the value used in + * scanning the index. + * + * This means our index scan's scan key is a function of + * information obtained during the execution of the plan + * in which case we need to recalculate the index scan key + * at run time. + * + * Hence, we set have_runtime_keys to true and then set + * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP. + * The corresponding scan keys are recomputed at run time. + * ---------------- + */ + + scanvar = NO_OP; + + /* ---------------- + * determine information in leftop + * ---------------- + */ + leftop = (Node *) get_leftop(clause); + + if (IsA(leftop, Var) && var_is_rel((Var *) leftop)) + { + /* ---------------- + * if the leftop is a "rel-var", then it means + * that it is a var node which tells us which + * attribute to use for our scan key. + * ---------------- + */ + varattno = ((Var *) leftop)->varattno; + scanvar = LEFT_OP; + } + else if (IsA(leftop, Const)) + { + /* ---------------- + * if the leftop is a const node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ((Const *) leftop)->constvalue; #ifdef INDEXSCAN_PATCH - } else if (IsA(leftop,Param)) { - bool isnull; - /* ---------------- - * if the leftop is a Param node then it means - * it identifies the value to place in our scan key. - * ---------------- - */ - run_keys[ j ] = NO_OP; - scanvalue = ExecEvalParam((Param*) leftop, - scanstate->cstate.cs_ExprContext, - &isnull); - if ( isnull ) - flags |= SK_ISNULL; + } + else if (IsA(leftop, Param)) + { + bool isnull; + + /* ---------------- + * if the leftop is a Param node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ExecEvalParam((Param *) leftop, + scanstate->cstate.cs_ExprContext, + &isnull); + if (isnull) + flags |= SK_ISNULL; #endif - } else if (leftop != NULL && - is_funcclause(leftop) && - var_is_rel(lfirst(((Expr*)leftop)->args))) { - /* ---------------- - * if the leftop is a func node then it means - * it identifies the value to place in our scan key. - * Since functional indices have only one attribute - * the attno must always be set to 1. - * ---------------- - */ - varattno = 1; - scanvar = LEFT_OP; - - } else { - /* ---------------- - * otherwise, the leftop contains information usable - * at runtime to figure out the value to place in our - * scan key. - * ---------------- - */ - have_runtime_keys = true; - run_keys[ j ] = LEFT_OP; - scanvalue = Int32GetDatum((int32) true); - } - - /* ---------------- - * now determine information in rightop - * ---------------- - */ - rightop = (Node*) get_rightop(clause); - - if (IsA(rightop,Var) && var_is_rel((Var*)rightop)) { - /* ---------------- - * here we make sure only one op identifies the - * scan-attribute... - * ---------------- - */ - if (scanvar == LEFT_OP) - elog(WARN, "ExecInitIndexScan: %s", - "both left and right op's are rel-vars"); - - /* ---------------- - * if the rightop is a "rel-var", then it means - * that it is a var node which tells us which - * attribute to use for our scan key. - * ---------------- - */ - varattno = ((Var*) rightop)->varattno; - scanvar = RIGHT_OP; - - } else if (IsA(rightop,Const)) { - /* ---------------- - * if the leftop is a const node then it means - * it identifies the value to place in our scan key. - * ---------------- - */ - run_keys[ j ] = NO_OP; - scanvalue = ((Const*) rightop)->constvalue; + } + else if (leftop != NULL && + is_funcclause(leftop) && + var_is_rel(lfirst(((Expr *) leftop)->args))) + { + /* ---------------- + * if the leftop is a func node then it means + * it identifies the value to place in our scan key. + * Since functional indices have only one attribute + * the attno must always be set to 1. + * ---------------- + */ + varattno = 1; + scanvar = LEFT_OP; + + } + else + { + /* ---------------- + * otherwise, the leftop contains information usable + * at runtime to figure out the value to place in our + * scan key. + * ---------------- + */ + have_runtime_keys = true; + run_keys[j] = LEFT_OP; + scanvalue = Int32GetDatum((int32) true); + } + + /* ---------------- + * now determine information in rightop + * ---------------- + */ + rightop = (Node *) get_rightop(clause); + + if (IsA(rightop, Var) && var_is_rel((Var *) rightop)) + { + /* ---------------- + * here we make sure only one op identifies the + * scan-attribute... + * ---------------- + */ + if (scanvar == LEFT_OP) + elog(WARN, "ExecInitIndexScan: %s", + "both left and right op's are rel-vars"); + + /* ---------------- + * if the rightop is a "rel-var", then it means + * that it is a var node which tells us which + * attribute to use for our scan key. + * ---------------- + */ + varattno = ((Var *) rightop)->varattno; + scanvar = RIGHT_OP; + + } + else if (IsA(rightop, Const)) + { + /* ---------------- + * if the leftop is a const node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ((Const *) rightop)->constvalue; #ifdef INDEXSCAN_PATCH - } else if (IsA(rightop,Param)) { - bool isnull; - /* ---------------- - * if the rightop is a Param node then it means - * it identifies the value to place in our scan key. - * ---------------- - */ - run_keys[ j ] = NO_OP; - scanvalue = ExecEvalParam((Param*) rightop, - scanstate->cstate.cs_ExprContext, - &isnull); - if ( isnull ) - flags |= SK_ISNULL; + } + else if (IsA(rightop, Param)) + { + bool isnull; + + /* ---------------- + * if the rightop is a Param node then it means + * it identifies the value to place in our scan key. + * ---------------- + */ + run_keys[j] = NO_OP; + scanvalue = ExecEvalParam((Param *) rightop, + scanstate->cstate.cs_ExprContext, + &isnull); + if (isnull) + flags |= SK_ISNULL; #endif - } else if (rightop!=NULL && - is_funcclause(rightop) && - var_is_rel(lfirst(((Expr*)rightop)->args))) { - /* ---------------- - * if the rightop is a func node then it means - * it identifies the value to place in our scan key. - * Since functional indices have only one attribute - * the attno must always be set to 1. - * ---------------- - */ - if (scanvar == LEFT_OP) - elog(WARN, "ExecInitIndexScan: %s", - "both left and right ops are rel-vars"); - - varattno = 1; - scanvar = RIGHT_OP; - - } else { + } + else if (rightop != NULL && + is_funcclause(rightop) && + var_is_rel(lfirst(((Expr *) rightop)->args))) + { + /* ---------------- + * if the rightop is a func node then it means + * it identifies the value to place in our scan key. + * Since functional indices have only one attribute + * the attno must always be set to 1. + * ---------------- + */ + if (scanvar == LEFT_OP) + elog(WARN, "ExecInitIndexScan: %s", + "both left and right ops are rel-vars"); + + varattno = 1; + scanvar = RIGHT_OP; + + } + else + { + /* ---------------- + * otherwise, the leftop contains information usable + * at runtime to figure out the value to place in our + * scan key. + * ---------------- + */ + have_runtime_keys = true; + run_keys[j] = RIGHT_OP; + scanvalue = Int32GetDatum((int32) true); + } + + /* ---------------- + * now check that at least one op tells us the scan + * attribute... + * ---------------- + */ + if (scanvar == NO_OP) + elog(WARN, "ExecInitIndexScan: %s", + "neither leftop nor rightop refer to scan relation"); + + /* ---------------- + * initialize the scan key's fields appropriately + * ---------------- + */ + ScanKeyEntryInitialize(&scan_keys[j], + flags, + varattno, /* attribute number to + * scan */ + (RegProcedure) opid, /* reg proc to use */ + (Datum) scanvalue); /* constant */ + } + /* ---------------- - * otherwise, the leftop contains information usable - * at runtime to figure out the value to place in our - * scan key. + * store the key information into our array. * ---------------- */ - have_runtime_keys = true; - run_keys[ j ] = RIGHT_OP; - scanvalue = Int32GetDatum((int32) true); - } - - /* ---------------- - * now check that at least one op tells us the scan - * attribute... - * ---------------- - */ - if (scanvar == NO_OP) - elog(WARN, "ExecInitIndexScan: %s", - "neither leftop nor rightop refer to scan relation"); - - /* ---------------- - * initialize the scan key's fields appropriately - * ---------------- - */ - ScanKeyEntryInitialize(&scan_keys[j], - flags, - varattno, /* attribute number to scan */ - (RegProcedure) opid, /* reg proc to use */ - (Datum) scanvalue); /* constant */ + numScanKeys[i] = n_keys; + scanKeys[i] = scan_keys; + runtimeKeyInfo[i] = (Pointer) run_keys; } - + + indexstate->iss_NumIndices = numIndices; + indexstate->iss_IndexPtr = indexPtr; + indexstate->iss_ScanKeys = scanKeys; + indexstate->iss_NumScanKeys = numScanKeys; + /* ---------------- - * store the key information into our array. + * If all of our keys have the form (op var const) , then we have no + * runtime keys so we store NULL in the runtime key info. + * Otherwise runtime key info contains an array of pointers + * (one for each index) to arrays of flags (one for each key) + * which indicate that the qual needs to be evaluated at runtime. + * -cim 10/24/89 * ---------------- */ - numScanKeys[ i ] = n_keys; - scanKeys[ i ] = scan_keys; - runtimeKeyInfo[ i ] = (Pointer) run_keys; - } - - indexstate->iss_NumIndices = numIndices; - indexstate->iss_IndexPtr = indexPtr; - indexstate->iss_ScanKeys = scanKeys; - indexstate->iss_NumScanKeys = numScanKeys; - - /* ---------------- - * If all of our keys have the form (op var const) , then we have no - * runtime keys so we store NULL in the runtime key info. - * Otherwise runtime key info contains an array of pointers - * (one for each index) to arrays of flags (one for each key) - * which indicate that the qual needs to be evaluated at runtime. - * -cim 10/24/89 - * ---------------- - */ - if (have_runtime_keys) + if (have_runtime_keys) { - indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo; + indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo; } - else { - indexstate->iss_RuntimeKeyInfo = NULL; - for (i=0; i < numIndices; i++) { - List *qual; - int n_keys; - qual = nth(i, indxqual); - n_keys = length(qual); - if (n_keys > 0) - pfree(runtimeKeyInfo[i]); + else + { + indexstate->iss_RuntimeKeyInfo = NULL; + for (i = 0; i < numIndices; i++) + { + List *qual; + int n_keys; + + qual = nth(i, indxqual); + n_keys = length(qual); + if (n_keys > 0) + pfree(runtimeKeyInfo[i]); + } + pfree(runtimeKeyInfo); } - pfree(runtimeKeyInfo); - } - - /* ---------------- - * get the range table and direction information - * from the execution state (these are needed to - * open the relations). - * ---------------- - */ - rangeTable = estate->es_range_table; - direction = estate->es_direction; - - /* ---------------- - * open the base relation - * ---------------- - */ - relid = node->scan.scanrelid; - rtentry = rt_fetch(relid, rangeTable); - reloid = rtentry->relid; - timeQual = rtentry->timeQual; - - ExecOpenScanR(reloid, /* relation */ - 0, /* nkeys */ - (ScanKey) NULL, /* scan key */ - 0, /* is index */ - direction, /* scan direction */ - timeQual, /* time qual */ - ¤tRelation, /* return: rel desc */ - (Pointer *) ¤tScanDesc); /* return: scan desc */ - - scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = currentScanDesc; - - - /* ---------------- - * get the scan type from the relation descriptor. - * ---------------- - */ - ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation)); - ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); - - /* ---------------- - * index scans don't have subtrees.. - * ---------------- - */ -/* scanstate->ss_ProcOuterFlag = false; */ - - /* ---------------- - * open the index relations and initialize - * relation and scan descriptors. - * ---------------- - */ - for (i=0; i < numIndices; i++) { - Oid indexOid; - - indexOid = (Oid)nthi(i, indxid); - - if (indexOid != 0) { - ExecOpenScanR(indexOid, /* relation */ - numScanKeys[ i ], /* nkeys */ - scanKeys[ i ], /* scan key */ - true, /* is index */ - direction, /* scan direction */ - timeQual, /* time qual */ - &(relationDescs[ i ]), /* return: rel desc */ - (Pointer *) &(scanDescs[ i ])); - /* return: scan desc */ + + /* ---------------- + * get the range table and direction information + * from the execution state (these are needed to + * open the relations). + * ---------------- + */ + rangeTable = estate->es_range_table; + direction = estate->es_direction; + + /* ---------------- + * open the base relation + * ---------------- + */ + relid = node->scan.scanrelid; + rtentry = rt_fetch(relid, rangeTable); + reloid = rtentry->relid; + timeQual = rtentry->timeQual; + + ExecOpenScanR(reloid, /* relation */ + 0, /* nkeys */ + (ScanKey) NULL, /* scan key */ + 0, /* is index */ + direction, /* scan direction */ + timeQual, /* time qual */ + ¤tRelation, /* return: rel desc */ + (Pointer *) & currentScanDesc); /* return: scan desc */ + + scanstate->css_currentRelation = currentRelation; + scanstate->css_currentScanDesc = currentScanDesc; + + + /* ---------------- + * get the scan type from the relation descriptor. + * ---------------- + */ + ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation)); + ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); + + /* ---------------- + * index scans don't have subtrees.. + * ---------------- + */ +/* scanstate->ss_ProcOuterFlag = false; */ + + /* ---------------- + * open the index relations and initialize + * relation and scan descriptors. + * ---------------- + */ + for (i = 0; i < numIndices; i++) + { + Oid indexOid; + + indexOid = (Oid) nthi(i, indxid); + + if (indexOid != 0) + { + ExecOpenScanR(indexOid, /* relation */ + numScanKeys[i], /* nkeys */ + scanKeys[i], /* scan key */ + true, /* is index */ + direction, /* scan direction */ + timeQual, /* time qual */ + &(relationDescs[i]), /* return: rel desc */ + (Pointer *) & (scanDescs[i])); + /* return: scan desc */ + } } - } - indexstate->iss_RelationDescs = relationDescs; - indexstate->iss_ScanDescs = scanDescs; + indexstate->iss_RelationDescs = relationDescs; + indexstate->iss_ScanDescs = scanDescs; - indexstate->cstate.cs_TupFromTlist = false; + indexstate->cstate.cs_TupFromTlist = false; - /* ---------------- - * all done. - * ---------------- - */ - return TRUE; + /* ---------------- + * all done. + * ---------------- + */ + return TRUE; } int -ExecCountSlotsIndexScan(IndexScan *node) +ExecCountSlotsIndexScan(IndexScan * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - INDEXSCAN_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + INDEXSCAN_NSLOTS; } diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index daf324bb77a..49ba73d3bf0 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * nodeMaterial.c-- - * Routines to handle materialization nodes. + * Routines to handle materialization nodes. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.6 1997/08/20 14:53:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.7 1997/09/07 04:41:36 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecMaterial - generate a temporary relation - * ExecInitMaterial - initialize node and subnodes.. - * ExecEndMaterial - shutdown node and subnodes + * ExecMaterial - generate a temporary relation + * ExecInitMaterial - initialize node and subnodes.. + * ExecEndMaterial - shutdown node and subnodes * */ #include "postgres.h" @@ -29,368 +29,373 @@ #include "access/heapam.h" /* ---------------------------------------------------------------- - * ExecMaterial + * ExecMaterial * - * The first time this is called, ExecMaterial retrieves tuples - * this node's outer subplan and inserts them into a temporary - * relation. After this is done, a flag is set indicating that - * the subplan has been materialized. Once the relation is - * materialized, the first tuple is then returned. Successive - * calls to ExecMaterial return successive tuples from the temp - * relation. + * The first time this is called, ExecMaterial retrieves tuples + * this node's outer subplan and inserts them into a temporary + * relation. After this is done, a flag is set indicating that + * the subplan has been materialized. Once the relation is + * materialized, the first tuple is then returned. Successive + * calls to ExecMaterial return successive tuples from the temp + * relation. * - * Initial State: + * Initial State: * - * ExecMaterial assumes the temporary relation has been - * created and openend by ExecInitMaterial during the prior - * InitPlan() phase. + * ExecMaterial assumes the temporary relation has been + * created and openend by ExecInitMaterial during the prior + * InitPlan() phase. * * ---------------------------------------------------------------- */ -TupleTableSlot * /* result tuple from subplan */ -ExecMaterial(Material *node) +TupleTableSlot * /* result tuple from subplan */ +ExecMaterial(Material * node) { - EState *estate; - MaterialState *matstate; - Plan *outerNode; - ScanDirection dir; - Relation tempRelation; - Relation currentRelation; - HeapScanDesc currentScanDesc; - HeapTuple heapTuple; - TupleTableSlot *slot; - Buffer buffer; - - /* ---------------- - * get state info from node - * ---------------- - */ - matstate = node->matstate; - estate = node->plan.state; - dir = estate->es_direction; - - /* ---------------- - * the first time we call this, we retrieve all tuples - * from the subplan into a temporary relation and then - * we sort the relation. Subsequent calls return tuples - * from the temporary relation. - * ---------------- - */ - - if (matstate->mat_Flag == false) { + EState *estate; + MaterialState *matstate; + Plan *outerNode; + ScanDirection dir; + Relation tempRelation; + Relation currentRelation; + HeapScanDesc currentScanDesc; + HeapTuple heapTuple; + TupleTableSlot *slot; + Buffer buffer; + /* ---------------- - * set all relations to be scanned in the forward direction - * while creating the temporary relation. + * get state info from node * ---------------- */ - estate->es_direction = ForwardScanDirection; - + matstate = node->matstate; + estate = node->plan.state; + dir = estate->es_direction; + /* ---------------- - * if we couldn't create the temp or current relations then - * we print a warning and return NULL. + * the first time we call this, we retrieve all tuples + * from the subplan into a temporary relation and then + * we sort the relation. Subsequent calls return tuples + * from the temporary relation. * ---------------- */ - tempRelation = matstate->mat_TempRelation; - if (tempRelation == NULL) { - elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting..."); - return NULL; - } - - currentRelation = matstate->csstate.css_currentRelation; - if (currentRelation == NULL) { - elog(DEBUG, "ExecMaterial: current relation is NULL! aborting..."); - return NULL; + + if (matstate->mat_Flag == false) + { + /* ---------------- + * set all relations to be scanned in the forward direction + * while creating the temporary relation. + * ---------------- + */ + estate->es_direction = ForwardScanDirection; + + /* ---------------- + * if we couldn't create the temp or current relations then + * we print a warning and return NULL. + * ---------------- + */ + tempRelation = matstate->mat_TempRelation; + if (tempRelation == NULL) + { + elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting..."); + return NULL; + } + + currentRelation = matstate->csstate.css_currentRelation; + if (currentRelation == NULL) + { + elog(DEBUG, "ExecMaterial: current relation is NULL! aborting..."); + return NULL; + } + + /* ---------------- + * retrieve tuples from the subplan and + * insert them in the temporary relation + * ---------------- + */ + outerNode = outerPlan((Plan *) node); + for (;;) + { + slot = ExecProcNode(outerNode, (Plan *) node); + + heapTuple = slot->val; + if (heapTuple == NULL) + break; + + heap_insert(tempRelation, /* relation desc */ + heapTuple); /* heap tuple to insert */ + + ExecClearTuple(slot); + } + currentRelation = tempRelation; + + /* ---------------- + * restore to user specified direction + * ---------------- + */ + estate->es_direction = dir; + + /* ---------------- + * now initialize the scan descriptor to scan the + * sorted relation and update the sortstate information + * ---------------- + */ + currentScanDesc = heap_beginscan(currentRelation, /* relation */ + ScanDirectionIsBackward(dir), + /* bkwd flag */ + NowTimeQual, /* time qual */ + 0, /* num scan keys */ + NULL); /* scan keys */ + matstate->csstate.css_currentRelation = currentRelation; + matstate->csstate.css_currentScanDesc = currentScanDesc; + + ExecAssignScanType(&matstate->csstate, + RelationGetTupleDescriptor(currentRelation)); + + /* ---------------- + * finally set the sorted flag to true + * ---------------- + */ + matstate->mat_Flag = true; } - + /* ---------------- - * retrieve tuples from the subplan and - * insert them in the temporary relation + * at this point we know we have a sorted relation so + * we preform a simple scan on it with amgetnext().. * ---------------- */ - outerNode = outerPlan((Plan *) node); - for (;;) { - slot = ExecProcNode(outerNode, (Plan*) node); - - heapTuple = slot->val; - if (heapTuple == NULL) - break; - - heap_insert(tempRelation, /* relation desc */ - heapTuple); /* heap tuple to insert */ - - ExecClearTuple( slot); - } - currentRelation = tempRelation; - + currentScanDesc = matstate->csstate.css_currentScanDesc; + + heapTuple = heap_getnext(currentScanDesc, /* scan desc */ + ScanDirectionIsBackward(dir), + /* bkwd flag */ + &buffer); /* return: buffer */ + /* ---------------- - * restore to user specified direction + * put the tuple into the scan tuple slot and return the slot. + * Note: since the tuple is really a pointer to a page, we don't want + * to call pfree() on it.. * ---------------- */ - estate->es_direction = dir; - + slot = (TupleTableSlot *) matstate->csstate.css_ScanTupleSlot; + + return ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + buffer, /* buffer for this tuple */ + false); /* don't pfree this pointer */ + +} + +/* ---------------------------------------------------------------- + * ExecInitMaterial + * ---------------------------------------------------------------- + */ +bool /* initialization status */ +ExecInitMaterial(Material * node, EState * estate, Plan * parent) +{ + MaterialState *matstate; + Plan *outerPlan; + TupleDesc tupType; + Relation tempDesc; + + /* int len; */ + /* ---------------- - * now initialize the scan descriptor to scan the - * sorted relation and update the sortstate information + * assign the node's execution state * ---------------- */ - currentScanDesc = heap_beginscan(currentRelation, /* relation */ - ScanDirectionIsBackward(dir), - /* bkwd flag */ - NowTimeQual, /* time qual */ - 0, /* num scan keys */ - NULL); /* scan keys */ - matstate->csstate.css_currentRelation = currentRelation; - matstate->csstate.css_currentScanDesc = currentScanDesc; - - ExecAssignScanType(&matstate->csstate, - RelationGetTupleDescriptor(currentRelation)); - + node->plan.state = estate; + /* ---------------- - * finally set the sorted flag to true + * create state structure * ---------------- */ - matstate->mat_Flag = true; - } - - /* ---------------- - * at this point we know we have a sorted relation so - * we preform a simple scan on it with amgetnext().. - * ---------------- - */ - currentScanDesc = matstate->csstate.css_currentScanDesc; - - heapTuple = heap_getnext(currentScanDesc, /* scan desc */ - ScanDirectionIsBackward(dir), - /* bkwd flag */ - &buffer); /* return: buffer */ - - /* ---------------- - * put the tuple into the scan tuple slot and return the slot. - * Note: since the tuple is really a pointer to a page, we don't want - * to call pfree() on it.. - * ---------------- - */ - slot = (TupleTableSlot *)matstate->csstate.css_ScanTupleSlot; - - return ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - buffer, /* buffer for this tuple */ - false); /* don't pfree this pointer */ - -} + matstate = makeNode(MaterialState); + matstate->mat_Flag = false; + matstate->mat_TempRelation = NULL; + node->matstate = matstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + assign result tuple slot + * + * Materialization nodes don't need ExprContexts because + * they never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent); -/* ---------------------------------------------------------------- - * ExecInitMaterial - * ---------------------------------------------------------------- - */ -bool /* initialization status */ -ExecInitMaterial(Material *node, EState *estate, Plan *parent) -{ - MaterialState *matstate; - Plan *outerPlan; - TupleDesc tupType; - Relation tempDesc; - /* int len; */ - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - matstate = makeNode(MaterialState); - matstate->mat_Flag = false; - matstate->mat_TempRelation = NULL; - node->matstate = matstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + assign result tuple slot - * - * Materialization nodes don't need ExprContexts because - * they never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent); - #define MATERIAL_NSLOTS 1 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitScanTupleSlot(estate, &matstate->csstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* ---------------- - * initialize matstate information - * ---------------- - */ - matstate->mat_Flag = false; - - /* ---------------- - * initialize tuple type. no need to initialize projection - * info because this node doesn't do projections. - * ---------------- - */ - ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate); - matstate->csstate.cstate.cs_ProjInfo = NULL; - - /* ---------------- - * get type information needed for ExecCreatR - * ---------------- - */ - tupType = ExecGetScanType(&matstate->csstate); - - /* ---------------- - * ExecCreatR wants it's second argument to be an object id of - * a relation in the range table or a _TEMP_RELATION_ID - * indicating that the relation is not in the range table. - * - * In the second case ExecCreatR creates a temp relation. - * (currently this is the only case we support -cim 10/16/89) - * ---------------- - */ - /* ---------------- - * create the temporary relation - * ---------------- - */ -/* len = ExecTargetListLength(node->plan.targetlist); */ - tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_); - - /* ---------------- - * save the relation descriptor in the sortstate - * ---------------- - */ - matstate->mat_TempRelation = tempDesc; - matstate->csstate.css_currentRelation = tempDesc; - - /* ---------------- - * return relation oid of temporary relation in a list - * (someday -- for now we return LispTrue... cim 10/12/89) - * ---------------- - */ - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitScanTupleSlot(estate, &matstate->csstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize matstate information + * ---------------- + */ + matstate->mat_Flag = false; + + /* ---------------- + * initialize tuple type. no need to initialize projection + * info because this node doesn't do projections. + * ---------------- + */ + ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate); + matstate->csstate.cstate.cs_ProjInfo = NULL; + + /* ---------------- + * get type information needed for ExecCreatR + * ---------------- + */ + tupType = ExecGetScanType(&matstate->csstate); + + /* ---------------- + * ExecCreatR wants it's second argument to be an object id of + * a relation in the range table or a _TEMP_RELATION_ID + * indicating that the relation is not in the range table. + * + * In the second case ExecCreatR creates a temp relation. + * (currently this is the only case we support -cim 10/16/89) + * ---------------- + */ + /* ---------------- + * create the temporary relation + * ---------------- + */ +/* len = ExecTargetListLength(node->plan.targetlist); */ + tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_); + + /* ---------------- + * save the relation descriptor in the sortstate + * ---------------- + */ + matstate->mat_TempRelation = tempDesc; + matstate->csstate.css_currentRelation = tempDesc; + + /* ---------------- + * return relation oid of temporary relation in a list + * (someday -- for now we return LispTrue... cim 10/12/89) + * ---------------- + */ + return TRUE; } int -ExecCountSlotsMaterial(Material *node) +ExecCountSlotsMaterial(Material * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - MATERIAL_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + MATERIAL_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndMaterial + * ExecEndMaterial * * old comments - * destroys the temporary relation. + * destroys the temporary relation. * ---------------------------------------------------------------- */ void -ExecEndMaterial(Material *node) +ExecEndMaterial(Material * node) { - MaterialState *matstate; - Relation tempRelation; - Plan *outerPlan; - - /* ---------------- - * get info from the material state - * ---------------- - */ - matstate = node->matstate; - tempRelation = matstate->mat_TempRelation; - - heap_destroyr(tempRelation); - - /* ---------------- - * close the temp relation and shut down the scan. - * ---------------- - */ - ExecCloseR((Plan *) node); - - /* ---------------- - * shut down the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecEndNode(outerPlan, (Plan*) node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(matstate->csstate.css_ScanTupleSlot); -} - -#ifdef NOT_USED /* not used */ + MaterialState *matstate; + Relation tempRelation; + Plan *outerPlan; + + /* ---------------- + * get info from the material state + * ---------------- + */ + matstate = node->matstate; + tempRelation = matstate->mat_TempRelation; + + heap_destroyr(tempRelation); + + /* ---------------- + * close the temp relation and shut down the scan. + * ---------------- + */ + ExecCloseR((Plan *) node); + + /* ---------------- + * shut down the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecEndNode(outerPlan, (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(matstate->csstate.css_ScanTupleSlot); +} + +#ifdef NOT_USED /* not used */ /* ---------------------------------------------------------------- - * ExecMaterialMarkPos + * ExecMaterialMarkPos * ---------------------------------------------------------------- */ -List /* nothing of interest */ +List /* nothing of interest */ ExecMaterialMarkPos(Material node) { - MaterialState matstate; - HeapScanDesc sdesc; - - /* ---------------- - * if we haven't materialized yet, just return NIL. - * ---------------- - */ - matstate = get_matstate(node); - if (get_mat_Flag(matstate) == false) + MaterialState matstate; + HeapScanDesc sdesc; + + /* ---------------- + * if we haven't materialized yet, just return NIL. + * ---------------- + */ + matstate = get_matstate(node); + if (get_mat_Flag(matstate) == false) + return NIL; + + /* ---------------- + * XXX access methods don't return positions yet so + * for now we return NIL. It's possible that + * they will never return positions for all I know -cim 10/16/89 + * ---------------- + */ + sdesc = get_css_currentScanDesc((CommonScanState) matstate); + heap_markpos(sdesc); + return NIL; - - /* ---------------- - * XXX access methods don't return positions yet so - * for now we return NIL. It's possible that - * they will never return positions for all I know -cim 10/16/89 - * ---------------- - */ - sdesc = get_css_currentScanDesc((CommonScanState)matstate); - heap_markpos(sdesc); - - return NIL; } /* ---------------------------------------------------------------- - * ExecMaterialRestrPos + * ExecMaterialRestrPos * ---------------------------------------------------------------- */ void ExecMaterialRestrPos(Material node) { - MaterialState matstate; - HeapScanDesc sdesc; - - /* ---------------- - * if we haven't materialized yet, just return. - * ---------------- - */ - matstate = get_matstate(node); - if (get_mat_Flag(matstate) == false) - return; - - /* ---------------- - * restore the scan to the previously marked position - * ---------------- - */ - sdesc = get_css_currentScanDesc((CommonScanState)matstate); - heap_restrpos(sdesc); + MaterialState matstate; + HeapScanDesc sdesc; + + /* ---------------- + * if we haven't materialized yet, just return. + * ---------------- + */ + matstate = get_matstate(node); + if (get_mat_Flag(matstate) == false) + return; + + /* ---------------- + * restore the scan to the previously marked position + * ---------------- + */ + sdesc = get_css_currentScanDesc((CommonScanState) matstate); + heap_restrpos(sdesc); } -#endif +#endif diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 9151479d306..348d3fa1e00 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -1,78 +1,78 @@ /*------------------------------------------------------------------------- * * nodeMergejoin.c-- - * routines supporting merge joins + * routines supporting merge joins * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.8 1997/08/19 21:31:10 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.9 1997/09/07 04:41:37 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecMergeJoin mergejoin outer and inner relations. - * ExecInitMergeJoin creates and initializes run time states - * ExecEndMergeJoin cleand up the node. + * ExecMergeJoin mergejoin outer and inner relations. + * ExecInitMergeJoin creates and initializes run time states + * ExecEndMergeJoin cleand up the node. * * NOTES - * Essential operation of the merge join algorithm is as follows: - * (** indicates the tuples satisify the merge clause). + * Essential operation of the merge join algorithm is as follows: + * (** indicates the tuples satisify the merge clause). * - * Join { - - * get initial outer and inner tuples INITIALIZE - * Skip Inner SKIPINNER - * mark inner position JOINMARK - * do forever { - - * while (outer ** inner) { JOINTEST - * join tuples JOINTUPLES - * advance inner position NEXTINNER - * } - - * advance outer position NEXTOUTER - * if (outer ** mark) { TESTOUTER - * restore inner position to mark TESTOUTER - * continue - - * } else { - - * Skip Outer SKIPOUTER - * mark inner position JOINMARK - * } - - * } - - * } - + * Join { - + * get initial outer and inner tuples INITIALIZE + * Skip Inner SKIPINNER + * mark inner position JOINMARK + * do forever { - + * while (outer ** inner) { JOINTEST + * join tuples JOINTUPLES + * advance inner position NEXTINNER + * } - + * advance outer position NEXTOUTER + * if (outer ** mark) { TESTOUTER + * restore inner position to mark TESTOUTER + * continue - + * } else { - + * Skip Outer SKIPOUTER + * mark inner position JOINMARK + * } - + * } - + * } - * - * Skip Outer { SKIPOUTER - * if (inner ** outer) Join Tuples JOINTUPLES - * while (outer < inner) SKIPOUTER - * advance outer SKIPOUTER - * if (outer > inner) SKIPOUTER - * Skip Inner SKIPINNER - * } - + * Skip Outer { SKIPOUTER + * if (inner ** outer) Join Tuples JOINTUPLES + * while (outer < inner) SKIPOUTER + * advance outer SKIPOUTER + * if (outer > inner) SKIPOUTER + * Skip Inner SKIPINNER + * } - * - * Skip Inner { SKIPINNER - * if (inner ** outer) Join Tuples JOINTUPLES - * while (outer > inner) SKIPINNER - * advance inner SKIPINNER - * if (outer < inner) SKIPINNER - * Skip Outer SKIPOUTER - * } - + * Skip Inner { SKIPINNER + * if (inner ** outer) Join Tuples JOINTUPLES + * while (outer > inner) SKIPINNER + * advance inner SKIPINNER + * if (outer < inner) SKIPINNER + * Skip Outer SKIPOUTER + * } - * - * Currently, the merge join operation is coded in the fashion - * of a state machine. At each state, we do something and then - * proceed to another state. This state is stored in the node's - * execution state information and is preserved across calls to - * ExecMergeJoin. -cim 10/31/89 + * Currently, the merge join operation is coded in the fashion + * of a state machine. At each state, we do something and then + * proceed to another state. This state is stored in the node's + * execution state information and is preserved across calls to + * ExecMergeJoin. -cim 10/31/89 * - * Warning: This code is known to fail for inequality operations - * and is being redesigned. Specifically, = and > work - * but the logic is not correct for <. Since mergejoins - * are no better then nestloops for inequalitys, the planner - * should not plan them anyways. Alternatively, the - * planner could just exchange the inner/outer relations - * if it ever sees a <... -cim 7/1/90 + * Warning: This code is known to fail for inequality operations + * and is being redesigned. Specifically, = and > work + * but the logic is not correct for <. Since mergejoins + * are no better then nestloops for inequalitys, the planner + * should not plan them anyways. Alternatively, the + * planner could just exchange the inner/outer relations + * if it ever sees a <... -cim 7/1/90 * - * Update: The executor tuple table has long since alleviated the - * problem described above -cim 4/23/91 + * Update: The executor tuple table has long since alleviated the + * problem described above -cim 4/23/91 * */ #include "postgres.h" @@ -84,1134 +84,1151 @@ #include "utils/lsyscache.h" #include "utils/psort.h" -static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext); +static bool MergeCompare(List * eqQual, List * compareQual, ExprContext * econtext); /* ---------------------------------------------------------------- - * MarkInnerTuple and RestoreInnerTuple macros + * MarkInnerTuple and RestoreInnerTuple macros * - * when we "mark" a tuple, we place a pointer to it - * in the marked tuple slot. now there are two pointers - * to this tuple and we don't want it to be freed until - * next time we mark a tuple, so we move the policy to - * the marked tuple slot and set the inner tuple slot policy - * to false. + * when we "mark" a tuple, we place a pointer to it + * in the marked tuple slot. now there are two pointers + * to this tuple and we don't want it to be freed until + * next time we mark a tuple, so we move the policy to + * the marked tuple slot and set the inner tuple slot policy + * to false. * - * But, when we restore the inner tuple, the marked tuple - * retains the policy. Basically once a tuple is marked, it - * should only be freed when we mark another tuple. -cim 9/27/90 + * But, when we restore the inner tuple, the marked tuple + * retains the policy. Basically once a tuple is marked, it + * should only be freed when we mark another tuple. -cim 9/27/90 * - * Note: now that we store buffers in the tuple table, - * we have to also increment buffer reference counts - * correctly whenever we propagate an additional pointer - * to a buffer item. Later, when ExecStoreTuple() is - * called again on this slot, the refcnt is decremented - * when the old tuple is replaced. + * Note: now that we store buffers in the tuple table, + * we have to also increment buffer reference counts + * correctly whenever we propagate an additional pointer + * to a buffer item. Later, when ExecStoreTuple() is + * called again on this slot, the refcnt is decremented + * when the old tuple is replaced. * ---------------------------------------------------------------- */ #define MarkInnerTuple(innerTupleSlot, mergestate) \ { \ - bool shouldFree; \ - shouldFree = ExecSetSlotPolicy(innerTupleSlot, false); \ - ExecStoreTuple(innerTupleSlot->val, \ - mergestate->mj_MarkedTupleSlot, \ - innerTupleSlot->ttc_buffer, \ - shouldFree); \ - ExecIncrSlotBufferRefcnt(innerTupleSlot); \ + bool shouldFree; \ + shouldFree = ExecSetSlotPolicy(innerTupleSlot, false); \ + ExecStoreTuple(innerTupleSlot->val, \ + mergestate->mj_MarkedTupleSlot, \ + innerTupleSlot->ttc_buffer, \ + shouldFree); \ + ExecIncrSlotBufferRefcnt(innerTupleSlot); \ } #define RestoreInnerTuple(innerTupleSlot, markedTupleSlot) \ - ExecStoreTuple(markedTupleSlot->val, \ - innerTupleSlot, \ - markedTupleSlot->ttc_buffer, \ - false); \ - ExecIncrSlotBufferRefcnt(innerTupleSlot) + ExecStoreTuple(markedTupleSlot->val, \ + innerTupleSlot, \ + markedTupleSlot->ttc_buffer, \ + false); \ + ExecIncrSlotBufferRefcnt(innerTupleSlot) /* ---------------------------------------------------------------- - * MJFormOSortopI + * MJFormOSortopI * - * This takes the mergeclause which is a qualification of the - * form ((= expr expr) (= expr expr) ...) and forms a new - * qualification like ((> expr expr) (> expr expr) ...) which - * is used by ExecMergeJoin() in order to determine if we should - * skip tuples. + * This takes the mergeclause which is a qualification of the + * form ((= expr expr) (= expr expr) ...) and forms a new + * qualification like ((> expr expr) (> expr expr) ...) which + * is used by ExecMergeJoin() in order to determine if we should + * skip tuples. * * old comments - * The 'qual' must be of the form: - * {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...} - * The "sortOp outerkey innerkey" is formed by substituting the "=" - * by "sortOp". + * The 'qual' must be of the form: + * {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...} + * The "sortOp outerkey innerkey" is formed by substituting the "=" + * by "sortOp". * ---------------------------------------------------------------- */ -static List * -MJFormOSortopI(List *qualList, Oid sortOp) +static List * +MJFormOSortopI(List * qualList, Oid sortOp) { - List *qualCopy; - List *qualcdr; - Expr *qual; - Oper *op; - - /* ---------------- - * qualList is a list: ((op .. ..) ...) - * first we make a copy of it. copyObject() makes a deep copy - * so let's use it instead of the old fashoned lispCopy()... - * ---------------- - */ - qualCopy = (List*) copyObject((Node*) qualList); - - foreach (qualcdr, qualCopy) { - /* ---------------- - * first get the current (op .. ..) list - * ---------------- - */ - qual = lfirst(qualcdr); - + List *qualCopy; + List *qualcdr; + Expr *qual; + Oper *op; + /* ---------------- - * now get at the op + * qualList is a list: ((op .. ..) ...) + * first we make a copy of it. copyObject() makes a deep copy + * so let's use it instead of the old fashoned lispCopy()... * ---------------- */ - op = (Oper*)qual->oper; - if (!IsA(op,Oper)) { - elog(DEBUG, "MJFormOSortopI: op not an Oper!"); - return NIL; + qualCopy = (List *) copyObject((Node *) qualList); + + foreach(qualcdr, qualCopy) + { + /* ---------------- + * first get the current (op .. ..) list + * ---------------- + */ + qual = lfirst(qualcdr); + + /* ---------------- + * now get at the op + * ---------------- + */ + op = (Oper *) qual->oper; + if (!IsA(op, Oper)) + { + elog(DEBUG, "MJFormOSortopI: op not an Oper!"); + return NIL; + } + + /* ---------------- + * change it's opid and since Op nodes now carry around a + * cached pointer to the associated op function, we have + * to make sure we invalidate this. Otherwise you get bizarre + * behavior when someone runs a mergejoin with _exec_repeat_ > 1 + * -cim 4/23/91 + * ---------------- + */ + op->opid = sortOp; + op->op_fcache = NULL; } - - /* ---------------- - * change it's opid and since Op nodes now carry around a - * cached pointer to the associated op function, we have - * to make sure we invalidate this. Otherwise you get bizarre - * behavior when someone runs a mergejoin with _exec_repeat_ > 1 - * -cim 4/23/91 - * ---------------- - */ - op->opid = sortOp; - op->op_fcache = NULL; - } - - return qualCopy; + + return qualCopy; } - + /* ---------------------------------------------------------------- - * MJFormISortopO + * MJFormISortopO * - * This does the same thing as MJFormOSortopI() except that - * it also reverses the expressions in the qualifications. - * For example: ((= expr1 expr2)) produces ((> expr2 expr1)) + * This does the same thing as MJFormOSortopI() except that + * it also reverses the expressions in the qualifications. + * For example: ((= expr1 expr2)) produces ((> expr2 expr1)) * * old comments - * The 'qual' must be of the form: - * {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...} - * The 'sortOp innerkey1 outerkey" is formed by substituting the "=" - * by "sortOp" and reversing the positions of the keys. - * ---------------------------------------------------------------- + * The 'qual' must be of the form: + * {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...} + * The 'sortOp innerkey1 outerkey" is formed by substituting the "=" + * by "sortOp" and reversing the positions of the keys. + * ---------------------------------------------------------------- */ -static List * -MJFormISortopO(List *qualList, Oid sortOp) +static List * +MJFormISortopO(List * qualList, Oid sortOp) { - List *ISortopO; - List *qualcdr; - - /* ---------------- - * first generate OSortopI, a list of the form - * ((op outer inner) (op outer inner) ... ) - * ---------------- - */ - ISortopO = MJFormOSortopI(qualList, sortOp); - - /* ---------------- - * now swap the cadr and caddr of each qual to form ISortopO, - * ((op inner outer) (op inner outer) ... ) - * ---------------- - */ - foreach (qualcdr, ISortopO) { - Expr *qual; - List *inner; - List *outer; - qual = lfirst(qualcdr); - - inner = lfirst(qual->args); - outer = lfirst(lnext(qual->args)); - lfirst(qual->args) = outer; - lfirst(lnext(qual->args)) = inner; - } - - return ISortopO; + List *ISortopO; + List *qualcdr; + + /* ---------------- + * first generate OSortopI, a list of the form + * ((op outer inner) (op outer inner) ... ) + * ---------------- + */ + ISortopO = MJFormOSortopI(qualList, sortOp); + + /* ---------------- + * now swap the cadr and caddr of each qual to form ISortopO, + * ((op inner outer) (op inner outer) ... ) + * ---------------- + */ + foreach(qualcdr, ISortopO) + { + Expr *qual; + List *inner; + List *outer; + + qual = lfirst(qualcdr); + + inner = lfirst(qual->args); + outer = lfirst(lnext(qual->args)); + lfirst(qual->args) = outer; + lfirst(lnext(qual->args)) = inner; + } + + return ISortopO; } - + /* ---------------------------------------------------------------- - * MergeCompare - * - * Compare the keys according to 'compareQual' which is of the - * form: {(key1a > key2a)(key1b > key2b) ...}. + * MergeCompare * - * (actually, it could also be the form (key1a < key2a)..) - * - * This is different from calling ExecQual because ExecQual returns - * true only if ALL the comparisions clauses are satisfied. - * However, there is an order of significance among the keys with - * the first keys being most significant. Therefore, the clauses - * are evaluated in order and the 'compareQual' is satisfied - * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i. + * Compare the keys according to 'compareQual' which is of the + * form: {(key1a > key2a)(key1b > key2b) ...}. + * + * (actually, it could also be the form (key1a < key2a)..) + * + * This is different from calling ExecQual because ExecQual returns + * true only if ALL the comparisions clauses are satisfied. + * However, there is an order of significance among the keys with + * the first keys being most significant. Therefore, the clauses + * are evaluated in order and the 'compareQual' is satisfied + * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i. * ---------------------------------------------------------------- */ -static bool -MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) +static bool +MergeCompare(List * eqQual, List * compareQual, ExprContext * econtext) { - List *clause; - List *eqclause; - Datum const_value; - bool isNull; - bool isDone; - - /* ---------------- - * if we have no compare qualification, return nil - * ---------------- - */ - if (compareQual == NIL) - return false; - - /* ---------------- - * for each pair of clauses, test them until - * our compare conditions are satisified - * ---------------- - */ - eqclause = eqQual; - foreach (clause, compareQual) { + List *clause; + List *eqclause; + Datum const_value; + bool isNull; + bool isDone; + /* ---------------- - * first test if our compare clause is satisified. - * if so then return true. ignore isDone, don't iterate in - * quals. + * if we have no compare qualification, return nil * ---------------- */ - const_value = (Datum) - ExecEvalExpr((Node*) lfirst(clause), econtext, &isNull, &isDone); - - if (DatumGetInt32(const_value) != 0) - return true; - + if (compareQual == NIL) + return false; + /* ---------------- - * ok, the compare clause failed so we test if the keys - * are equal... if key1 != key2, we return false. - * otherwise key1 = key2 so we move on to the next pair of keys. - * - * ignore isDone, don't iterate in quals. + * for each pair of clauses, test them until + * our compare conditions are satisified * ---------------- */ - const_value = ExecEvalExpr((Node*) lfirst(eqclause), - econtext, - &isNull, - &isDone); - - if (DatumGetInt32(const_value) == 0) - return false; - eqclause = lnext(eqclause); - } - - /* ---------------- - * if we get here then it means none of our key greater-than - * conditions were satisified so we return false. - * ---------------- - */ - return false; + eqclause = eqQual; + foreach(clause, compareQual) + { + /* ---------------- + * first test if our compare clause is satisified. + * if so then return true. ignore isDone, don't iterate in + * quals. + * ---------------- + */ + const_value = (Datum) + ExecEvalExpr((Node *) lfirst(clause), econtext, &isNull, &isDone); + + if (DatumGetInt32(const_value) != 0) + return true; + + /* ---------------- + * ok, the compare clause failed so we test if the keys + * are equal... if key1 != key2, we return false. + * otherwise key1 = key2 so we move on to the next pair of keys. + * + * ignore isDone, don't iterate in quals. + * ---------------- + */ + const_value = ExecEvalExpr((Node *) lfirst(eqclause), + econtext, + &isNull, + &isDone); + + if (DatumGetInt32(const_value) == 0) + return false; + eqclause = lnext(eqclause); + } + + /* ---------------- + * if we get here then it means none of our key greater-than + * conditions were satisified so we return false. + * ---------------- + */ + return false; } - + /* ---------------------------------------------------------------- - * ExecMergeTupleDump + * ExecMergeTupleDump * - * This function is called through the MJ_dump() macro - * when EXEC_MERGEJOINDEBUG is defined + * This function is called through the MJ_dump() macro + * when EXEC_MERGEJOINDEBUG is defined * ---------------------------------------------------------------- */ #ifdef EXEC_MERGEJOINDEBUG void -ExecMergeTupleDumpInner(ExprContext *econtext) +ExecMergeTupleDumpInner(ExprContext * econtext) { - TupleTableSlot *innerSlot; - - printf("==== inner tuple ====\n"); - innerSlot = econtext->ecxt_innertuple; - if (TupIsNull(innerSlot)) - printf("(nil)\n"); - else - debugtup(innerSlot->val, - innerSlot->ttc_tupleDescriptor); + TupleTableSlot *innerSlot; + + printf("==== inner tuple ====\n"); + innerSlot = econtext->ecxt_innertuple; + if (TupIsNull(innerSlot)) + printf("(nil)\n"); + else + debugtup(innerSlot->val, + innerSlot->ttc_tupleDescriptor); } void -ExecMergeTupleDumpOuter(ExprContext *econtext) +ExecMergeTupleDumpOuter(ExprContext * econtext) { - TupleTableSlot *outerSlot; - - printf("==== outer tuple ====\n"); - outerSlot = econtext->ecxt_outertuple; - if (TupIsNull(outerSlot)) - printf("(nil)\n"); - else - debugtup(outerSlot->val, - outerSlot->ttc_tupleDescriptor); + TupleTableSlot *outerSlot; + + printf("==== outer tuple ====\n"); + outerSlot = econtext->ecxt_outertuple; + if (TupIsNull(outerSlot)) + printf("(nil)\n"); + else + debugtup(outerSlot->val, + outerSlot->ttc_tupleDescriptor); } void -ExecMergeTupleDumpMarked(ExprContext *econtext, - MergeJoinState *mergestate) +ExecMergeTupleDumpMarked(ExprContext * econtext, + MergeJoinState * mergestate) { - TupleTableSlot *markedSlot; + TupleTableSlot *markedSlot; - printf("==== marked tuple ====\n"); - markedSlot = mergestate->mj_MarkedTupleSlot; + printf("==== marked tuple ====\n"); + markedSlot = mergestate->mj_MarkedTupleSlot; - if (TupIsNull(markedSlot)) - printf("(nil)\n"); - else - debugtup(markedSlot->val, - markedSlot->ttc_tupleDescriptor); + if (TupIsNull(markedSlot)) + printf("(nil)\n"); + else + debugtup(markedSlot->val, + markedSlot->ttc_tupleDescriptor); } void -ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate) +ExecMergeTupleDump(ExprContext * econtext, MergeJoinState * mergestate) { - printf("******** ExecMergeTupleDump ********\n"); - - ExecMergeTupleDumpInner(econtext); - ExecMergeTupleDumpOuter(econtext); - ExecMergeTupleDumpMarked(econtext, mergestate); - - printf("******** \n"); + printf("******** ExecMergeTupleDump ********\n"); + + ExecMergeTupleDumpInner(econtext); + ExecMergeTupleDumpOuter(econtext); + ExecMergeTupleDumpMarked(econtext, mergestate); + + printf("******** \n"); } + #endif - + static void -CleanUpSort(Plan *plan) { - - if (plan == NULL) - return; - - if (plan->type == T_Sort) { - Sort *sort = (Sort *)plan; - psort_end(sort); - } +CleanUpSort(Plan * plan) +{ + + if (plan == NULL) + return; + + if (plan->type == T_Sort) + { + Sort *sort = (Sort *) plan; + + psort_end(sort); + } } /* ---------------------------------------------------------------- - * ExecMergeJoin + * ExecMergeJoin * * old comments - * Details of the merge-join routines: - * - * (1) ">" and "<" operators - * - * Merge-join is done by joining the inner and outer tuples satisfying - * the join clauses of the form ((= outerKey innerKey) ...). - * The join clauses is provided by the query planner and may contain - * more than one (= outerKey innerKey) clauses (for composite key). - * - * However, the query executor needs to know whether an outer - * tuple is "greater/smaller" than an inner tuple so that it can - * "synchronize" the two relations. For e.g., consider the following - * relations: - * - * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1 - * inner: (1 ^3 5 5 5 5 6) current tuple: 3 - * - * To continue the merge-join, the executor needs to scan both inner - * and outer relations till the matching tuples 5. It needs to know - * that currently inner tuple 3 is "greater" than outer tuple 1 and - * therefore it should scan the outer relation first to find a - * matching tuple and so on. - * - * Therefore, when initializing the merge-join node, the executor - * creates the "greater/smaller" clause by substituting the "=" - * operator in the join clauses with the sort operator used to - * sort the outer and inner relation forming (outerKey sortOp innerKey). - * The sort operator is "<" if the relations are in ascending order - * otherwise, it is ">" if the relations are in descending order. - * The opposite "smaller/greater" clause is formed by reversing the - * outer and inner keys forming (innerKey sortOp outerKey). - * - * (2) repositioning inner "cursor" - * - * Consider the above relations and suppose that the executor has - * just joined the first outer "5" with the last inner "5". The - * next step is of course to join the second outer "5" with all - * the inner "5's". This requires repositioning the inner "cursor" - * to point at the first inner "5". This is done by "marking" the - * first inner 5 and restore the "cursor" to it before joining - * with the second outer 5. The access method interface provides - * routines to mark and restore to a tuple. + * Details of the merge-join routines: + * + * (1) ">" and "<" operators + * + * Merge-join is done by joining the inner and outer tuples satisfying + * the join clauses of the form ((= outerKey innerKey) ...). + * The join clauses is provided by the query planner and may contain + * more than one (= outerKey innerKey) clauses (for composite key). + * + * However, the query executor needs to know whether an outer + * tuple is "greater/smaller" than an inner tuple so that it can + * "synchronize" the two relations. For e.g., consider the following + * relations: + * + * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1 + * inner: (1 ^3 5 5 5 5 6) current tuple: 3 + * + * To continue the merge-join, the executor needs to scan both inner + * and outer relations till the matching tuples 5. It needs to know + * that currently inner tuple 3 is "greater" than outer tuple 1 and + * therefore it should scan the outer relation first to find a + * matching tuple and so on. + * + * Therefore, when initializing the merge-join node, the executor + * creates the "greater/smaller" clause by substituting the "=" + * operator in the join clauses with the sort operator used to + * sort the outer and inner relation forming (outerKey sortOp innerKey). + * The sort operator is "<" if the relations are in ascending order + * otherwise, it is ">" if the relations are in descending order. + * The opposite "smaller/greater" clause is formed by reversing the + * outer and inner keys forming (innerKey sortOp outerKey). + * + * (2) repositioning inner "cursor" + * + * Consider the above relations and suppose that the executor has + * just joined the first outer "5" with the last inner "5". The + * next step is of course to join the second outer "5" with all + * the inner "5's". This requires repositioning the inner "cursor" + * to point at the first inner "5". This is done by "marking" the + * first inner 5 and restore the "cursor" to it before joining + * with the second outer 5. The access method interface provides + * routines to mark and restore to a tuple. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecMergeJoin(MergeJoin *node) +ExecMergeJoin(MergeJoin * node) { - EState *estate; - MergeJoinState *mergestate; - ScanDirection direction; - List *innerSkipQual; - List *outerSkipQual; - List *mergeclauses; - List *qual; - bool qualResult; - bool compareResult; - - Plan *innerPlan; - TupleTableSlot *innerTupleSlot; - - Plan *outerPlan; - TupleTableSlot *outerTupleSlot; - - TupleTableSlot *markedTupleSlot; - - ExprContext *econtext; - - /* ---------------- - * get information from node - * ---------------- - */ - mergestate = node->mergestate; - estate = node->join.state; - direction = estate->es_direction; - innerPlan = innerPlan((Plan *)node); - outerPlan = outerPlan((Plan *)node); - econtext = mergestate->jstate.cs_ExprContext; - mergeclauses = node->mergeclauses; - qual = node->join.qual; - - if (ScanDirectionIsForward(direction)) { - outerSkipQual = mergestate->mj_OSortopI; - innerSkipQual = mergestate->mj_ISortopO; - } else { - outerSkipQual = mergestate->mj_ISortopO; - innerSkipQual = mergestate->mj_OSortopI; - } - - /* ---------------- - * ok, everything is setup.. let's go to work - * ---------------- - */ - if (mergestate->jstate.cs_TupFromTlist) { - TupleTableSlot *result; - ProjectionInfo *projInfo; - bool isDone; - - projInfo = mergestate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - if (!isDone) - return result; - } - for (;;) { + EState *estate; + MergeJoinState *mergestate; + ScanDirection direction; + List *innerSkipQual; + List *outerSkipQual; + List *mergeclauses; + List *qual; + bool qualResult; + bool compareResult; + + Plan *innerPlan; + TupleTableSlot *innerTupleSlot; + + Plan *outerPlan; + TupleTableSlot *outerTupleSlot; + + TupleTableSlot *markedTupleSlot; + + ExprContext *econtext; + /* ---------------- - * get the current state of the join and do things accordingly. - * Note: The join states are highlighted with 32-* comments for - * improved readability. + * get information from node * ---------------- */ - MJ_dump(econtext, mergestate); - - switch (mergestate->mj_JoinState) { - /* ******************************** - * EXEC_MJ_INITIALIZE means that this is the first time - * ExecMergeJoin() has been called and so we have to - * initialize the inner, outer and marked tuples as well - * as various stuff in the expression context. - * ******************************** - */ - case EXEC_MJ_INITIALIZE: - MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n"); - /* ---------------- - * Note: at this point, if either of our inner or outer - * tuples are nil, then the join ends immediately because - * we know one of the subplans is empty. - * ---------------- - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - if (TupIsNull(innerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n"); - return NULL; - } - - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - if (TupIsNull(outerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); - return NULL; - } - - /* ---------------- - * store the inner and outer tuple in the merge state - * ---------------- - */ - econtext->ecxt_innertuple = innerTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * set the marked tuple to nil - * and initialize its tuple descriptor atttributes. - * -jeff 10 july 1991 - * ---------------- - */ - ExecClearTuple(mergestate->mj_MarkedTupleSlot); - mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor = - innerTupleSlot->ttc_tupleDescriptor; -/* - mergestate->mj_MarkedTupleSlot->ttc_execTupDescriptor = - innerTupleSlot->ttc_execTupDescriptor; -*/ - /* ---------------- - * initialize merge join state to skip inner tuples. - * ---------------- - */ - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; - break; - - /* ******************************** - * EXEC_MJ_JOINMARK means we have just found a new - * outer tuple and a possible matching inner tuple. - * This is the case after the INITIALIZE, SKIPOUTER - * or SKIPINNER states. - * ******************************** - */ - case EXEC_MJ_JOINMARK: - MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n"); - ExecMarkPos(innerPlan); - - innerTupleSlot = econtext->ecxt_innertuple; - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; - break; - - /* ******************************** - * EXEC_MJ_JOINTEST means we have two tuples which - * might satisify the merge clause, so we test them. - * - * If they do satisify, then we join them and move - * on to the next inner tuple (EXEC_MJ_JOINTUPLES). - * - * If they do not satisify then advance to next outer tuple. - * ******************************** - */ - case EXEC_MJ_JOINTEST: - MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n"); - - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) - { - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; - } - break; - - /* ******************************** - * EXEC_MJ_JOINTUPLES means we have two tuples which - * satisified the merge clause so we join them and then - * proceed to get the next inner tuple (EXEC_NEXT_INNER). - * ******************************** + mergestate = node->mergestate; + estate = node->join.state; + direction = estate->es_direction; + innerPlan = innerPlan((Plan *) node); + outerPlan = outerPlan((Plan *) node); + econtext = mergestate->jstate.cs_ExprContext; + mergeclauses = node->mergeclauses; + qual = node->join.qual; + + if (ScanDirectionIsForward(direction)) + { + outerSkipQual = mergestate->mj_OSortopI; + innerSkipQual = mergestate->mj_ISortopO; + } + else + { + outerSkipQual = mergestate->mj_ISortopO; + innerSkipQual = mergestate->mj_OSortopI; + } + + /* ---------------- + * ok, everything is setup.. let's go to work + * ---------------- */ - case EXEC_MJ_JOINTUPLES: - MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n"); - mergestate->mj_JoinState = EXEC_MJ_NEXTINNER; - - qualResult = ExecQual((List*)qual, econtext); - MJ_DEBUG_QUAL(qual, qualResult); - - if (qualResult) { - /* ---------------- - * qualification succeeded. now form the desired - * projection tuple and return the slot containing it. - * ---------------- - */ - ProjectionInfo *projInfo; + if (mergestate->jstate.cs_TupFromTlist) + { TupleTableSlot *result; - bool isDone; - - MJ_printf("ExecMergeJoin: **** returning tuple ****\n"); - + ProjectionInfo *projInfo; + bool isDone; + projInfo = mergestate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - mergestate->jstate.cs_TupFromTlist = !isDone; - return result; - } - break; - - /* ******************************** - * EXEC_MJ_NEXTINNER means advance the inner scan - * to the next tuple. If the tuple is not nil, we then - * proceed to test it against the join qualification. - * ******************************** - */ - case EXEC_MJ_NEXTINNER: - MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n"); - - /* ---------------- - * now we get the next inner tuple, if any - * ---------------- - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(innerTupleSlot); - econtext->ecxt_innertuple = innerTupleSlot; - - if (TupIsNull(innerTupleSlot)) - { - mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; - } - break; - - /* ******************************** - * EXEC_MJ_NEXTOUTER means - * - * outer inner - * outer tuple - 5 5 - marked tuple - * 5 5 - * 6 6 - inner tuple - * 7 7 - * - * we know we just bumped into - * the first inner tuple > current outer tuple - * so get a new outer tuple and then proceed to test - * it against the marked tuple (EXEC_MJ_TESTOUTER) - * ******************************** - */ - case EXEC_MJ_NEXTOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n"); - - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(outerTupleSlot); - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * if the outer tuple is null then we know - * we are done with the join - * ---------------- - */ - if (TupIsNull(outerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); - CleanUpSort(node->join.lefttree->lefttree); - CleanUpSort(node->join.righttree->lefttree); - return NULL; - } - - mergestate->mj_JoinState = EXEC_MJ_TESTOUTER; - break; - - /* ******************************** - * EXEC_MJ_TESTOUTER - * If the new outer tuple and the marked tuple satisify - * the merge clause then we know we have duplicates in - * the outer scan so we have to restore the inner scan - * to the marked tuple and proceed to join the new outer - * tuples with the inner tuples (EXEC_MJ_JOINTEST) - * - * This is the case when - * - * outer inner - * 4 5 - marked tuple - * outer tuple - 5 5 - * new outer tuple - 5 5 - * 6 8 - inner tuple - * 7 12 - * - * new outer tuple = marked tuple - * - * If the outer tuple fails the test, then we know we have - * to proceed to skip outer tuples until outer >= inner - * (EXEC_MJ_SKIPOUTER). - * - * This is the case when - * - * outer inner - * 5 5 - marked tuple - * outer tuple - 5 5 - * new outer tuple - 6 8 - inner tuple - * 7 12 - * - * new outer tuple > marked tuple - * - * ******************************** - */ - case EXEC_MJ_TESTOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n"); - - /* ---------------- - * here we compare the outer tuple with the marked inner tuple - * by using the marked tuple in place of the inner tuple. - * ---------------- - */ - innerTupleSlot = econtext->ecxt_innertuple; - markedTupleSlot = mergestate->mj_MarkedTupleSlot; - econtext->ecxt_innertuple = markedTupleSlot; - - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) { - /* ---------------- - * the merge clause matched so now we juggle the slots - * back the way they were and proceed to JOINTEST. - * ---------------- - */ - econtext->ecxt_innertuple = innerTupleSlot; - - RestoreInnerTuple(innerTupleSlot, markedTupleSlot); - - ExecRestrPos(innerPlan); - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; - - } else { - /* ---------------- - * if the inner tuple was nil and the new outer - * tuple didn't match the marked outer tuple then - * we may have the case: - * - * outer inner - * 4 4 - marked tuple - * new outer - 5 4 - * 6 nil - inner tuple - * 7 - * - * which means that all subsequent outer tuples will be - * larger than our inner tuples. - * ---------------- - */ - if (TupIsNull(innerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n"); - return NULL; - } - - /* ---------------- - * restore the inner tuple and continue on to - * skip outer tuples. - * ---------------- - */ - econtext->ecxt_innertuple = innerTupleSlot; - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; - } - break; - - /* ******************************** - * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan - * until we find an outer tuple > current inner tuple. - * - * For example: - * - * outer inner - * 5 5 - * 5 5 - * outer tuple - 6 8 - inner tuple - * 7 12 - * 8 14 - * - * we have to advance the outer scan - * until we find the outer 8. - * - * ******************************** - */ - case EXEC_MJ_SKIPOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n"); - /* ---------------- - * before we advance, make sure the current tuples - * do not satisify the mergeclauses. If they do, then - * we update the marked tuple and go join them. - * ---------------- - */ - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) { - ExecMarkPos(innerPlan); - innerTupleSlot = econtext->ecxt_innertuple; - - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - break; - } - - /* ---------------- - * ok, now test the skip qualification - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - outerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); - - /* ---------------- - * compareResult is true as long as we should - * continue skipping tuples. - * ---------------- - */ - if (compareResult) { - - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(outerTupleSlot); - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * if the outer tuple is null then we know - * we are done with the join - * ---------------- - */ - if (TupIsNull(outerTupleSlot)) { - MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n"); - return NULL; - } - /* ---------------- - * otherwise test the new tuple against the skip qual. - * (we remain in the EXEC_MJ_SKIPOUTER state) - * ---------------- - */ - break; - } - - /* ---------------- - * now check the inner skip qual to see if we - * should now skip inner tuples... if we fail the - * inner skip qual, then we know we have a new pair - * of matching tuples. - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - innerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); - - if (compareResult) - { - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_JOINMARK; - } - break; - - /* ******************************** - * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan - * until we find an inner tuple > current outer tuple. - * - * For example: - * - * outer inner - * 5 5 - * 5 5 - * outer tuple - 12 8 - inner tuple - * 14 10 - * 17 12 - * - * we have to advance the inner scan - * until we find the inner 12. - * - * ******************************** - */ - case EXEC_MJ_SKIPINNER: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n"); - /* ---------------- - * before we advance, make sure the current tuples - * do not satisify the mergeclauses. If they do, then - * we update the marked tuple and go join them. - * ---------------- - */ - qualResult = ExecQual((List*)mergeclauses, econtext); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) { - ExecMarkPos(innerPlan); - innerTupleSlot = econtext->ecxt_innertuple; - - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - break; - } - - /* ---------------- - * ok, now test the skip qualification - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - innerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); - - /* ---------------- - * compareResult is true as long as we should - * continue skipping tuples. - * ---------------- - */ - if (compareResult) { - /* ---------------- - * now try and get a new inner tuple - * ---------------- - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - MJ_DEBUG_PROC_NODE(innerTupleSlot); - econtext->ecxt_innertuple = innerTupleSlot; - + if (!isDone) + return result; + } + for (;;) + { /* ---------------- - * if the inner tuple is null then we know - * we have to restore the inner scan - * and advance to the next outer tuple + * get the current state of the join and do things accordingly. + * Note: The join states are highlighted with 32-* comments for + * improved readability. * ---------------- */ - if (TupIsNull(innerTupleSlot)) { - /* ---------------- - * this is an interesting case.. all our - * inner tuples are smaller then our outer - * tuples so we never found an inner tuple - * to mark. - * - * outer inner - * outer tuple - 5 4 - * 5 4 - * 6 nil - inner tuple - * 7 - * - * This means the join should end. - * ---------------- - */ - MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n"); - return NULL; + MJ_dump(econtext, mergestate); + + switch (mergestate->mj_JoinState) + { + + /* + * ******************************** EXEC_MJ_INITIALIZE means + * that this is the first time ExecMergeJoin() has been called + * and so we have to initialize the inner, outer and marked + * tuples as well as various stuff in the expression context. ******************************** + * + */ + case EXEC_MJ_INITIALIZE: + MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n"); + /* ---------------- + * Note: at this point, if either of our inner or outer + * tuples are nil, then the join ends immediately because + * we know one of the subplans is empty. + * ---------------- + */ + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + if (TupIsNull(innerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n"); + return NULL; + } + + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + if (TupIsNull(outerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); + return NULL; + } + + /* ---------------- + * store the inner and outer tuple in the merge state + * ---------------- + */ + econtext->ecxt_innertuple = innerTupleSlot; + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * set the marked tuple to nil + * and initialize its tuple descriptor atttributes. + * -jeff 10 july 1991 + * ---------------- + */ + ExecClearTuple(mergestate->mj_MarkedTupleSlot); + mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor = + innerTupleSlot->ttc_tupleDescriptor; +/* + mergestate->mj_MarkedTupleSlot->ttc_execTupDescriptor = + innerTupleSlot->ttc_execTupDescriptor; +*/ + /* ---------------- + * initialize merge join state to skip inner tuples. + * ---------------- + */ + mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; + break; + + /* + * ******************************** EXEC_MJ_JOINMARK means we + * have just found a new outer tuple and a possible matching + * inner tuple. This is the case after the INITIALIZE, + * SKIPOUTER or SKIPINNER states. ******************************** + * + */ + case EXEC_MJ_JOINMARK: + MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n"); + ExecMarkPos(innerPlan); + + innerTupleSlot = econtext->ecxt_innertuple; + MarkInnerTuple(innerTupleSlot, mergestate); + + mergestate->mj_JoinState = EXEC_MJ_JOINTEST; + break; + + /* + * ******************************** EXEC_MJ_JOINTEST means we + * have two tuples which might satisify the merge clause, so + * we test them. + * + * If they do satisify, then we join them and move on to the next + * inner tuple (EXEC_MJ_JOINTUPLES). + * + * If they do not satisify then advance to next outer tuple. ******************************** + * + */ + case EXEC_MJ_JOINTEST: + MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n"); + + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; + } + break; + + /* + * ******************************** EXEC_MJ_JOINTUPLES means + * we have two tuples which satisified the merge clause so we + * join them and then proceed to get the next inner tuple + * (EXEC_NEXT_INNER). ******************************** + * + */ + case EXEC_MJ_JOINTUPLES: + MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n"); + mergestate->mj_JoinState = EXEC_MJ_NEXTINNER; + + qualResult = ExecQual((List *) qual, econtext); + MJ_DEBUG_QUAL(qual, qualResult); + + if (qualResult) + { + /* ---------------- + * qualification succeeded. now form the desired + * projection tuple and return the slot containing it. + * ---------------- + */ + ProjectionInfo *projInfo; + TupleTableSlot *result; + bool isDone; + + MJ_printf("ExecMergeJoin: **** returning tuple ****\n"); + + projInfo = mergestate->jstate.cs_ProjInfo; + + result = ExecProject(projInfo, &isDone); + mergestate->jstate.cs_TupFromTlist = !isDone; + return result; + } + break; + + /* + * ******************************** EXEC_MJ_NEXTINNER means + * advance the inner scan to the next tuple. If the tuple is + * not nil, we then proceed to test it against the join + * qualification. ******************************** + * + */ + case EXEC_MJ_NEXTINNER: + MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n"); + + /* ---------------- + * now we get the next inner tuple, if any + * ---------------- + */ + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(innerTupleSlot); + econtext->ecxt_innertuple = innerTupleSlot; + + if (TupIsNull(innerTupleSlot)) + { + mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_JOINTEST; + } + break; + + /* + * ******************************** EXEC_MJ_NEXTOUTER means + * + * outer inner outer tuple - 5 5 - marked tuple 5 5 6 + * 6 - inner tuple 7 7 + * + * we know we just bumped into the first inner tuple > current + * outer tuple so get a new outer tuple and then proceed to + * test it against the marked tuple (EXEC_MJ_TESTOUTER) ******************************** + * + */ + case EXEC_MJ_NEXTOUTER: + MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n"); + + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(outerTupleSlot); + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * if the outer tuple is null then we know + * we are done with the join + * ---------------- + */ + if (TupIsNull(outerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n"); + CleanUpSort(node->join.lefttree->lefttree); + CleanUpSort(node->join.righttree->lefttree); + return NULL; + } + + mergestate->mj_JoinState = EXEC_MJ_TESTOUTER; + break; + + /* + * ******************************** EXEC_MJ_TESTOUTER If the + * new outer tuple and the marked tuple satisify the merge + * clause then we know we have duplicates in the outer scan so + * we have to restore the inner scan to the marked tuple and + * proceed to join the new outer tuples with the inner tuples + * (EXEC_MJ_JOINTEST) + * + * This is the case when + * + * outer inner 4 5 - marked tuple outer tuple - 5 5 new + * outer tuple - 5 5 6 8 - inner tuple 7 12 + * + * new outer tuple = marked tuple + * + * If the outer tuple fails the test, then we know we have to + * proceed to skip outer tuples until outer >= inner + * (EXEC_MJ_SKIPOUTER). + * + * This is the case when + * + * outer inner 5 5 - marked tuple outer tuple - 5 5 new + * outer tuple - 6 8 - inner tuple 7 12 + * + * new outer tuple > marked tuple + * + ******************************** + * + */ + case EXEC_MJ_TESTOUTER: + MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n"); + + /* ---------------- + * here we compare the outer tuple with the marked inner tuple + * by using the marked tuple in place of the inner tuple. + * ---------------- + */ + innerTupleSlot = econtext->ecxt_innertuple; + markedTupleSlot = mergestate->mj_MarkedTupleSlot; + econtext->ecxt_innertuple = markedTupleSlot; + + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + /* ---------------- + * the merge clause matched so now we juggle the slots + * back the way they were and proceed to JOINTEST. + * ---------------- + */ + econtext->ecxt_innertuple = innerTupleSlot; + + RestoreInnerTuple(innerTupleSlot, markedTupleSlot); + + ExecRestrPos(innerPlan); + mergestate->mj_JoinState = EXEC_MJ_JOINTEST; + + } + else + { + /* ---------------- + * if the inner tuple was nil and the new outer + * tuple didn't match the marked outer tuple then + * we may have the case: + * + * outer inner + * 4 4 - marked tuple + * new outer - 5 4 + * 6 nil - inner tuple + * 7 + * + * which means that all subsequent outer tuples will be + * larger than our inner tuples. + * ---------------- + */ + if (TupIsNull(innerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n"); + return NULL; + } + + /* ---------------- + * restore the inner tuple and continue on to + * skip outer tuples. + * ---------------- + */ + econtext->ecxt_innertuple = innerTupleSlot; + mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; + } + break; + + /* + * ******************************** EXEC_MJ_SKIPOUTER means + * skip over tuples in the outer plan until we find an outer + * tuple > current inner tuple. + * + * For example: + * + * outer inner 5 5 5 5 outer tuple - 6 8 - inner + * tuple 7 12 8 14 + * + * we have to advance the outer scan until we find the outer 8. + * + ******************************** + * + */ + case EXEC_MJ_SKIPOUTER: + MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n"); + /* ---------------- + * before we advance, make sure the current tuples + * do not satisify the mergeclauses. If they do, then + * we update the marked tuple and go join them. + * ---------------- + */ + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + ExecMarkPos(innerPlan); + innerTupleSlot = econtext->ecxt_innertuple; + + MarkInnerTuple(innerTupleSlot, mergestate); + + mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; + break; + } + + /* ---------------- + * ok, now test the skip qualification + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + outerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); + + /* ---------------- + * compareResult is true as long as we should + * continue skipping tuples. + * ---------------- + */ + if (compareResult) + { + + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(outerTupleSlot); + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * if the outer tuple is null then we know + * we are done with the join + * ---------------- + */ + if (TupIsNull(outerTupleSlot)) + { + MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n"); + return NULL; + } + /* ---------------- + * otherwise test the new tuple against the skip qual. + * (we remain in the EXEC_MJ_SKIPOUTER state) + * ---------------- + */ + break; + } + + /* ---------------- + * now check the inner skip qual to see if we + * should now skip inner tuples... if we fail the + * inner skip qual, then we know we have a new pair + * of matching tuples. + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + innerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); + + if (compareResult) + { + mergestate->mj_JoinState = EXEC_MJ_SKIPINNER; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_JOINMARK; + } + break; + + /* + * ******************************** EXEC_MJ_SKIPINNER means + * skip over tuples in the inner plan until we find an inner + * tuple > current outer tuple. + * + * For example: + * + * outer inner 5 5 5 5 outer tuple - 12 8 - inner + * tuple 14 10 17 12 + * + * we have to advance the inner scan until we find the inner 12. + * + ******************************** + * + */ + case EXEC_MJ_SKIPINNER: + MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n"); + /* ---------------- + * before we advance, make sure the current tuples + * do not satisify the mergeclauses. If they do, then + * we update the marked tuple and go join them. + * ---------------- + */ + qualResult = ExecQual((List *) mergeclauses, econtext); + MJ_DEBUG_QUAL(mergeclauses, qualResult); + + if (qualResult) + { + ExecMarkPos(innerPlan); + innerTupleSlot = econtext->ecxt_innertuple; + + MarkInnerTuple(innerTupleSlot, mergestate); + + mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; + break; + } + + /* ---------------- + * ok, now test the skip qualification + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + innerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); + + /* ---------------- + * compareResult is true as long as we should + * continue skipping tuples. + * ---------------- + */ + if (compareResult) + { + /* ---------------- + * now try and get a new inner tuple + * ---------------- + */ + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + MJ_DEBUG_PROC_NODE(innerTupleSlot); + econtext->ecxt_innertuple = innerTupleSlot; + + /* ---------------- + * if the inner tuple is null then we know + * we have to restore the inner scan + * and advance to the next outer tuple + * ---------------- + */ + if (TupIsNull(innerTupleSlot)) + { + /* ---------------- + * this is an interesting case.. all our + * inner tuples are smaller then our outer + * tuples so we never found an inner tuple + * to mark. + * + * outer inner + * outer tuple - 5 4 + * 5 4 + * 6 nil - inner tuple + * 7 + * + * This means the join should end. + * ---------------- + */ + MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n"); + return NULL; + } + + /* ---------------- + * otherwise test the new tuple against the skip qual. + * (we remain in the EXEC_MJ_SKIPINNER state) + * ---------------- + */ + break; + } + + /* ---------------- + * compare finally failed and we have stopped skipping + * inner tuples so now check the outer skip qual + * to see if we should now skip outer tuples... + * ---------------- + */ + compareResult = MergeCompare(mergeclauses, + outerSkipQual, + econtext); + + MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); + + if (compareResult) + { + mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; + } + else + { + mergestate->mj_JoinState = EXEC_MJ_JOINMARK; + } + + break; + + /* + * ******************************** if we get here it means + * our code is fucked up and so we just end the join + * prematurely. ******************************** + * + */ + default: + elog(NOTICE, "ExecMergeJoin: invalid join state. aborting"); + return NULL; } - - /* ---------------- - * otherwise test the new tuple against the skip qual. - * (we remain in the EXEC_MJ_SKIPINNER state) - * ---------------- - */ - break; - } - - /* ---------------- - * compare finally failed and we have stopped skipping - * inner tuples so now check the outer skip qual - * to see if we should now skip outer tuples... - * ---------------- - */ - compareResult = MergeCompare(mergeclauses, - outerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); - - if (compareResult) - { - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER; - } - else - { - mergestate->mj_JoinState = EXEC_MJ_JOINMARK; - } - - break; - - /* ******************************** - * if we get here it means our code is fucked up and - * so we just end the join prematurely. - * ******************************** - */ - default: - elog(NOTICE, "ExecMergeJoin: invalid join state. aborting"); - return NULL; } - } } - + /* ---------------------------------------------------------------- - * ExecInitMergeJoin + * ExecInitMergeJoin * * old comments - * Creates the run-time state information for the node and - * sets the relation id to contain relevant decriptors. + * Creates the run-time state information for the node and + * sets the relation id to contain relevant decriptors. * ---------------------------------------------------------------- */ bool -ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) +ExecInitMergeJoin(MergeJoin * node, EState * estate, Plan * parent) { - MergeJoinState *mergestate; - List *joinclauses; - RegProcedure rightsortop; - RegProcedure leftsortop; - RegProcedure sortop; - - List *OSortopI; - List *ISortopO; - - MJ1_printf("ExecInitMergeJoin: %s\n", - "initializing node"); - - /* ---------------- - * assign the node's execution state and - * get the range table and direction from it - * ---------------- - */ - node->join.state = estate; - - /* ---------------- - * create new merge state for node - * ---------------- - */ - mergestate = makeNode(MergeJoinState); - mergestate->mj_OSortopI = NIL; - mergestate->mj_ISortopO = NIL; - mergestate->mj_JoinState = 0; - mergestate->mj_MarkedTupleSlot = NULL; - node->mergestate = mergestate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent); - ExecAssignExprContext(estate, &mergestate->jstate); + MergeJoinState *mergestate; + List *joinclauses; + RegProcedure rightsortop; + RegProcedure leftsortop; + RegProcedure sortop; + + List *OSortopI; + List *ISortopO; + + MJ1_printf("ExecInitMergeJoin: %s\n", + "initializing node"); + + /* ---------------- + * assign the node's execution state and + * get the range table and direction from it + * ---------------- + */ + node->join.state = estate; + + /* ---------------- + * create new merge state for node + * ---------------- + */ + mergestate = makeNode(MergeJoinState); + mergestate->mj_OSortopI = NIL; + mergestate->mj_ISortopO = NIL; + mergestate->mj_JoinState = 0; + mergestate->mj_MarkedTupleSlot = NULL; + node->mergestate = mergestate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent); + ExecAssignExprContext(estate, &mergestate->jstate); #define MERGEJOIN_NSLOTS 2 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &mergestate->jstate); - ExecInitMarkedTupleSlot(estate, mergestate); - - /* ---------------- - * get merge sort operators. - * - * XXX for now we assume all quals in the joinclauses were - * sorted with the same operator in both the inner and - * outer relations. -cim 11/2/89 - * ---------------- - */ - joinclauses = node->mergeclauses; - - rightsortop = get_opcode(node->mergerightorder[0]); - leftsortop = get_opcode(node->mergeleftorder[0]); - - if (leftsortop != rightsortop) - elog(NOTICE, "ExecInitMergeJoin: %s", - "left and right sortop's are unequal!"); - - sortop = rightsortop; - - /* ---------------- - * form merge skip qualifications - * - * XXX MJform routines need to be extended - * to take a list of sortops.. -cim 11/2/89 - * ---------------- - */ - OSortopI = MJFormOSortopI(joinclauses, sortop); - ISortopO = MJFormISortopO(joinclauses, sortop); - mergestate->mj_OSortopI = OSortopI; - mergestate->mj_ISortopO = ISortopO; - - MJ_printf("\nExecInitMergeJoin: OSortopI is "); - MJ_nodeDisplay(OSortopI); - MJ_printf("\nExecInitMergeJoin: ISortopO is "); - MJ_nodeDisplay(ISortopO); - MJ_printf("\n"); - - /* ---------------- - * initialize join state - * ---------------- - */ - mergestate->mj_JoinState = EXEC_MJ_INITIALIZE; - - /* ---------------- - * initialize subplans - * ---------------- - */ - ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); - ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate); - ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate); - - mergestate->jstate.cs_TupFromTlist = false; - /* ---------------- - * initialization successful - * ---------------- - */ - MJ1_printf("ExecInitMergeJoin: %s\n", - "node initialized"); - - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &mergestate->jstate); + ExecInitMarkedTupleSlot(estate, mergestate); + + /* ---------------- + * get merge sort operators. + * + * XXX for now we assume all quals in the joinclauses were + * sorted with the same operator in both the inner and + * outer relations. -cim 11/2/89 + * ---------------- + */ + joinclauses = node->mergeclauses; + + rightsortop = get_opcode(node->mergerightorder[0]); + leftsortop = get_opcode(node->mergeleftorder[0]); + + if (leftsortop != rightsortop) + elog(NOTICE, "ExecInitMergeJoin: %s", + "left and right sortop's are unequal!"); + + sortop = rightsortop; + + /* ---------------- + * form merge skip qualifications + * + * XXX MJform routines need to be extended + * to take a list of sortops.. -cim 11/2/89 + * ---------------- + */ + OSortopI = MJFormOSortopI(joinclauses, sortop); + ISortopO = MJFormISortopO(joinclauses, sortop); + mergestate->mj_OSortopI = OSortopI; + mergestate->mj_ISortopO = ISortopO; + + MJ_printf("\nExecInitMergeJoin: OSortopI is "); + MJ_nodeDisplay(OSortopI); + MJ_printf("\nExecInitMergeJoin: ISortopO is "); + MJ_nodeDisplay(ISortopO); + MJ_printf("\n"); + + /* ---------------- + * initialize join state + * ---------------- + */ + mergestate->mj_JoinState = EXEC_MJ_INITIALIZE; + + /* ---------------- + * initialize subplans + * ---------------- + */ + ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); + ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate); + ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate); + + mergestate->jstate.cs_TupFromTlist = false; + /* ---------------- + * initialization successful + * ---------------- + */ + MJ1_printf("ExecInitMergeJoin: %s\n", + "node initialized"); + + return TRUE; } - + int -ExecCountSlotsMergeJoin(MergeJoin *node) +ExecCountSlotsMergeJoin(MergeJoin * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - MERGEJOIN_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + MERGEJOIN_NSLOTS; } - + /* ---------------------------------------------------------------- - * ExecEndMergeJoin + * ExecEndMergeJoin * * old comments - * frees storage allocated through C routines. + * frees storage allocated through C routines. * ---------------------------------------------------------------- */ void -ExecEndMergeJoin(MergeJoin *node) +ExecEndMergeJoin(MergeJoin * node) { - MergeJoinState *mergestate; - - MJ1_printf("ExecEndMergeJoin: %s\n", - "ending node processing"); - - /* ---------------- - * get state information from the node - * ---------------- - */ - mergestate = node->mergestate; - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(mergestate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&mergestate->jstate); - - /* ---------------- - * shut down the subplans - * ---------------- - */ - ExecEndNode((Plan*) innerPlan((Plan *) node), (Plan*)node); - ExecEndNode((Plan*) outerPlan((Plan *) node), (Plan*)node); - - /* ---------------- - * clean out the tuple table so that we don't try and - * pfree the marked tuples.. see HACK ALERT at the top of - * this file. - * ---------------- - */ - ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot); - ExecClearTuple(mergestate->mj_MarkedTupleSlot); - - MJ1_printf("ExecEndMergeJoin: %s\n", - "node processing ended"); + MergeJoinState *mergestate; + + MJ1_printf("ExecEndMergeJoin: %s\n", + "ending node processing"); + + /* ---------------- + * get state information from the node + * ---------------- + */ + mergestate = node->mergestate; + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(mergestate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&mergestate->jstate); + + /* ---------------- + * shut down the subplans + * ---------------- + */ + ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node); + ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node); + + /* ---------------- + * clean out the tuple table so that we don't try and + * pfree the marked tuples.. see HACK ALERT at the top of + * this file. + * ---------------- + */ + ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot); + ExecClearTuple(mergestate->mj_MarkedTupleSlot); + + MJ1_printf("ExecEndMergeJoin: %s\n", + "node processing ended"); } - diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index d83d306bba9..e7cba2e756e 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * nodeNestloop.c-- - * routines to support nest-loop joins + * routines to support nest-loop joins * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.3 1996/11/08 05:56:15 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.4 1997/09/07 04:41:41 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES - * ExecNestLoop - process a nestloop join of two plans - * ExecInitNestLoop - initialize the join - * ExecEndNestLoop - shut down the join + * INTERFACE ROUTINES + * ExecNestLoop - process a nestloop join of two plans + * ExecInitNestLoop - initialize the join + * ExecEndNestLoop - shut down the join */ #include "postgres.h" @@ -25,349 +25,363 @@ #include "executor/nodeIndexscan.h" /* ---------------------------------------------------------------- - * ExecNestLoop(node) + * ExecNestLoop(node) * * old comments - * Returns the tuple joined from inner and outer tuples which - * satisfies the qualification clause. + * Returns the tuple joined from inner and outer tuples which + * satisfies the qualification clause. * - * It scans the inner relation to join with current outer tuple. + * It scans the inner relation to join with current outer tuple. * - * If none is found, next tuple form the outer relation is retrieved - * and the inner relation is scanned from the beginning again to join - * with the outer tuple. + * If none is found, next tuple form the outer relation is retrieved + * and the inner relation is scanned from the beginning again to join + * with the outer tuple. * - * Nil is returned if all the remaining outer tuples are tried and - * all fail to join with the inner tuples. + * Nil is returned if all the remaining outer tuples are tried and + * all fail to join with the inner tuples. * - * Nil is also returned if there is no tuple from inner realtion. - * - * Conditions: - * -- outerTuple contains current tuple from outer relation and - * the right son(inner realtion) maintains "cursor" at the tuple - * returned previously. - * This is achieved by maintaining a scan position on the outer - * relation. - * - * Initial States: - * -- the outer child and the inner child - * are prepared to return the first tuple. + * Nil is also returned if there is no tuple from inner realtion. + * + * Conditions: + * -- outerTuple contains current tuple from outer relation and + * the right son(inner realtion) maintains "cursor" at the tuple + * returned previously. + * This is achieved by maintaining a scan position on the outer + * relation. + * + * Initial States: + * -- the outer child and the inner child + * are prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecNestLoop(NestLoop *node, Plan* parent) +ExecNestLoop(NestLoop * node, Plan * parent) { - NestLoopState *nlstate; - Plan *innerPlan; - Plan *outerPlan; - bool needNewOuterTuple; - - TupleTableSlot *outerTupleSlot; - TupleTableSlot *innerTupleSlot; - - List *qual; - bool qualResult; - ExprContext *econtext; - - /* ---------------- - * get information from the node - * ---------------- - */ - ENL1_printf("getting info from node"); - - nlstate = node->nlstate; - qual = node->join.qual; - outerPlan = outerPlan(&node->join); - innerPlan = innerPlan(&node->join); - - /* ---------------- - * initialize expression context - * ---------------- - */ - econtext = nlstate->jstate.cs_ExprContext; - - /* ---------------- * get the current outer tuple - * ---------------- - */ - outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * Ok, everything is setup for the join so now loop until - * we return a qualifying join tuple.. - * ---------------- - */ - - if (nlstate->jstate.cs_TupFromTlist) { - TupleTableSlot *result; - bool isDone; - - result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); - if (!isDone) - return result; - } - - ENL1_printf("entering main loop"); - for(;;) { + NestLoopState *nlstate; + Plan *innerPlan; + Plan *outerPlan; + bool needNewOuterTuple; + + TupleTableSlot *outerTupleSlot; + TupleTableSlot *innerTupleSlot; + + List *qual; + bool qualResult; + ExprContext *econtext; + /* ---------------- - * The essential idea now is to get the next inner tuple - * and join it with the current outer tuple. + * get information from the node * ---------------- */ - needNewOuterTuple = false; - + ENL1_printf("getting info from node"); + + nlstate = node->nlstate; + qual = node->join.qual; + outerPlan = outerPlan(&node->join); + innerPlan = innerPlan(&node->join); + /* ---------------- - * If outer tuple is not null then that means - * we are in the middle of a scan and we should - * restore our previously saved scan position. + * initialize expression context * ---------------- */ - if (! TupIsNull(outerTupleSlot)) { - ENL1_printf("have outer tuple, restoring outer plan"); - ExecRestrPos(outerPlan); - } else { - ENL1_printf("outer tuple is nil, need new outer tuple"); - needNewOuterTuple = true; - } - - /* ---------------- - * if we have an outerTuple, try to get the next inner tuple. + econtext = nlstate->jstate.cs_ExprContext; + + /* ---------------- * get the current outer tuple * ---------------- */ - if (!needNewOuterTuple) { - ENL1_printf("getting new inner tuple"); - - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - econtext->ecxt_innertuple = innerTupleSlot; - - if (TupIsNull(innerTupleSlot)) { - ENL1_printf("no inner tuple, need new outer tuple"); - needNewOuterTuple = true; - } - } - + outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; + econtext->ecxt_outertuple = outerTupleSlot; + /* ---------------- - * loop until we have a new outer tuple and a new - * inner tuple. + * Ok, everything is setup for the join so now loop until + * we return a qualifying join tuple.. * ---------------- */ - while (needNewOuterTuple) { - /* ---------------- - * now try to get the next outer tuple - * ---------------- - */ - ENL1_printf("getting new outer tuple"); - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - econtext->ecxt_outertuple = outerTupleSlot; - - /* ---------------- - * if there are no more outer tuples, then the join - * is complete.. - * ---------------- - */ - if (TupIsNull(outerTupleSlot)) { - ENL1_printf("no outer tuple, ending join"); - return NULL; - } - - /* ---------------- - * we have a new outer tuple so we mark our position - * in the outer scan and save the outer tuple in the - * NestLoop state - * ---------------- - */ - ENL1_printf("saving new outer tuple information"); - ExecMarkPos(outerPlan); - nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; - - /* ---------------- - * now rescan the inner plan and get a new inner tuple - * ---------------- - */ - - ENL1_printf("rescanning inner plan"); - /* - * The scan key of the inner plan might depend on the current - * outer tuple (e.g. in index scans), that's why we pass our - * expr context. - */ - ExecReScan(innerPlan, econtext, parent); - - ENL1_printf("getting new inner tuple"); - - innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); - econtext->ecxt_innertuple = innerTupleSlot; - - if (TupIsNull(innerTupleSlot)) { - ENL1_printf("couldn't get inner tuple - need new outer tuple"); - } else { - ENL1_printf("got inner and outer tuples"); + + if (nlstate->jstate.cs_TupFromTlist) + { + TupleTableSlot *result; + bool isDone; + + result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); + if (!isDone) + return result; + } + + ENL1_printf("entering main loop"); + for (;;) + { + /* ---------------- + * The essential idea now is to get the next inner tuple + * and join it with the current outer tuple. + * ---------------- + */ needNewOuterTuple = false; - } - } /* while (needNewOuterTuple) */ - + + /* ---------------- + * If outer tuple is not null then that means + * we are in the middle of a scan and we should + * restore our previously saved scan position. + * ---------------- + */ + if (!TupIsNull(outerTupleSlot)) + { + ENL1_printf("have outer tuple, restoring outer plan"); + ExecRestrPos(outerPlan); + } + else + { + ENL1_printf("outer tuple is nil, need new outer tuple"); + needNewOuterTuple = true; + } + + /* ---------------- + * if we have an outerTuple, try to get the next inner tuple. + * ---------------- + */ + if (!needNewOuterTuple) + { + ENL1_printf("getting new inner tuple"); + + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + econtext->ecxt_innertuple = innerTupleSlot; + + if (TupIsNull(innerTupleSlot)) + { + ENL1_printf("no inner tuple, need new outer tuple"); + needNewOuterTuple = true; + } + } + + /* ---------------- + * loop until we have a new outer tuple and a new + * inner tuple. + * ---------------- + */ + while (needNewOuterTuple) + { + /* ---------------- + * now try to get the next outer tuple + * ---------------- + */ + ENL1_printf("getting new outer tuple"); + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + econtext->ecxt_outertuple = outerTupleSlot; + + /* ---------------- + * if there are no more outer tuples, then the join + * is complete.. + * ---------------- + */ + if (TupIsNull(outerTupleSlot)) + { + ENL1_printf("no outer tuple, ending join"); + return NULL; + } + + /* ---------------- + * we have a new outer tuple so we mark our position + * in the outer scan and save the outer tuple in the + * NestLoop state + * ---------------- + */ + ENL1_printf("saving new outer tuple information"); + ExecMarkPos(outerPlan); + nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; + + /* ---------------- + * now rescan the inner plan and get a new inner tuple + * ---------------- + */ + + ENL1_printf("rescanning inner plan"); + + /* + * The scan key of the inner plan might depend on the current + * outer tuple (e.g. in index scans), that's why we pass our + * expr context. + */ + ExecReScan(innerPlan, econtext, parent); + + ENL1_printf("getting new inner tuple"); + + innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); + econtext->ecxt_innertuple = innerTupleSlot; + + if (TupIsNull(innerTupleSlot)) + { + ENL1_printf("couldn't get inner tuple - need new outer tuple"); + } + else + { + ENL1_printf("got inner and outer tuples"); + needNewOuterTuple = false; + } + } /* while (needNewOuterTuple) */ + + /* ---------------- + * at this point we have a new pair of inner and outer + * tuples so we test the inner and outer tuples to see + * if they satisify the node's qualification. + * ---------------- + */ + ENL1_printf("testing qualification"); + qualResult = ExecQual((List *) qual, econtext); + + if (qualResult) + { + /* ---------------- + * qualification was satisified so we project and + * return the slot containing the result tuple + * using ExecProject(). + * ---------------- + */ + ProjectionInfo *projInfo; + TupleTableSlot *result; + bool isDone; + + ENL1_printf("qualification succeeded, projecting tuple"); + + projInfo = nlstate->jstate.cs_ProjInfo; + result = ExecProject(projInfo, &isDone); + nlstate->jstate.cs_TupFromTlist = !isDone; + return result; + } + + /* ---------------- + * qualification failed so we have to try again.. + * ---------------- + */ + ENL1_printf("qualification failed, looping"); + } +} + +/* ---------------------------------------------------------------- + * ExecInitNestLoop + * + * Creates the run-time state information for the nestloop node + * produced by the planner and initailizes inner and outer relations + * (child nodes). + * ---------------------------------------------------------------- + */ +bool +ExecInitNestLoop(NestLoop * node, EState * estate, Plan * parent) +{ + NestLoopState *nlstate; + + NL1_printf("ExecInitNestLoop: %s\n", + "initializing node"); + /* ---------------- - * at this point we have a new pair of inner and outer - * tuples so we test the inner and outer tuples to see - * if they satisify the node's qualification. + * assign execution state to node * ---------------- */ - ENL1_printf("testing qualification"); - qualResult = ExecQual((List*)qual, econtext); - - if (qualResult) { - /* ---------------- - * qualification was satisified so we project and - * return the slot containing the result tuple - * using ExecProject(). - * ---------------- - */ - ProjectionInfo *projInfo; - TupleTableSlot *result; - bool isDone; - - ENL1_printf("qualification succeeded, projecting tuple"); - - projInfo = nlstate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - nlstate->jstate.cs_TupFromTlist = !isDone; - return result; - } - + node->join.state = estate; + /* ---------------- - * qualification failed so we have to try again.. + * create new nest loop state * ---------------- */ - ENL1_printf("qualification failed, looping"); - } -} + nlstate = makeNode(NestLoopState); + nlstate->nl_PortalFlag = false; + node->nlstate = nlstate; -/* ---------------------------------------------------------------- - * ExecInitNestLoop - * - * Creates the run-time state information for the nestloop node - * produced by the planner and initailizes inner and outer relations - * (child nodes). - * ---------------------------------------------------------------- - */ -bool -ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent) -{ - NestLoopState *nlstate; - - NL1_printf("ExecInitNestLoop: %s\n", - "initializing node"); - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->join.state = estate; - - /* ---------------- - * create new nest loop state - * ---------------- - */ - nlstate = makeNode(NestLoopState); - nlstate->nl_PortalFlag = false; - node->nlstate = nlstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent); - ExecAssignExprContext(estate, &nlstate->jstate); + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent); + ExecAssignExprContext(estate, &nlstate->jstate); #define NESTLOOP_NSLOTS 1 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &nlstate->jstate); - - /* ---------------- - * now initialize children - * ---------------- - */ - ExecInitNode(outerPlan((Plan*)node), estate, (Plan*)node); - ExecInitNode(innerPlan((Plan*)node), estate, (Plan*)node); - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate); - ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate); - - /* ---------------- - * finally, wipe the current outer tuple clean. - * ---------------- - */ - nlstate->jstate.cs_OuterTupleSlot = NULL; - nlstate->jstate.cs_TupFromTlist = false; - - NL1_printf("ExecInitNestLoop: %s\n", - "node initialized"); - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &nlstate->jstate); + + /* ---------------- + * now initialize children + * ---------------- + */ + ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); + ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate); + ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate); + + /* ---------------- + * finally, wipe the current outer tuple clean. + * ---------------- + */ + nlstate->jstate.cs_OuterTupleSlot = NULL; + nlstate->jstate.cs_TupFromTlist = false; + + NL1_printf("ExecInitNestLoop: %s\n", + "node initialized"); + return TRUE; } int -ExecCountSlotsNestLoop(NestLoop *node) +ExecCountSlotsNestLoop(NestLoop * node) { - return ExecCountSlotsNode(outerPlan(node)) + - ExecCountSlotsNode(innerPlan(node)) + - NESTLOOP_NSLOTS; + return ExecCountSlotsNode(outerPlan(node)) + + ExecCountSlotsNode(innerPlan(node)) + + NESTLOOP_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndNestLoop - * - * closes down scans and frees allocated storage + * ExecEndNestLoop + * + * closes down scans and frees allocated storage * ---------------------------------------------------------------- */ void -ExecEndNestLoop(NestLoop *node) +ExecEndNestLoop(NestLoop * node) { - NestLoopState *nlstate; - - NL1_printf("ExecEndNestLoop: %s\n", - "ending node processing"); - - /* ---------------- - * get info from the node - * ---------------- - */ - nlstate = node->nlstate; - - /* ---------------- - * Free the projection info - * - * Note: we don't ExecFreeResultType(nlstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&nlstate->jstate); - - /* ---------------- - * close down subplans - * ---------------- - */ - ExecEndNode(outerPlan((Plan *) node), (Plan*)node); - ExecEndNode(innerPlan((Plan *) node), (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot); - - NL1_printf("ExecEndNestLoop: %s\n", - "node processing ended"); + NestLoopState *nlstate; + + NL1_printf("ExecEndNestLoop: %s\n", + "ending node processing"); + + /* ---------------- + * get info from the node + * ---------------- + */ + nlstate = node->nlstate; + + /* ---------------- + * Free the projection info + * + * Note: we don't ExecFreeResultType(nlstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&nlstate->jstate); + + /* ---------------- + * close down subplans + * ---------------- + */ + ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + ExecEndNode(innerPlan((Plan *) node), (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot); + + NL1_printf("ExecEndNestLoop: %s\n", + "node processing ended"); } diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index f4553dcc7b7..743bd73f2b3 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -1,33 +1,33 @@ /*------------------------------------------------------------------------- * * nodeResult.c-- - * support for constant nodes needing special code. + * support for constant nodes needing special code. * * Copyright (c) 1994, Regents of the University of California * * - * DESCRIPTION + * DESCRIPTION * - * Example: in constant queries where no relations are scanned, - * the planner generates result nodes. Examples of such queries are: + * Example: in constant queries where no relations are scanned, + * the planner generates result nodes. Examples of such queries are: * - * retrieve (x = 1) - * and - * append emp (name = "mike", salary = 15000) + * retrieve (x = 1) + * and + * append emp (name = "mike", salary = 15000) * - * Result nodes are also used to optimise queries - * with tautological qualifications like: + * Result nodes are also used to optimise queries + * with tautological qualifications like: * - * retrieve (emp.all) where 2 > 1 + * retrieve (emp.all) where 2 > 1 * - * In this case, the plan generated is + * In this case, the plan generated is * - * Result (with 2 > 1 qual) - * / - * SeqScan (emp.all) + * Result (with 2 > 1 qual) + * / + * SeqScan (emp.all) * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.2 1996/10/31 10:12:18 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.3 1997/09/07 04:41:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -38,253 +38,259 @@ #include "executor/nodeResult.h" /* ---------------------------------------------------------------- - * ExecResult(node) + * ExecResult(node) * - * returns the tuples from the outer plan which satisify the - * qualification clause. Since result nodes with right - * subtrees are never planned, we ignore the right subtree - * entirely (for now).. -cim 10/7/89 + * returns the tuples from the outer plan which satisify the + * qualification clause. Since result nodes with right + * subtrees are never planned, we ignore the right subtree + * entirely (for now).. -cim 10/7/89 * - * The qualification containing only constant clauses are - * checked first before any processing is done. It always returns - * 'nil' if the constant qualification is not satisfied. + * The qualification containing only constant clauses are + * checked first before any processing is done. It always returns + * 'nil' if the constant qualification is not satisfied. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecResult(Result *node) +ExecResult(Result * node) { - ResultState *resstate; - TupleTableSlot *outerTupleSlot; - TupleTableSlot *resultSlot; - Plan *outerPlan; - ExprContext *econtext; - Node *qual; - bool qualResult; - bool isDone; - ProjectionInfo *projInfo; - - /* ---------------- - * initialize the result node's state - * ---------------- - */ - resstate = node->resstate; - - /* ---------------- - * get the expression context - * ---------------- - */ - econtext = resstate->cstate.cs_ExprContext; - - /* ---------------- - * check tautological qualifications like (2 > 1) - * ---------------- - */ - qual = node->resconstantqual; - if (qual != NULL) { - qualResult = ExecQual((List*)qual, econtext); - /* ---------------- - * if we failed the constant qual, then there - * is no need to continue processing because regardless of - * what happens, the constant qual will be false.. - * ---------------- - */ - if (qualResult == false) - return NULL; - - /* ---------------- - * our constant qualification succeeded so now we - * throw away the qual because we know it will always - * succeed. - * ---------------- - */ - node->resconstantqual = NULL; - } - - if (resstate->cstate.cs_TupFromTlist) { + ResultState *resstate; + TupleTableSlot *outerTupleSlot; + TupleTableSlot *resultSlot; + Plan *outerPlan; + ExprContext *econtext; + Node *qual; + bool qualResult; + bool isDone; ProjectionInfo *projInfo; - - projInfo = resstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - if (!isDone) - return resultSlot; - } - - /* ---------------- - * retrieve a tuple that satisfy the qual from the outer plan until - * there are no more. - * - * if rs_done is 1 then it means that we were asked to return - * a constant tuple and we alread did the last time ExecResult() - * was called, so now we are through. - * ---------------- - */ - outerPlan = outerPlan(node); - - while (!resstate->rs_done) { /* ---------------- - * get next outer tuple if necessary. + * initialize the result node's state * ---------------- */ - if (outerPlan != NULL) { - outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); - - if (TupIsNull(outerTupleSlot)) - return NULL; - - resstate->cstate.cs_OuterTupleSlot = outerTupleSlot; - } else { - - /* ---------------- - * if we don't have an outer plan, then it's probably - * the case that we are doing a retrieve or an append - * with a constant target list, so we should only return - * the constant tuple once or never if we fail the qual. - * ---------------- - */ - resstate->rs_done = 1; - } - + resstate = node->resstate; + /* ---------------- - * get the information to place into the expr context + * get the expression context * ---------------- */ - resstate = node->resstate; - - outerTupleSlot = resstate->cstate.cs_OuterTupleSlot; - + econtext = resstate->cstate.cs_ExprContext; + /* ---------------- - * fill in the information in the expression context - * XXX gross hack. use outer tuple as scan tuple + * check tautological qualifications like (2 > 1) * ---------------- */ - econtext->ecxt_outertuple = outerTupleSlot; - econtext->ecxt_scantuple = outerTupleSlot; - + qual = node->resconstantqual; + if (qual != NULL) + { + qualResult = ExecQual((List *) qual, econtext); + /* ---------------- + * if we failed the constant qual, then there + * is no need to continue processing because regardless of + * what happens, the constant qual will be false.. + * ---------------- + */ + if (qualResult == false) + return NULL; + + /* ---------------- + * our constant qualification succeeded so now we + * throw away the qual because we know it will always + * succeed. + * ---------------- + */ + node->resconstantqual = NULL; + } + + if (resstate->cstate.cs_TupFromTlist) + { + ProjectionInfo *projInfo; + + projInfo = resstate->cstate.cs_ProjInfo; + resultSlot = ExecProject(projInfo, &isDone); + if (!isDone) + return resultSlot; + } + /* ---------------- - * form the result tuple and pass it back using ExecProject() + * retrieve a tuple that satisfy the qual from the outer plan until + * there are no more. + * + * if rs_done is 1 then it means that we were asked to return + * a constant tuple and we alread did the last time ExecResult() + * was called, so now we are through. * ---------------- */ - projInfo = resstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - resstate->cstate.cs_TupFromTlist = !isDone; - return resultSlot; - } + outerPlan = outerPlan(node); + + while (!resstate->rs_done) + { + + /* ---------------- + * get next outer tuple if necessary. + * ---------------- + */ + if (outerPlan != NULL) + { + outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); + + if (TupIsNull(outerTupleSlot)) + return NULL; - return NULL; + resstate->cstate.cs_OuterTupleSlot = outerTupleSlot; + } + else + { + + /* ---------------- + * if we don't have an outer plan, then it's probably + * the case that we are doing a retrieve or an append + * with a constant target list, so we should only return + * the constant tuple once or never if we fail the qual. + * ---------------- + */ + resstate->rs_done = 1; + } + + /* ---------------- + * get the information to place into the expr context + * ---------------- + */ + resstate = node->resstate; + + outerTupleSlot = resstate->cstate.cs_OuterTupleSlot; + + /* ---------------- + * fill in the information in the expression context + * XXX gross hack. use outer tuple as scan tuple + * ---------------- + */ + econtext->ecxt_outertuple = outerTupleSlot; + econtext->ecxt_scantuple = outerTupleSlot; + + /* ---------------- + * form the result tuple and pass it back using ExecProject() + * ---------------- + */ + projInfo = resstate->cstate.cs_ProjInfo; + resultSlot = ExecProject(projInfo, &isDone); + resstate->cstate.cs_TupFromTlist = !isDone; + return resultSlot; + } + + return NULL; } /* ---------------------------------------------------------------- - * ExecInitResult - * - * Creates the run-time state information for the result node - * produced by the planner and initailizes outer relations - * (child nodes). + * ExecInitResult + * + * Creates the run-time state information for the result node + * produced by the planner and initailizes outer relations + * (child nodes). * ---------------------------------------------------------------- */ bool -ExecInitResult(Result *node, EState *estate, Plan *parent) +ExecInitResult(Result * node, EState * estate, Plan * parent) { - ResultState *resstate; - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create new ResultState for node - * ---------------- - */ - resstate = makeNode(ResultState); - resstate->rs_done = 0; - node->resstate = resstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent); - ExecAssignExprContext(estate, &resstate->cstate); - + ResultState *resstate; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create new ResultState for node + * ---------------- + */ + resstate = makeNode(ResultState); + resstate->rs_done = 0; + node->resstate = resstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent); + ExecAssignExprContext(estate, &resstate->cstate); + #define RESULT_NSLOTS 1 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &resstate->cstate); - - /* ---------------- - * then initialize children - * ---------------- - */ - ExecInitNode(outerPlan(node), estate, (Plan*)node); - - /* - * we don't use inner plan - */ - Assert(innerPlan(node)==NULL); - - /* ---------------- - * initialize tuple type and projection info - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan*)node, &resstate->cstate); - ExecAssignProjectionInfo((Plan*)node, &resstate->cstate); - - /* ---------------- - * set "are we done yet" to false - * ---------------- - */ - resstate->rs_done = 0; - - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &resstate->cstate); + + /* ---------------- + * then initialize children + * ---------------- + */ + ExecInitNode(outerPlan(node), estate, (Plan *) node); + + /* + * we don't use inner plan + */ + Assert(innerPlan(node) == NULL); + + /* ---------------- + * initialize tuple type and projection info + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &resstate->cstate); + ExecAssignProjectionInfo((Plan *) node, &resstate->cstate); + + /* ---------------- + * set "are we done yet" to false + * ---------------- + */ + resstate->rs_done = 0; + + return TRUE; } int -ExecCountSlotsResult(Result *node) +ExecCountSlotsResult(Result * node) { - return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS; + return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndResult - * - * fees up storage allocated through C routines + * ExecEndResult + * + * fees up storage allocated through C routines * ---------------------------------------------------------------- */ void -ExecEndResult(Result *node) +ExecEndResult(Result * node) { - ResultState *resstate; - - resstate = node->resstate; - - /* ---------------- - * Free the projection info - * - * Note: we don't ExecFreeResultType(resstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&resstate->cstate); - - /* ---------------- - * shut down subplans - * ---------------- - */ - ExecEndNode(outerPlan(node), (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(resstate->cstate.cs_ResultTupleSlot); + ResultState *resstate; + + resstate = node->resstate; + + /* ---------------- + * Free the projection info + * + * Note: we don't ExecFreeResultType(resstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&resstate->cstate); + + /* ---------------- + * shut down subplans + * ---------------- + */ + ExecEndNode(outerPlan(node), (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(resstate->cstate.cs_ResultTupleSlot); } diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index b94bb58d260..d3451f8026f 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -1,25 +1,25 @@ /*------------------------------------------------------------------------- * * nodeSeqscan.c-- - * Support routines for sequential scans of relations. + * Support routines for sequential scans of relations. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.4 1997/08/19 21:31:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.5 1997/09/07 04:41:44 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecSeqScan sequentially scans a relation. - * ExecSeqNext retrieve next tuple in sequential order. - * ExecInitSeqScan creates and initializes a seqscan node. - * ExecEndSeqScan releases any storage allocated. - * ExecSeqReScan rescans the relation - * ExecMarkPos marks scan position - * ExecRestrPos restores scan position + * ExecSeqScan sequentially scans a relation. + * ExecSeqNext retrieve next tuple in sequential order. + * ExecInitSeqScan creates and initializes a seqscan node. + * ExecEndSeqScan releases any storage allocated. + * ExecSeqReScan rescans the relation + * ExecMarkPos marks scan position + * ExecRestrPos restores scan position * */ #include "postgres.h" @@ -30,429 +30,443 @@ #include "access/heapam.h" #include "parser/parsetree.h" -static Oid InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate, Plan *outerPlan); +static Oid +InitScanRelation(SeqScan * node, EState * estate, + CommonScanState * scanstate, Plan * outerPlan); -static TupleTableSlot *SeqNext(SeqScan *node); +static TupleTableSlot *SeqNext(SeqScan * node); /* ---------------------------------------------------------------- - * Scan Support + * Scan Support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * SeqNext + * SeqNext * - * This is a workhorse for ExecSeqScan + * This is a workhorse for ExecSeqScan * ---------------------------------------------------------------- */ static TupleTableSlot * -SeqNext(SeqScan *node) +SeqNext(SeqScan * node) { - HeapTuple tuple; - HeapScanDesc scandesc; - CommonScanState *scanstate; - EState *estate; - ScanDirection direction; - TupleTableSlot *slot; - Buffer buffer; - - /* ---------------- - * get information from the estate and scan state - * ---------------- - */ - estate = node->plan.state; - scanstate = node->scanstate; - scandesc = scanstate->css_currentScanDesc; - direction = estate->es_direction; - - /* ---------------- - * get the next tuple from the access methods - * ---------------- - */ - tuple = heap_getnext(scandesc, /* scan desc */ - ScanDirectionIsBackward(direction), /*backward flag*/ - &buffer); /* return: buffer */ - - /* ---------------- - * save the tuple and the buffer returned to us by the access methods - * in our scan tuple slot and return the slot. Note: we pass 'false' - * because tuples returned by heap_getnext() are pointers onto - * disk pages and were not created with palloc() and so should not - * be pfree()'d. - * ---------------- - */ - slot = scanstate->css_ScanTupleSlot; - - slot = ExecStoreTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - buffer, /* buffer associated with this tuple */ - false); /* don't pfree this pointer */ - - /* ---------------- - * XXX -- mao says: The sequential scan for heap relations will - * automatically unpin the buffer this tuple is on when we cross - * a page boundary. The clearslot code also does this. We bump - * the pin count on the page here, since we actually have two - * pointers to it -- one in the scan desc and one in the tuple - * table slot. --mar 20 91 - * ---------------- - */ - ExecIncrSlotBufferRefcnt(slot); - - return slot; + HeapTuple tuple; + HeapScanDesc scandesc; + CommonScanState *scanstate; + EState *estate; + ScanDirection direction; + TupleTableSlot *slot; + Buffer buffer; + + /* ---------------- + * get information from the estate and scan state + * ---------------- + */ + estate = node->plan.state; + scanstate = node->scanstate; + scandesc = scanstate->css_currentScanDesc; + direction = estate->es_direction; + + /* ---------------- + * get the next tuple from the access methods + * ---------------- + */ + tuple = heap_getnext(scandesc, /* scan desc */ + ScanDirectionIsBackward(direction), /* backward flag */ + &buffer); /* return: buffer */ + + /* ---------------- + * save the tuple and the buffer returned to us by the access methods + * in our scan tuple slot and return the slot. Note: we pass 'false' + * because tuples returned by heap_getnext() are pointers onto + * disk pages and were not created with palloc() and so should not + * be pfree()'d. + * ---------------- + */ + slot = scanstate->css_ScanTupleSlot; + + slot = ExecStoreTuple(tuple,/* tuple to store */ + slot, /* slot to store in */ + buffer, /* buffer associated with this + * tuple */ + false); /* don't pfree this pointer */ + + /* ---------------- + * XXX -- mao says: The sequential scan for heap relations will + * automatically unpin the buffer this tuple is on when we cross + * a page boundary. The clearslot code also does this. We bump + * the pin count on the page here, since we actually have two + * pointers to it -- one in the scan desc and one in the tuple + * table slot. --mar 20 91 + * ---------------- + */ + ExecIncrSlotBufferRefcnt(slot); + + return slot; } /* ---------------------------------------------------------------- - * ExecSeqScan(node) + * ExecSeqScan(node) + * + * Scans the relation sequentially and returns the next qualifying + * tuple. + * It calls the ExecScan() routine and passes it the access method + * which retrieve tuples sequentially. * - * Scans the relation sequentially and returns the next qualifying - * tuple. - * It calls the ExecScan() routine and passes it the access method - * which retrieve tuples sequentially. - * */ TupleTableSlot * -ExecSeqScan(SeqScan *node) +ExecSeqScan(SeqScan * node) { - TupleTableSlot *slot; - Plan *outerPlan; - -S_printf("ExecSeqScan: scanning node: "); S_nodeDisplay(node); - - /* ---------------- - * if there is an outer subplan, get a tuple from it - * else, scan the relation - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - if (outerPlan) { - slot = ExecProcNode(outerPlan, (Plan*) node); - } else { - slot = ExecScan(node, SeqNext); - } - -S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot); - - return slot; + TupleTableSlot *slot; + Plan *outerPlan; + + S_printf("ExecSeqScan: scanning node: "); + S_nodeDisplay(node); + + /* ---------------- + * if there is an outer subplan, get a tuple from it + * else, scan the relation + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + slot = ExecProcNode(outerPlan, (Plan *) node); + } + else + { + slot = ExecScan(node, SeqNext); + } + + S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot); + + return slot; } /* ---------------------------------------------------------------- - * InitScanRelation + * InitScanRelation * - * This does the initialization for scan relations and - * subplans of scans. + * This does the initialization for scan relations and + * subplans of scans. * ---------------------------------------------------------------- */ -static Oid -InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate, Plan *outerPlan) +static Oid +InitScanRelation(SeqScan * node, EState * estate, + CommonScanState * scanstate, Plan * outerPlan) { - Index relid; - List *rangeTable; - RangeTblEntry *rtentry; - Oid reloid; - TimeQual timeQual; - ScanDirection direction; - Relation currentRelation; - HeapScanDesc currentScanDesc; - RelationInfo *resultRelationInfo; - - if (outerPlan == NULL) { - /* ---------------- - * if the outer node is nil then we are doing a simple - * sequential scan of a relation... - * - * get the relation object id from the relid'th entry - * in the range table, open that relation and initialize - * the scan state... - * ---------------- - */ - relid = node->scanrelid; - rangeTable = estate->es_range_table; - rtentry = rt_fetch(relid, rangeTable); - reloid = rtentry->relid; - timeQual = rtentry->timeQual; - direction = estate->es_direction; - resultRelationInfo = estate->es_result_relation_info; - - ExecOpenScanR(reloid, /* relation */ - 0, /* nkeys */ - NULL, /* scan key */ - 0, /* is index */ - direction, /* scan direction */ - timeQual, /* time qual */ - ¤tRelation, /* return: rel desc */ - (Pointer *) ¤tScanDesc); /* return: scan desc */ - - scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = currentScanDesc; - - ExecAssignScanType(scanstate, - RelationGetTupleDescriptor(currentRelation)); - } else { + Index relid; + List *rangeTable; + RangeTblEntry *rtentry; + Oid reloid; + TimeQual timeQual; + ScanDirection direction; + Relation currentRelation; + HeapScanDesc currentScanDesc; + RelationInfo *resultRelationInfo; + + if (outerPlan == NULL) + { + /* ---------------- + * if the outer node is nil then we are doing a simple + * sequential scan of a relation... + * + * get the relation object id from the relid'th entry + * in the range table, open that relation and initialize + * the scan state... + * ---------------- + */ + relid = node->scanrelid; + rangeTable = estate->es_range_table; + rtentry = rt_fetch(relid, rangeTable); + reloid = rtentry->relid; + timeQual = rtentry->timeQual; + direction = estate->es_direction; + resultRelationInfo = estate->es_result_relation_info; + + ExecOpenScanR(reloid, /* relation */ + 0, /* nkeys */ + NULL, /* scan key */ + 0, /* is index */ + direction,/* scan direction */ + timeQual, /* time qual */ + ¤tRelation, /* return: rel desc */ + (Pointer *) & currentScanDesc); /* return: scan desc */ + + scanstate->css_currentRelation = currentRelation; + scanstate->css_currentScanDesc = currentScanDesc; + + ExecAssignScanType(scanstate, + RelationGetTupleDescriptor(currentRelation)); + } + else + { + /* ---------------- + * otherwise we are scanning tuples from the + * outer subplan so we initialize the outer plan + * and nullify + * ---------------- + */ + ExecInitNode(outerPlan, estate, (Plan *) node); + + node->scanrelid = 0; + scanstate->css_currentRelation = NULL; + scanstate->css_currentScanDesc = NULL; + ExecAssignScanType(scanstate, NULL); + reloid = InvalidOid; + } + /* ---------------- - * otherwise we are scanning tuples from the - * outer subplan so we initialize the outer plan - * and nullify + * return the relation * ---------------- */ - ExecInitNode(outerPlan, estate, (Plan*)node); - - node->scanrelid = 0; - scanstate->css_currentRelation = NULL; - scanstate->css_currentScanDesc = NULL; - ExecAssignScanType(scanstate, NULL); - reloid = InvalidOid; - } - - /* ---------------- - * return the relation - * ---------------- - */ - return reloid; + return reloid; } /* ---------------------------------------------------------------- - * ExecInitSeqScan + * ExecInitSeqScan * * old comments - * Creates the run-time state information for the seqscan node - * and sets the relation id to contain relevant descriptors. - * - * If there is a outer subtree (sort), the outer subtree - * is initialized and the relation id is set to the descriptors - * returned by the subtree. + * Creates the run-time state information for the seqscan node + * and sets the relation id to contain relevant descriptors. + * + * If there is a outer subtree (sort), the outer subtree + * is initialized and the relation id is set to the descriptors + * returned by the subtree. * ---------------------------------------------------------------- */ bool -ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent) +ExecInitSeqScan(SeqScan * node, EState * estate, Plan * parent) { - CommonScanState *scanstate; - Plan *outerPlan; - Oid reloid; - HeapScanDesc scandesc; - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create new CommonScanState for node - * ---------------- - */ - scanstate = makeNode(CommonScanState); - node->scanstate = scanstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent); - ExecAssignExprContext(estate, &scanstate->cstate); + CommonScanState *scanstate; + Plan *outerPlan; + Oid reloid; + HeapScanDesc scandesc; + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create new CommonScanState for node + * ---------------- + */ + scanstate = makeNode(CommonScanState); + node->scanstate = scanstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent); + ExecAssignExprContext(estate, &scanstate->cstate); #define SEQSCAN_NSLOTS 3 - /* ---------------- - * tuple table initialization - * ---------------- - */ - ExecInitResultTupleSlot(estate, &scanstate->cstate); - ExecInitScanTupleSlot(estate, scanstate); - - /* ---------------- - * initialize scan relation or outer subplan - * ---------------- - */ - outerPlan = outerPlan((Plan *)node); - - reloid = InitScanRelation(node, estate, scanstate, outerPlan); - - scandesc = scanstate->css_currentScanDesc; - scanstate->cstate.cs_TupFromTlist = false; - - /* ---------------- - * initialize tuple type - * ---------------- - */ - ExecAssignResultTypeFromTL((Plan*)node, &scanstate->cstate); - ExecAssignProjectionInfo((Plan*)node, &scanstate->cstate); - - return TRUE; + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &scanstate->cstate); + ExecInitScanTupleSlot(estate, scanstate); + + /* ---------------- + * initialize scan relation or outer subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + + reloid = InitScanRelation(node, estate, scanstate, outerPlan); + + scandesc = scanstate->css_currentScanDesc; + scanstate->cstate.cs_TupFromTlist = false; + + /* ---------------- + * initialize tuple type + * ---------------- + */ + ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); + ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); + + return TRUE; } int -ExecCountSlotsSeqScan(SeqScan *node) +ExecCountSlotsSeqScan(SeqScan * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - SEQSCAN_NSLOTS; + SEQSCAN_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndSeqScan - * - * frees any storage allocated through C routines. - *| ...and also closes relations and/or shuts down outer subplan - *| -cim 8/14/89 + * ExecEndSeqScan + * + * frees any storage allocated through C routines. + *| ...and also closes relations and/or shuts down outer subplan + *| -cim 8/14/89 * ---------------------------------------------------------------- */ void -ExecEndSeqScan(SeqScan *node) +ExecEndSeqScan(SeqScan * node) { - CommonScanState *scanstate; - Plan *outerPlan; - - /* ---------------- - * get information from node - * ---------------- - */ - scanstate = node->scanstate; - - /* ---------------- - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(scanstate) - * because the rule manager depends on the tupType - * returned by ExecMain(). So for now, this - * is freed at end-transaction time. -cim 6/2/91 - * ---------------- - */ - ExecFreeProjectionInfo(&scanstate->cstate); - - /* ---------------- - * close scan relation - * ---------------- - */ - ExecCloseR((Plan*) node); - - /* ---------------- - * clean up outer subtree (does nothing if there is no outerPlan) - * ---------------- - */ - outerPlan = outerPlan((Plan *)node); - ExecEndNode(outerPlan, (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); - ExecClearTuple(scanstate->css_ScanTupleSlot); + CommonScanState *scanstate; + Plan *outerPlan; + + /* ---------------- + * get information from node + * ---------------- + */ + scanstate = node->scanstate; + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(scanstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&scanstate->cstate); + + /* ---------------- + * close scan relation + * ---------------- + */ + ExecCloseR((Plan *) node); + + /* ---------------- + * clean up outer subtree (does nothing if there is no outerPlan) + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecEndNode(outerPlan, (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); + ExecClearTuple(scanstate->css_ScanTupleSlot); } /* ---------------------------------------------------------------- - * Join Support + * Join Support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- - * ExecSeqReScan - * - * Rescans the relation. + * ExecSeqReScan + * + * Rescans the relation. * ---------------------------------------------------------------- */ void -ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent) +ExecSeqReScan(SeqScan * node, ExprContext * exprCtxt, Plan * parent) { - CommonScanState *scanstate; - EState *estate; - Plan *outerPlan; - Relation rdesc; - HeapScanDesc sdesc; - ScanDirection direction; - - scanstate = node->scanstate; - estate = node->plan.state; - - outerPlan = outerPlan((Plan*)node); - if (outerPlan) { - /* we are scanning a subplan */ - outerPlan = outerPlan((Plan *)node); - ExecReScan(outerPlan, exprCtxt, parent); - } else { - /* otherwise, we are scanning a relation */ - rdesc = scanstate->css_currentRelation; - sdesc = scanstate->css_currentScanDesc; - direction = estate->es_direction; - sdesc = ExecReScanR(rdesc, sdesc, direction, 0, NULL); - scanstate->css_currentScanDesc = sdesc; - } + CommonScanState *scanstate; + EState *estate; + Plan *outerPlan; + Relation rdesc; + HeapScanDesc sdesc; + ScanDirection direction; + + scanstate = node->scanstate; + estate = node->plan.state; + + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + /* we are scanning a subplan */ + outerPlan = outerPlan((Plan *) node); + ExecReScan(outerPlan, exprCtxt, parent); + } + else + { + /* otherwise, we are scanning a relation */ + rdesc = scanstate->css_currentRelation; + sdesc = scanstate->css_currentScanDesc; + direction = estate->es_direction; + sdesc = ExecReScanR(rdesc, sdesc, direction, 0, NULL); + scanstate->css_currentScanDesc = sdesc; + } } /* ---------------------------------------------------------------- - * ExecSeqMarkPos(node) - * - * Marks scan position. + * ExecSeqMarkPos(node) + * + * Marks scan position. * ---------------------------------------------------------------- */ void -ExecSeqMarkPos(SeqScan *node) +ExecSeqMarkPos(SeqScan * node) { - CommonScanState *scanstate; - Plan *outerPlan; - HeapScanDesc sdesc; - - scanstate = node->scanstate; - - /* ---------------- - * if we are scanning a subplan then propagate - * the ExecMarkPos() request to the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan*)node); - if (outerPlan) { - ExecMarkPos(outerPlan); + CommonScanState *scanstate; + Plan *outerPlan; + HeapScanDesc sdesc; + + scanstate = node->scanstate; + + /* ---------------- + * if we are scanning a subplan then propagate + * the ExecMarkPos() request to the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + ExecMarkPos(outerPlan); + return; + } + + /* ---------------- + * otherwise we are scanning a relation so mark the + * position using the access methods.. + * + * ---------------- + */ + sdesc = scanstate->css_currentScanDesc; + heap_markpos(sdesc); + return; - } - - /* ---------------- - * otherwise we are scanning a relation so mark the - * position using the access methods.. - * - * ---------------- - */ - sdesc = scanstate->css_currentScanDesc; - heap_markpos(sdesc); - - return; } /* ---------------------------------------------------------------- - * ExecSeqRestrPos - * - * Restores scan position. + * ExecSeqRestrPos + * + * Restores scan position. * ---------------------------------------------------------------- */ void -ExecSeqRestrPos(SeqScan *node) +ExecSeqRestrPos(SeqScan * node) { - CommonScanState *scanstate; - Plan *outerPlan; - HeapScanDesc sdesc; - - scanstate = node->scanstate; - - /* ---------------- - * if we are scanning a subplan then propagate - * the ExecRestrPos() request to the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan*)node); - if (outerPlan) { - ExecRestrPos(outerPlan); - return; - } - - /* ---------------- - * otherwise we are scanning a relation so restore the - * position using the access methods.. - * ---------------- - */ - sdesc = scanstate->css_currentScanDesc; - heap_restrpos(sdesc); + CommonScanState *scanstate; + Plan *outerPlan; + HeapScanDesc sdesc; + + scanstate = node->scanstate; + + /* ---------------- + * if we are scanning a subplan then propagate + * the ExecRestrPos() request to the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + if (outerPlan) + { + ExecRestrPos(outerPlan); + return; + } + + /* ---------------- + * otherwise we are scanning a relation so restore the + * position using the access methods.. + * ---------------- + */ + sdesc = scanstate->css_currentScanDesc; + heap_restrpos(sdesc); } diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index 0955108b2fb..eb2e2e7b180 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * nodeSort.c-- - * Routines to handle sorting of relations. + * Routines to handle sorting of relations. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.6 1997/08/21 02:28:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.7 1997/09/07 04:41:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,383 +25,389 @@ #include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */ /* ---------------------------------------------------------------- - * FormSortKeys(node) - * - * Forms the structure containing information used to sort the relation. - * - * Returns an array of ScanKeyData. + * FormSortKeys(node) + * + * Forms the structure containing information used to sort the relation. + * + * Returns an array of ScanKeyData. * ---------------------------------------------------------------- */ -static ScanKey -FormSortKeys(Sort *sortnode) +static ScanKey +FormSortKeys(Sort * sortnode) { - ScanKey sortkeys; - List *targetList; - List *tl; - int keycount; - Resdom *resdom; - AttrNumber resno; - Index reskey; - Oid reskeyop; - - /* ---------------- - * get information from the node - * ---------------- - */ - targetList = sortnode->plan.targetlist; - keycount = sortnode->keycount; - - /* ---------------- - * first allocate space for scan keys - * ---------------- - */ - if (keycount <= 0) - elog(WARN, "FormSortKeys: keycount <= 0"); - sortkeys = (ScanKey) palloc(keycount * sizeof(ScanKeyData)); - - /* ---------------- - * form each scan key from the resdom info in the target list - * ---------------- - */ - foreach(tl, targetList) { - TargetEntry *target = (TargetEntry *)lfirst(tl); - resdom = target->resdom; - resno = resdom->resno; - reskey = resdom->reskey; - reskeyop = resdom->reskeyop; - - if (reskey > 0) { - ScanKeyEntryInitialize(&sortkeys[reskey-1], - 0, - resno, - (RegProcedure) DatumGetInt32(reskeyop), - (Datum) 0); + ScanKey sortkeys; + List *targetList; + List *tl; + int keycount; + Resdom *resdom; + AttrNumber resno; + Index reskey; + Oid reskeyop; + + /* ---------------- + * get information from the node + * ---------------- + */ + targetList = sortnode->plan.targetlist; + keycount = sortnode->keycount; + + /* ---------------- + * first allocate space for scan keys + * ---------------- + */ + if (keycount <= 0) + elog(WARN, "FormSortKeys: keycount <= 0"); + sortkeys = (ScanKey) palloc(keycount * sizeof(ScanKeyData)); + + /* ---------------- + * form each scan key from the resdom info in the target list + * ---------------- + */ + foreach(tl, targetList) + { + TargetEntry *target = (TargetEntry *) lfirst(tl); + + resdom = target->resdom; + resno = resdom->resno; + reskey = resdom->reskey; + reskeyop = resdom->reskeyop; + + if (reskey > 0) + { + ScanKeyEntryInitialize(&sortkeys[reskey - 1], + 0, + resno, + (RegProcedure) DatumGetInt32(reskeyop), + (Datum) 0); + } } - } - - return sortkeys; + + return sortkeys; } /* ---------------------------------------------------------------- - * ExecSort + * ExecSort * * old comments - * Sorts tuples from the outer subtree of the node in psort, - * which saves the results in a temporary file or memory. After the - * initial call, returns a tuple from the file with each call. - * Assumes that heap access method is used. - * - * Conditions: - * -- none. - * - * Initial States: - * -- the outer child is prepared to return the first tuple. + * Sorts tuples from the outer subtree of the node in psort, + * which saves the results in a temporary file or memory. After the + * initial call, returns a tuple from the file with each call. + * Assumes that heap access method is used. + * + * Conditions: + * -- none. + * + * Initial States: + * -- the outer child is prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * -ExecSort(Sort *node) +ExecSort(Sort * node) { - EState *estate; - SortState *sortstate; - Plan *outerNode; - ScanDirection dir; - int keycount; - ScanKey sortkeys; - HeapTuple heapTuple; - TupleTableSlot *slot; - - /* ---------------- - * get state info from node - * ---------------- - */ - SO1_printf("ExecSort: %s\n", - "entering routine"); - - sortstate = node->sortstate; - estate = node->plan.state; - dir = estate->es_direction; - - /* ---------------- - * the first time we call this, psort sorts this into a file. - * Subsequent calls return tuples from psort. - * ---------------- - */ - - if (sortstate->sort_Flag == false) { - SO1_printf("ExecSort: %s\n", - "sortstate == false -> sorting subplan"); + EState *estate; + SortState *sortstate; + Plan *outerNode; + ScanDirection dir; + int keycount; + ScanKey sortkeys; + HeapTuple heapTuple; + TupleTableSlot *slot; + /* ---------------- - * set all relations to be scanned in the forward direction - * while creating the temporary relation. + * get state info from node * ---------------- */ - estate->es_direction = ForwardScanDirection; + SO1_printf("ExecSort: %s\n", + "entering routine"); + + sortstate = node->sortstate; + estate = node->plan.state; + dir = estate->es_direction; /* ---------------- - * prepare information for psort_begin() + * the first time we call this, psort sorts this into a file. + * Subsequent calls return tuples from psort. * ---------------- */ - outerNode = outerPlan((Plan *) node); - keycount = node->keycount; - sortkeys = (ScanKey)sortstate->sort_Keys; - SO1_printf("ExecSort: %s\n", - "calling psort_begin"); - - if (!psort_begin(node, /* this node */ - keycount, /* number keys */ - sortkeys)) /* keys */ + if (sortstate->sort_Flag == false) { - /* Psort says, there are no tuples to be sorted */ - return NULL; - } - - /* ---------------- - * restore to user specified direction - * ---------------- - */ - estate->es_direction = dir; - - /* ---------------- - * make sure the tuple descriptor is up to date - * ---------------- - */ - slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot; - /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ + SO1_printf("ExecSort: %s\n", + "sortstate == false -> sorting subplan"); + /* ---------------- + * set all relations to be scanned in the forward direction + * while creating the temporary relation. + * ---------------- + */ + estate->es_direction = ForwardScanDirection; + + /* ---------------- + * prepare information for psort_begin() + * ---------------- + */ + outerNode = outerPlan((Plan *) node); + + keycount = node->keycount; + sortkeys = (ScanKey) sortstate->sort_Keys; + SO1_printf("ExecSort: %s\n", + "calling psort_begin"); + + if (!psort_begin(node, /* this node */ + keycount, /* number keys */ + sortkeys)) /* keys */ + { + /* Psort says, there are no tuples to be sorted */ + return NULL; + } + + /* ---------------- + * restore to user specified direction + * ---------------- + */ + estate->es_direction = dir; - slot->ttc_tupleDescriptor = ExecGetTupType(outerNode); + /* ---------------- + * make sure the tuple descriptor is up to date + * ---------------- + */ + slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot; + /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ + + slot->ttc_tupleDescriptor = ExecGetTupType(outerNode); #if 0 - slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode); + slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode); #endif + /* ---------------- + * finally set the sorted flag to true + * ---------------- + */ + sortstate->sort_Flag = true; + SO1_printf(stderr, "ExecSort: sorting done.\n"); + } + else + { + slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot; + /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ +/* slot = sortstate->csstate.css_ScanTupleSlot; orig */ + } + + SO1_printf("ExecSort: %s\n", + "retrieving tuple from sorted relation"); + /* ---------------- - * finally set the sorted flag to true + * at this point we grab a tuple from psort * ---------------- */ - sortstate->sort_Flag = true; - SO1_printf(stderr, "ExecSort: sorting done.\n"); - } - else { - slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot; - /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */ -/* slot = sortstate->csstate.css_ScanTupleSlot; orig */ - } - - SO1_printf("ExecSort: %s\n", - "retrieving tuple from sorted relation"); - - /* ---------------- - * at this point we grab a tuple from psort - * ---------------- - */ - heapTuple = psort_grabtuple(node); - - if (heapTuple == NULL) { -/* psort_end(node); */ - return (ExecClearTuple(slot)); - } - - ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* no buffer */ - true); /* free the palloc'd tuple */ -/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/ - return slot; + heapTuple = psort_grabtuple(node); + + if (heapTuple == NULL) + { +/* psort_end(node); */ + return (ExecClearTuple(slot)); + } + + ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* no buffer */ + true); /* free the palloc'd tuple */ +/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/ + return slot; #if 0 - return ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* no buffer */ - true); /* free the palloc'd tuple */ + return ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* no buffer */ + true);/* free the palloc'd tuple */ #endif } /* ---------------------------------------------------------------- - * ExecInitSort + * ExecInitSort * * old comments - * Creates the run-time state information for the sort node - * produced by the planner and initailizes its outer subtree. + * Creates the run-time state information for the sort node + * produced by the planner and initailizes its outer subtree. * ---------------------------------------------------------------- */ bool -ExecInitSort(Sort *node, EState *estate, Plan *parent) +ExecInitSort(Sort * node, EState * estate, Plan * parent) { - SortState *sortstate; - Plan *outerPlan; - ScanKey sortkeys; - - SO1_printf("ExecInitSort: %s\n", - "initializing sort node"); - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create state structure - * ---------------- - */ - sortstate = makeNode(SortState); - sortstate->sort_Flag = 0; - sortstate->sort_Keys = NULL; - node->cleaned = FALSE; - - node->sortstate = sortstate; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks - * - * Sort nodes don't initialize their ExprContexts because - * they never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent); - + SortState *sortstate; + Plan *outerPlan; + ScanKey sortkeys; + + SO1_printf("ExecInitSort: %s\n", + "initializing sort node"); + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create state structure + * ---------------- + */ + sortstate = makeNode(SortState); + sortstate->sort_Flag = 0; + sortstate->sort_Keys = NULL; + node->cleaned = FALSE; + + node->sortstate = sortstate; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks + * + * Sort nodes don't initialize their ExprContexts because + * they never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent); + #define SORT_NSLOTS 1 - /* ---------------- - * tuple table initialization - * - * sort nodes only return scan tuples from their sorted - * relation. - * ---------------- - */ - ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate); - ExecInitScanTupleSlot(estate, &sortstate->csstate); - - /* ---------------- - * initializes child nodes - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* ---------------- - * initialize sortstate information - * ---------------- - */ - sortkeys = FormSortKeys(node); - sortstate->sort_Keys = sortkeys; - sortstate->sort_Flag = false; - - /* ---------------- - * initialize tuple type. no need to initialize projection - * info because this node doesn't do projections. - * ---------------- - */ - ExecAssignResultTypeFromOuterPlan((Plan *)node, &sortstate->csstate.cstate); - ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate); - sortstate->csstate.cstate.cs_ProjInfo = NULL; - - SO1_printf("ExecInitSort: %s\n", - "sort node initialized"); - - /* ---------------- - * return relation oid of temporary sort relation in a list - * (someday -- for now we return LispTrue... cim 10/12/89) - * ---------------- - */ - return TRUE; + /* ---------------- + * tuple table initialization + * + * sort nodes only return scan tuples from their sorted + * relation. + * ---------------- + */ + ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate); + ExecInitScanTupleSlot(estate, &sortstate->csstate); + + /* ---------------- + * initializes child nodes + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * initialize sortstate information + * ---------------- + */ + sortkeys = FormSortKeys(node); + sortstate->sort_Keys = sortkeys; + sortstate->sort_Flag = false; + + /* ---------------- + * initialize tuple type. no need to initialize projection + * info because this node doesn't do projections. + * ---------------- + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, &sortstate->csstate.cstate); + ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate); + sortstate->csstate.cstate.cs_ProjInfo = NULL; + + SO1_printf("ExecInitSort: %s\n", + "sort node initialized"); + + /* ---------------- + * return relation oid of temporary sort relation in a list + * (someday -- for now we return LispTrue... cim 10/12/89) + * ---------------- + */ + return TRUE; } int -ExecCountSlotsSort(Sort *node) +ExecCountSlotsSort(Sort * node) { - return ExecCountSlotsNode(outerPlan((Plan *)node)) + - ExecCountSlotsNode(innerPlan((Plan *)node)) + - SORT_NSLOTS; + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + + SORT_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndSort(node) + * ExecEndSort(node) * * old comments * ---------------------------------------------------------------- */ void -ExecEndSort(Sort *node) +ExecEndSort(Sort * node) { - SortState *sortstate; - Plan *outerPlan; - - /* ---------------- - * get info from the sort state - * ---------------- - */ - SO1_printf("ExecEndSort: %s\n", - "shutting down sort node"); - - sortstate = node->sortstate; - - /* ---------------- - * shut down the subplan - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecEndNode(outerPlan, (Plan*)node); - - /* ---------------- - * clean out the tuple table - * ---------------- - */ - ExecClearTuple(sortstate->csstate.css_ScanTupleSlot); - - /* Clean up after psort */ - psort_end(node); - - SO1_printf("ExecEndSort: %s\n", - "sort node shutdown"); -} + SortState *sortstate; + Plan *outerPlan; + + /* ---------------- + * get info from the sort state + * ---------------- + */ + SO1_printf("ExecEndSort: %s\n", + "shutting down sort node"); + + sortstate = node->sortstate; + + /* ---------------- + * shut down the subplan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecEndNode(outerPlan, (Plan *) node); + + /* ---------------- + * clean out the tuple table + * ---------------- + */ + ExecClearTuple(sortstate->csstate.css_ScanTupleSlot); + + /* Clean up after psort */ + psort_end(node); + + SO1_printf("ExecEndSort: %s\n", + "sort node shutdown"); +} /* ---------------------------------------------------------------- - * ExecSortMarkPos + * ExecSortMarkPos * - * Calls psort to save the current position in the sorted file. + * Calls psort to save the current position in the sorted file. * ---------------------------------------------------------------- */ void -ExecSortMarkPos(Sort *node) +ExecSortMarkPos(Sort * node) { - SortState *sortstate; - - /* ---------------- - * if we haven't sorted yet, just return - * ---------------- - */ - sortstate = node->sortstate; - if (sortstate->sort_Flag == false) - return; - - psort_markpos(node); - - return; + SortState *sortstate; + + /* ---------------- + * if we haven't sorted yet, just return + * ---------------- + */ + sortstate = node->sortstate; + if (sortstate->sort_Flag == false) + return; + + psort_markpos(node); + + return; } /* ---------------------------------------------------------------- - * ExecSortRestrPos + * ExecSortRestrPos * - * Calls psort to restore the last saved sort file position. + * Calls psort to restore the last saved sort file position. * ---------------------------------------------------------------- */ void -ExecSortRestrPos(Sort *node) +ExecSortRestrPos(Sort * node) { - SortState *sortstate; - - /* ---------------- - * if we haven't sorted yet, just return. - * ---------------- - */ - sortstate = node->sortstate; - if (sortstate->sort_Flag == false) - return; - - /* ---------------- - * restore the scan to the previously marked position - * ---------------- - */ - psort_restorepos(node); + SortState *sortstate; + + /* ---------------- + * if we haven't sorted yet, just return. + * ---------------- + */ + sortstate = node->sortstate; + if (sortstate->sort_Flag == false) + return; + + /* ---------------- + * restore the scan to the previously marked position + * ---------------- + */ + psort_restorepos(node); } diff --git a/src/backend/executor/nodeTee.c b/src/backend/executor/nodeTee.c index 6ddbecc3a49..8a1e233125a 100644 --- a/src/backend/executor/nodeTee.c +++ b/src/backend/executor/nodeTee.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * nodeTee.c-- - * + * * * Copyright (c) 1994, Regents of the University of California * - * DESCRIPTION - * This code provides support for a tee node, which allows multiple - * parent in a megaplan. - * - * INTERFACE ROUTINES - * ExecTee - * ExecInitTee - * ExecEndTee + * DESCRIPTION + * This code provides support for a tee node, which allows multiple + * parent in a megaplan. + * + * INTERFACE ROUTINES + * ExecTee + * ExecInitTee + * ExecEndTee * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.6 1997/07/28 00:54:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.7 1997/09/07 04:41:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,9 +25,9 @@ #include "postgres.h" #include "utils/palloc.h" -#include "utils/relcache.h" +#include "utils/relcache.h" #include "utils/mcxt.h" -#include "storage/bufmgr.h" /* for IncrBufferRefCount */ +#include "storage/bufmgr.h" /* for IncrBufferRefCount */ #include "storage/smgr.h" #include "optimizer/internal.h" #include "executor/executor.h" @@ -38,475 +38,520 @@ #include "access/heapam.h" /* ------------------------------------------------------------------ - * ExecInitTee + * ExecInitTee * - * Create tee state + * Create tee state * * ------------------------------------------------------------------ */ bool -ExecInitTee(Tee* node, EState *currentEstate, Plan * parent) +ExecInitTee(Tee * node, EState * currentEstate, Plan * parent) { - TeeState *teeState; - Plan *outerPlan; - int len; - Relation bufferRel; - TupleDesc tupType; - EState *estate; - - /* it is possible that the Tee has already been initialized - since it can be reached by multiple parents. - If it is already initialized, simply return and do - not initialize the children nodes again - */ - if (node->plan.state) - return TRUE; - - /* ---------------- - * assign the node's execution state - * ---------------- - */ - /* make a new executor state, because we have a different - es_range_table */ - -/* node->plan.state = estate;*/ - - estate = CreateExecutorState(); - estate->es_direction = currentEstate->es_direction; - estate->es_BaseId = currentEstate->es_BaseId; - estate->es_BaseId = currentEstate->es_BaseId; - estate->es_tupleTable = currentEstate->es_tupleTable; - estate->es_refcount = currentEstate->es_refcount; - estate->es_junkFilter = currentEstate->es_junkFilter; - - /* use the range table for Tee subplan since the range tables - for the two parents may be different */ - if (node->rtentries) - estate->es_range_table = node->rtentries; - else - estate->es_range_table = currentEstate->es_range_table; - - node->plan.state = estate; - - - /* ---------------- - * create teeState structure - * ---------------- - */ - teeState = makeNode(TeeState); - teeState->tee_leftPlace = 0; - teeState->tee_rightPlace = 0; - teeState->tee_lastPlace = 0; - teeState->tee_bufferRel = NULL; - teeState->tee_leftScanDesc = NULL; - teeState->tee_rightScanDesc = NULL; - - - node->teestate = teeState; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent); - ExecAssignExprContext(estate, &(teeState->cstate)); + TeeState *teeState; + Plan *outerPlan; + int len; + Relation bufferRel; + TupleDesc tupType; + EState *estate; + + /* + * it is possible that the Tee has already been initialized since it + * can be reached by multiple parents. If it is already initialized, + * simply return and do not initialize the children nodes again + */ + if (node->plan.state) + return TRUE; + + /* ---------------- + * assign the node's execution state + * ---------------- + */ + + /* + * make a new executor state, because we have a different + * es_range_table + */ + +/* node->plan.state = estate;*/ + + estate = CreateExecutorState(); + estate->es_direction = currentEstate->es_direction; + estate->es_BaseId = currentEstate->es_BaseId; + estate->es_BaseId = currentEstate->es_BaseId; + estate->es_tupleTable = currentEstate->es_tupleTable; + estate->es_refcount = currentEstate->es_refcount; + estate->es_junkFilter = currentEstate->es_junkFilter; + + /* + * use the range table for Tee subplan since the range tables for the + * two parents may be different + */ + if (node->rtentries) + estate->es_range_table = node->rtentries; + else + estate->es_range_table = currentEstate->es_range_table; + + node->plan.state = estate; + + + /* ---------------- + * create teeState structure + * ---------------- + */ + teeState = makeNode(TeeState); + teeState->tee_leftPlace = 0; + teeState->tee_rightPlace = 0; + teeState->tee_lastPlace = 0; + teeState->tee_bufferRel = NULL; + teeState->tee_leftScanDesc = NULL; + teeState->tee_rightScanDesc = NULL; + + + node->teestate = teeState; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + create expression context for node + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent); + ExecAssignExprContext(estate, &(teeState->cstate)); #define TEE_NSLOTS 2 - /* ---------------- - * initialize tuple slots - * ---------------- - */ - ExecInitResultTupleSlot(estate, &(teeState->cstate)); - - /* initialize child nodes */ - outerPlan = outerPlan((Plan*) node); - ExecInitNode(outerPlan, estate, (Plan*) node); - - /* ---------------- - * the tuple type info is from the outer plan of this node - * the result type is also the same as the outerplan - */ - ExecAssignResultTypeFromOuterPlan((Plan*) node, &(teeState->cstate)); - ExecAssignProjectionInfo((Plan*)node, &teeState->cstate); - - /* --------------------------------------- - initialize temporary relation to buffer tuples - */ - tupType = ExecGetResultType(&(teeState->cstate)); - len = ExecTargetListLength(((Plan*)node)->targetlist); - -/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */ - - /* create a catalogued relation even though this is a temporary relation */ - /* cleanup of catalogued relations is easier to do */ - - if (node->teeTableName[0] != '\0') { - Relation r; - - teeState->tee_bufferRelname = pstrdup(node->teeTableName); - - /* we are given an tee table name, - if a relation by that name exists, then we open it, - else we create it and then open it */ - r = RelationNameGetRelation(teeState->tee_bufferRelname); - - if (RelationIsValid(r)) - bufferRel = heap_openr(teeState->tee_bufferRelname); + /* ---------------- + * initialize tuple slots + * ---------------- + */ + ExecInitResultTupleSlot(estate, &(teeState->cstate)); + + /* initialize child nodes */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * the tuple type info is from the outer plan of this node + * the result type is also the same as the outerplan + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, &(teeState->cstate)); + ExecAssignProjectionInfo((Plan *) node, &teeState->cstate); + + /* --------------------------------------- + initialize temporary relation to buffer tuples + */ + tupType = ExecGetResultType(&(teeState->cstate)); + len = ExecTargetListLength(((Plan *) node)->targetlist); + +/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */ + + /* + * create a catalogued relation even though this is a temporary + * relation + */ + /* cleanup of catalogued relations is easier to do */ + + if (node->teeTableName[0] != '\0') + { + Relation r; + + teeState->tee_bufferRelname = pstrdup(node->teeTableName); + + /* + * we are given an tee table name, if a relation by that name + * exists, then we open it, else we create it and then open it + */ + r = RelationNameGetRelation(teeState->tee_bufferRelname); + + if (RelationIsValid(r)) + bufferRel = heap_openr(teeState->tee_bufferRelname); + else + bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, + /*FIX */ NULL, + 'n', + DEFAULT_SMGR, + tupType)); + } else - bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, -/*FIX */ NULL, - 'n', - DEFAULT_SMGR, - tupType)); - } - else { - sprintf(teeState->tee_bufferRelname, - "ttemp_%d", /* 'ttemp' for 'tee' temporary*/ - newoid()); -/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID); */ - bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, - NULL, /*XXX */ - 'n', - DEFAULT_SMGR, - tupType)); - } - - teeState->tee_bufferRel = bufferRel; - - /*initialize a memory context for allocating thing like scan descriptors */ - /* we do this so that on cleanup of the tee, we can free things. - if we didn't have our own memory context, we would be in the memory - context of the portal that we happen to be using at the moment */ - - teeState->tee_mcxt = (MemoryContext)CreateGlobalMemory(teeState->tee_bufferRelname); - - /* don't initialize the scan descriptors here - because it's not good to initialize scan descriptors on empty - rels. Wait until the scan descriptors are needed - before initializing them. */ - - teeState->tee_leftScanDesc = NULL; - teeState->tee_rightScanDesc = NULL; - - return TRUE; + { + sprintf(teeState->tee_bufferRelname, + "ttemp_%d", /* 'ttemp' for 'tee' temporary */ + newoid()); +/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID); */ + bufferRel = heap_open(heap_create(teeState->tee_bufferRelname, + NULL, /* XXX */ + 'n', + DEFAULT_SMGR, + tupType)); + } + + teeState->tee_bufferRel = bufferRel; + + /* + * initialize a memory context for allocating thing like scan + * descriptors + */ + + /* + * we do this so that on cleanup of the tee, we can free things. if we + * didn't have our own memory context, we would be in the memory + * context of the portal that we happen to be using at the moment + */ + + teeState->tee_mcxt = (MemoryContext) CreateGlobalMemory(teeState->tee_bufferRelname); + + /* + * don't initialize the scan descriptors here because it's not good to + * initialize scan descriptors on empty rels. Wait until the scan + * descriptors are needed before initializing them. + */ + + teeState->tee_leftScanDesc = NULL; + teeState->tee_rightScanDesc = NULL; + + return TRUE; } -int -ExecCountSlotsTee(Tee *node) +int +ExecCountSlotsTee(Tee * node) { - /* Tee nodes can't have innerPlans */ - return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS; + /* Tee nodes can't have innerPlans */ + return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS; } /* ---------------------------------------------------------------- initTeeScanDescs - initializes the left and right scandescs on the temporary - relation of a Tee node + initializes the left and right scandescs on the temporary + relation of a Tee node - must open two separate scan descriptors, - because the left and right scans may be at different points + must open two separate scan descriptors, + because the left and right scans may be at different points * ---------------------------------------------------------------- */ -static void -initTeeScanDescs(Tee* node) +static void +initTeeScanDescs(Tee * node) { - TeeState *teeState; - Relation bufferRel; - ScanDirection dir; - MemoryContext orig; - - teeState = node->teestate; - if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc) - return; - - orig = CurrentMemoryContext; - MemoryContextSwitchTo(teeState->tee_mcxt); - - bufferRel = teeState->tee_bufferRel; - dir = ((Plan*)node)->state->es_direction; /* backwards not handled yet XXX */ - - if (teeState->tee_leftScanDesc == NULL) - { - teeState->tee_leftScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - NowTimeQual, /* time qual */ - 0, /* num scan keys */ - NULL /* scan keys */ - ); - } - if (teeState->tee_rightScanDesc == NULL) - { - teeState->tee_rightScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - NowTimeQual, /* time qual */ - 0, /* num scan keys */ - NULL /* scan keys */ - ); - } - - MemoryContextSwitchTo(orig); + TeeState *teeState; + Relation bufferRel; + ScanDirection dir; + MemoryContext orig; + + teeState = node->teestate; + if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc) + return; + + orig = CurrentMemoryContext; + MemoryContextSwitchTo(teeState->tee_mcxt); + + bufferRel = teeState->tee_bufferRel; + dir = ((Plan *) node)->state->es_direction; /* backwards not handled + * yet XXX */ + + if (teeState->tee_leftScanDesc == NULL) + { + teeState->tee_leftScanDesc = heap_beginscan(bufferRel, + ScanDirectionIsBackward(dir), + NowTimeQual, /* time qual */ + 0, /* num scan keys */ + NULL /* scan keys */ + ); + } + if (teeState->tee_rightScanDesc == NULL) + { + teeState->tee_rightScanDesc = heap_beginscan(bufferRel, + ScanDirectionIsBackward(dir), + NowTimeQual, /* time qual */ + 0, /* num scan keys */ + NULL /* scan keys */ + ); + } + + MemoryContextSwitchTo(orig); } /* ---------------------------------------------------------------- - * ExecTee(node) + * ExecTee(node) + * * + * A Tee serves to connect a subplan to multiple parents. + * the subplan is always the outplan of the Tee node. * - * A Tee serves to connect a subplan to multiple parents. - * the subplan is always the outplan of the Tee node. - * - * The Tee gets requests from either leftParent or rightParent, - * fetches the result tuple from the child, and then - * stored the result into a temporary relation (serving as a queue). - * leftPlace and rightPlace keep track of where the left and rightParents - * are. - * If a parent requests a tuple and that parent is not at the end - * of the temporary relation, then the request is satisfied from - * the queue instead of by executing the child plan + * The Tee gets requests from either leftParent or rightParent, + * fetches the result tuple from the child, and then + * stored the result into a temporary relation (serving as a queue). + * leftPlace and rightPlace keep track of where the left and rightParents + * are. + * If a parent requests a tuple and that parent is not at the end + * of the temporary relation, then the request is satisfied from + * the queue instead of by executing the child plan * * ---------------------------------------------------------------- */ -TupleTableSlot* -ExecTee(Tee *node, Plan *parent) +TupleTableSlot * +ExecTee(Tee * node, Plan * parent) { - EState *estate; - TeeState *teeState; - int leftPlace, rightPlace, lastPlace; - int branch; - TupleTableSlot* result; - TupleTableSlot* slot; - Plan *childNode; - ScanDirection dir; - HeapTuple heapTuple; - Relation bufferRel; - HeapScanDesc scanDesc; - Buffer buffer; - - estate = ((Plan*)node)->state; - teeState = node->teestate; - leftPlace = teeState->tee_leftPlace; - rightPlace = teeState->tee_rightPlace; - lastPlace = teeState->tee_lastPlace; - bufferRel = teeState->tee_bufferRel; - - childNode = outerPlan(node); - - dir = estate->es_direction; - - /* XXX doesn't handle backwards direction yet */ - - if (parent == node->leftParent) { - branch = leftPlace; - } - else - if ( (parent == node->rightParent) || (parent == (Plan*) node)) - /* the tee node could be the root node of the plan, - in which case, we treat it like a right-parent pull*/ + EState *estate; + TeeState *teeState; + int leftPlace, + rightPlace, + lastPlace; + int branch; + TupleTableSlot *result; + TupleTableSlot *slot; + Plan *childNode; + ScanDirection dir; + HeapTuple heapTuple; + Relation bufferRel; + HeapScanDesc scanDesc; + Buffer buffer; + + estate = ((Plan *) node)->state; + teeState = node->teestate; + leftPlace = teeState->tee_leftPlace; + rightPlace = teeState->tee_rightPlace; + lastPlace = teeState->tee_lastPlace; + bufferRel = teeState->tee_bufferRel; + + childNode = outerPlan(node); + + dir = estate->es_direction; + + /* XXX doesn't handle backwards direction yet */ + + if (parent == node->leftParent) + { + branch = leftPlace; + } + else if ((parent == node->rightParent) || (parent == (Plan *) node)) + + /* + * the tee node could be the root node of the plan, in which case, + * we treat it like a right-parent pull + */ + { + branch = rightPlace; + } + else + { + elog(WARN, "A Tee node can only be executed from its left or right parent\n"); + return NULL; + } + + if (branch == lastPlace) + { /* we're at the end of the queue already, + * - get a new tuple from the child plan, + * - store it in the queue, - increment + * lastPlace, - increment leftPlace or + * rightPlace as appropriate, - and return + * result */ + slot = ExecProcNode(childNode, (Plan *) node); + if (!TupIsNull(slot)) + { + heapTuple = slot->val; + + /* insert into temporary relation */ + heap_insert(bufferRel, heapTuple); + + /* + * once there is data in the temporary relation, ensure that + * the left and right scandescs are initialized + */ + initTeeScanDescs(node); + + scanDesc = (parent == node->leftParent) ? + teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; + + { + + /* + * move the scandesc forward so we don't re-read this + * tuple later + */ + HeapTuple throwAway; + + /* Buffer buffer; */ + throwAway = heap_getnext(scanDesc, + ScanDirectionIsBackward(dir), + /* &buffer */ + (Buffer *) NULL); + } + + /* + * set the shouldFree field of the child's slot so that when + * the child's slot is free'd, this tuple isn't free'd also + */ + + /* + * does this mean this tuple has to be garbage collected + * later?? + */ + slot->ttc_shouldFree = false; + + teeState->tee_lastPlace = lastPlace + 1; + } + result = slot; + } + else + { /* the desired data already exists in the + * temporary relation */ + scanDesc = (parent == node->leftParent) ? + teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; + + heapTuple = heap_getnext(scanDesc, + ScanDirectionIsBackward(dir), + &buffer); + + /* + * Increase the pin count on the buffer page, because the tuple + * stored in the slot also points to it (as well as the scan + * descriptor). If we don't, ExecStoreTuple will decrease the pin + * count on the next iteration. + */ + + if (buffer != InvalidBuffer) + IncrBufferRefCount(buffer); + + slot = teeState->cstate.cs_ResultTupleSlot; + slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel); + + result = ExecStoreTuple(heapTuple, /* tuple to store */ + slot, /* slot to store in */ + buffer, /* this tuple's buffer */ + false); /* don't free stuff from + * heap_getnext */ + + } + + if (parent == node->leftParent) { - branch = rightPlace; - } - else - { - elog(WARN,"A Tee node can only be executed from its left or right parent\n"); - return NULL; - } - - if (branch == lastPlace) - { /* we're at the end of the queue already, - - get a new tuple from the child plan, - - store it in the queue, - - increment lastPlace, - - increment leftPlace or rightPlace as appropriate, - - and return result - */ - slot = ExecProcNode(childNode, (Plan*)node); - if (!TupIsNull(slot)) - { - heapTuple = slot->val; - - /* insert into temporary relation */ - heap_insert(bufferRel, heapTuple); - - /* once there is data in the temporary relation, - ensure that the left and right scandescs are initialized */ - initTeeScanDescs(node); - - scanDesc = (parent == node->leftParent) ? - teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; - - { - /* move the scandesc forward so we don't re-read this tuple later */ - HeapTuple throwAway; - /* Buffer buffer;*/ - throwAway = heap_getnext(scanDesc, - ScanDirectionIsBackward(dir), - /* &buffer */ - (Buffer*)NULL); - } - - /* set the shouldFree field of the child's slot so that - when the child's slot is free'd, this tuple isn't free'd also */ - /* does this mean this tuple has to be garbage collected later??*/ - slot->ttc_shouldFree = false; - - teeState->tee_lastPlace = lastPlace + 1; - } - result = slot; - } - else - {/* the desired data already exists in the temporary relation */ - scanDesc = (parent == node->leftParent) ? - teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; - - heapTuple = heap_getnext(scanDesc, - ScanDirectionIsBackward(dir), - &buffer); - - /* Increase the pin count on the buffer page, because the - tuple stored in the slot also points to it (as well as - the scan descriptor). If we don't, ExecStoreTuple will - decrease the pin count on the next iteration. */ - - if (buffer != InvalidBuffer) - IncrBufferRefCount(buffer); - - slot = teeState->cstate.cs_ResultTupleSlot; - slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel); - - result = ExecStoreTuple(heapTuple,/* tuple to store */ - slot, /* slot to store in */ - buffer,/* this tuple's buffer */ - false); /* don't free stuff from heap_getnext */ - - } - - if (parent == node->leftParent) - { - teeState->tee_leftPlace = leftPlace+1; - } - else - { - teeState->tee_rightPlace = rightPlace+1; - } - - return result; + teeState->tee_leftPlace = leftPlace + 1; + } + else + { + teeState->tee_rightPlace = rightPlace + 1; + } + + return result; } /* ---------------------------------------------------------------- - * ExecTeeReScan(node) - * - * Rescans the relation. + * ExecTeeReScan(node) + * + * Rescans the relation. * ---------------------------------------------------------------- */ void -ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent) +ExecTeeReScan(Tee * node, ExprContext * exprCtxt, Plan * parent) { - EState *estate; - TeeState *teeState; - ScanDirection dir; + EState *estate; + TeeState *teeState; + ScanDirection dir; - estate = ((Plan*)node)->state; - teeState = node->teestate; + estate = ((Plan *) node)->state; + teeState = node->teestate; - dir = estate->es_direction; - - /* XXX doesn't handle backwards direction yet */ + dir = estate->es_direction; - if (parent == node->leftParent) { - if (teeState->tee_leftScanDesc) + /* XXX doesn't handle backwards direction yet */ + + if (parent == node->leftParent) + { + if (teeState->tee_leftScanDesc) + { + heap_rescan(teeState->tee_leftScanDesc, + ScanDirectionIsBackward(dir), + NULL); + teeState->tee_leftPlace = 0; + } + } + else { - heap_rescan(teeState->tee_leftScanDesc, - ScanDirectionIsBackward(dir), - NULL); - teeState->tee_leftPlace = 0; + if (teeState->tee_rightScanDesc) + { + heap_rescan(teeState->tee_leftScanDesc, + ScanDirectionIsBackward(dir), + NULL); + teeState->tee_rightPlace = 0; + } } - } - else - { - if (teeState->tee_rightScanDesc) - { - heap_rescan(teeState->tee_leftScanDesc, - ScanDirectionIsBackward(dir), - NULL); - teeState->tee_rightPlace = 0; - } - } } /* --------------------------------------------------------------------- - * ExecEndTee + * ExecEndTee * - * End the Tee node, and free up any storage + * End the Tee node, and free up any storage * since a Tee node can be downstream of multiple parent nodes, * only free when both parents are done * -------------------------------------------------------------------- */ -void -ExecEndTee(Tee* node, Plan* parent) +void +ExecEndTee(Tee * node, Plan * parent) { - EState *estate; - TeeState *teeState; - int leftPlace, rightPlace, lastPlace; - Relation bufferRel; - MemoryContext orig; - - estate = ((Plan*)node)->state; - teeState = node->teestate; - leftPlace = teeState->tee_leftPlace; - rightPlace = teeState->tee_rightPlace; - lastPlace = teeState->tee_lastPlace; - - if (!node->leftParent || parent == node->leftParent) - leftPlace = -1; - - if (!node->rightParent || parent == node->rightParent) - rightPlace = -1; - - if (parent == (Plan*)node) - rightPlace = leftPlace = -1; - - teeState->tee_leftPlace = leftPlace; - teeState->tee_rightPlace = rightPlace; - if ( (leftPlace == -1) && (rightPlace == -1) ) - { - /* remove the temporary relations */ - /* and close the scan descriptors */ - - bufferRel = teeState->tee_bufferRel; - if (bufferRel) { - heap_destroyr(bufferRel); - teeState->tee_bufferRel = NULL; - if (teeState->tee_mcxt) { - orig = CurrentMemoryContext; - MemoryContextSwitchTo(teeState->tee_mcxt); - } - else - orig = 0; - - if (teeState->tee_leftScanDesc) - { - heap_endscan(teeState->tee_leftScanDesc); - teeState->tee_leftScanDesc = NULL; - } - if (teeState->tee_rightScanDesc) - { - heap_endscan(teeState->tee_rightScanDesc); - teeState->tee_rightScanDesc = NULL; - } - - if (teeState->tee_mcxt) { - MemoryContextSwitchTo(orig); - teeState->tee_mcxt = NULL; - } + EState *estate; + TeeState *teeState; + int leftPlace, + rightPlace, + lastPlace; + Relation bufferRel; + MemoryContext orig; + + estate = ((Plan *) node)->state; + teeState = node->teestate; + leftPlace = teeState->tee_leftPlace; + rightPlace = teeState->tee_rightPlace; + lastPlace = teeState->tee_lastPlace; + + if (!node->leftParent || parent == node->leftParent) + leftPlace = -1; + + if (!node->rightParent || parent == node->rightParent) + rightPlace = -1; + + if (parent == (Plan *) node) + rightPlace = leftPlace = -1; + + teeState->tee_leftPlace = leftPlace; + teeState->tee_rightPlace = rightPlace; + if ((leftPlace == -1) && (rightPlace == -1)) + { + /* remove the temporary relations */ + /* and close the scan descriptors */ + + bufferRel = teeState->tee_bufferRel; + if (bufferRel) + { + heap_destroyr(bufferRel); + teeState->tee_bufferRel = NULL; + if (teeState->tee_mcxt) + { + orig = CurrentMemoryContext; + MemoryContextSwitchTo(teeState->tee_mcxt); + } + else + orig = 0; + + if (teeState->tee_leftScanDesc) + { + heap_endscan(teeState->tee_leftScanDesc); + teeState->tee_leftScanDesc = NULL; + } + if (teeState->tee_rightScanDesc) + { + heap_endscan(teeState->tee_rightScanDesc); + teeState->tee_rightScanDesc = NULL; + } + + if (teeState->tee_mcxt) + { + MemoryContextSwitchTo(orig); + teeState->tee_mcxt = NULL; + } + } } - } - -} +} diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index 65c3bea76e0..75e40ccad96 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -1,25 +1,25 @@ /*------------------------------------------------------------------------- * * nodeUnique.c-- - * Routines to handle unique'ing of queries where appropriate + * Routines to handle unique'ing of queries where appropriate * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.7 1997/08/26 23:31:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.8 1997/09/07 04:41:50 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecUnique - generate a unique'd temporary relation - * ExecInitUnique - initialize node and subnodes.. - * ExecEndUnique - shutdown node and subnodes + * ExecUnique - generate a unique'd temporary relation + * ExecInitUnique - initialize node and subnodes.. + * ExecEndUnique - shutdown node and subnodes * * NOTES - * Assumes tuples returned from subplan arrive in - * sorted order. + * Assumes tuples returned from subplan arrive in + * sorted order. * */ #include <string.h> @@ -31,296 +31,318 @@ #include "executor/nodeUnique.h" #include "optimizer/clauses.h" #include "access/heapam.h" -#include "access/printtup.h" /* for typtoout() */ -#include "utils/builtins.h" /* for namecpy()*/ +#include "access/printtup.h" /* for typtoout() */ +#include "utils/builtins.h" /* for namecpy() */ /* ---------------------------------------------------------------- - * ExecIdenticalTuples + * ExecIdenticalTuples * - * This is a hack function used by ExecUnique to see if - * two tuples are identical. This should be provided - * by the heap tuple code but isn't. The real problem - * is that we assume we can byte compare tuples to determine - * if they are "equal". In fact, if we have user defined - * types there may be problems because it's possible that - * an ADT may have multiple representations with the - * same ADT value. -cim + * This is a hack function used by ExecUnique to see if + * two tuples are identical. This should be provided + * by the heap tuple code but isn't. The real problem + * is that we assume we can byte compare tuples to determine + * if they are "equal". In fact, if we have user defined + * types there may be problems because it's possible that + * an ADT may have multiple representations with the + * same ADT value. -cim * ---------------------------------------------------------------- */ -static bool /* true if tuples are identical, false otherwise */ -ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2) +static bool /* true if tuples are identical, false + * otherwise */ +ExecIdenticalTuples(TupleTableSlot * t1, TupleTableSlot * t2) { - HeapTuple h1; - HeapTuple h2; - char *d1; - char *d2; - int len; - - h1 = t1->val; - h2 = t2->val; - - /* ---------------- - * if tuples aren't the same length then they are - * obviously different (one may have null attributes). - * ---------------- - */ - if (h1->t_len != h2->t_len) - return false; - - /* ---------------- - * if the tuples have different header offsets then - * they are different. This will prevent us from returning - * true when comparing tuples of one attribute where one of - * two we're looking at is null (t_len - t_hoff == 0). - * THE t_len FIELDS CAN BE THE SAME IN THIS CASE!! - * ---------------- - */ - if (h1->t_hoff != h2->t_hoff) - return false; - - /* ---------------- - * ok, now get the pointers to the data and the - * size of the attribute portion of the tuple. - * ---------------- - */ - d1 = (char *) GETSTRUCT(h1); - d2 = (char *) GETSTRUCT(h2); - len = (int) h1->t_len - (int) h1->t_hoff; - - /* ---------------- - * byte compare the data areas and return the result. - * ---------------- - */ - if (memcmp(d1, d2, len) != 0) - return false; - - return true; + HeapTuple h1; + HeapTuple h2; + char *d1; + char *d2; + int len; + + h1 = t1->val; + h2 = t2->val; + + /* ---------------- + * if tuples aren't the same length then they are + * obviously different (one may have null attributes). + * ---------------- + */ + if (h1->t_len != h2->t_len) + return false; + + /* ---------------- + * if the tuples have different header offsets then + * they are different. This will prevent us from returning + * true when comparing tuples of one attribute where one of + * two we're looking at is null (t_len - t_hoff == 0). + * THE t_len FIELDS CAN BE THE SAME IN THIS CASE!! + * ---------------- + */ + if (h1->t_hoff != h2->t_hoff) + return false; + + /* ---------------- + * ok, now get the pointers to the data and the + * size of the attribute portion of the tuple. + * ---------------- + */ + d1 = (char *) GETSTRUCT(h1); + d2 = (char *) GETSTRUCT(h2); + len = (int) h1->t_len - (int) h1->t_hoff; + + /* ---------------- + * byte compare the data areas and return the result. + * ---------------- + */ + if (memcmp(d1, d2, len) != 0) + return false; + + return true; } /* ---------------------------------------------------------------- - * ExecUnique + * ExecUnique * - * This is a very simple node which filters out duplicate - * tuples from a stream of sorted tuples from a subplan. + * This is a very simple node which filters out duplicate + * tuples from a stream of sorted tuples from a subplan. * - * XXX see comments below regarding freeing tuples. + * XXX see comments below regarding freeing tuples. * ---------------------------------------------------------------- */ -TupleTableSlot * /* return: a tuple or NULL */ -ExecUnique(Unique *node) +TupleTableSlot * /* return: a tuple or NULL */ +ExecUnique(Unique * node) { - UniqueState *uniquestate; - TupleTableSlot *resultTupleSlot; - TupleTableSlot *slot; - Plan *outerPlan; - char *uniqueAttr; - AttrNumber uniqueAttrNum; - TupleDesc tupDesc; - Oid typoutput; - - /* ---------------- - * get information from the node - * ---------------- - */ - uniquestate = node->uniquestate; - outerPlan = outerPlan((Plan *) node); - resultTupleSlot = uniquestate->cs_ResultTupleSlot; - uniqueAttr = node->uniqueAttr; - uniqueAttrNum = node->uniqueAttrNum; - - if (uniqueAttr) { - tupDesc = ExecGetResultType(uniquestate); - typoutput = typtoout((Oid)tupDesc->attrs[uniqueAttrNum-1]->atttypid); - } - else { /* keep compiler quiet */ - tupDesc = NULL; - typoutput = 0; - } - - /* ---------------- - * now loop, returning only non-duplicate tuples. - * We assume that the tuples arrive in sorted order - * so we can detect duplicates easily. - * ---------------- - */ - for (;;) { + UniqueState *uniquestate; + TupleTableSlot *resultTupleSlot; + TupleTableSlot *slot; + Plan *outerPlan; + char *uniqueAttr; + AttrNumber uniqueAttrNum; + TupleDesc tupDesc; + Oid typoutput; + /* ---------------- - * fetch a tuple from the outer subplan + * get information from the node * ---------------- */ - slot = ExecProcNode(outerPlan, (Plan*)node); - if (TupIsNull(slot)) - return NULL; - + uniquestate = node->uniquestate; + outerPlan = outerPlan((Plan *) node); + resultTupleSlot = uniquestate->cs_ResultTupleSlot; + uniqueAttr = node->uniqueAttr; + uniqueAttrNum = node->uniqueAttrNum; + + if (uniqueAttr) + { + tupDesc = ExecGetResultType(uniquestate); + typoutput = typtoout((Oid) tupDesc->attrs[uniqueAttrNum - 1]->atttypid); + } + else + { /* keep compiler quiet */ + tupDesc = NULL; + typoutput = 0; + } + /* ---------------- - * we use the result tuple slot to hold our saved tuples. - * if we haven't a saved tuple to compare our new tuple with, - * then we exit the loop. This new tuple as the saved tuple - * the next time we get here. + * now loop, returning only non-duplicate tuples. + * We assume that the tuples arrive in sorted order + * so we can detect duplicates easily. * ---------------- */ - if (TupIsNull(resultTupleSlot)) - break; - + for (;;) + { + /* ---------------- + * fetch a tuple from the outer subplan + * ---------------- + */ + slot = ExecProcNode(outerPlan, (Plan *) node); + if (TupIsNull(slot)) + return NULL; + + /* ---------------- + * we use the result tuple slot to hold our saved tuples. + * if we haven't a saved tuple to compare our new tuple with, + * then we exit the loop. This new tuple as the saved tuple + * the next time we get here. + * ---------------- + */ + if (TupIsNull(resultTupleSlot)) + break; + + /* ---------------- + * now test if the new tuple and the previous + * tuple match. If so then we loop back and fetch + * another new tuple from the subplan. + * ---------------- + */ + + if (uniqueAttr) + { + + /* + * to check equality, we check to see if the typoutput of the + * attributes are equal + */ + bool isNull1, + isNull2; + char *attr1, + *attr2; + char *val1, + *val2; + + attr1 = heap_getattr(slot->val, InvalidBuffer, + uniqueAttrNum, tupDesc, &isNull1); + attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer, + uniqueAttrNum, tupDesc, &isNull2); + + if (isNull1 == isNull2) + { + if (isNull1) /* both are null, they are equal */ + continue; + val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum - 1]->atttypid)); + val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum - 1]->atttypid)); + + /* + * now, val1 and val2 are ascii representations so we can + * use strcmp for comparison + */ + if (strcmp(val1, val2) == 0) /* they are equal */ + continue; + else + break; + } + else +/* one is null and the other isn't, they aren't equal */ + break; + + } + else + { + if (!ExecIdenticalTuples(slot, resultTupleSlot)) + break; + } + + } + /* ---------------- - * now test if the new tuple and the previous - * tuple match. If so then we loop back and fetch - * another new tuple from the subplan. + * we have a new tuple different from the previous saved tuple + * so we save it in the saved tuple slot. We copy the tuple + * so we don't increment the buffer ref count. * ---------------- */ + ExecStoreTuple(heap_copytuple(slot->val), + resultTupleSlot, + InvalidBuffer, + true); - if (uniqueAttr) { - /* to check equality, we check to see if the typoutput - of the attributes are equal */ - bool isNull1,isNull2; - char *attr1, *attr2; - char *val1, *val2; - - attr1 = heap_getattr(slot->val, InvalidBuffer, - uniqueAttrNum, tupDesc,&isNull1); - attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer, - uniqueAttrNum, tupDesc,&isNull2); - - if (isNull1 == isNull2) { - if (isNull1) /* both are null, they are equal */ - continue; - val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum-1]->atttypid)); - val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum-1]->atttypid)); - /* now, val1 and val2 are ascii representations so we can - use strcmp for comparison */ - if (strcmp(val1,val2) == 0) /* they are equal */ - continue; - else - break; - } - else /* one is null and the other isn't, they aren't equal */ - break; - - } - else { - if (! ExecIdenticalTuples(slot, resultTupleSlot)) - break; - } - - } - - /* ---------------- - * we have a new tuple different from the previous saved tuple - * so we save it in the saved tuple slot. We copy the tuple - * so we don't increment the buffer ref count. - * ---------------- - */ - ExecStoreTuple(heap_copytuple(slot->val), - resultTupleSlot, - InvalidBuffer, - true); - - return resultTupleSlot; + return resultTupleSlot; } /* ---------------------------------------------------------------- - * ExecInitUnique + * ExecInitUnique * - * This initializes the unique node state structures and - * the node's subplan. + * This initializes the unique node state structures and + * the node's subplan. * ---------------------------------------------------------------- */ -bool /* return: initialization status */ -ExecInitUnique(Unique *node, EState *estate, Plan *parent) +bool /* return: initialization status */ +ExecInitUnique(Unique * node, EState * estate, Plan * parent) { - UniqueState *uniquestate; - Plan *outerPlan; - char *uniqueAttr; - - /* ---------------- - * assign execution state to node - * ---------------- - */ - node->plan.state = estate; - - /* ---------------- - * create new UniqueState for node - * ---------------- - */ - uniquestate = makeNode(UniqueState); - node->uniquestate = uniquestate; - uniqueAttr = node->uniqueAttr; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * - * Unique nodes have no ExprContext initialization because - * they never call ExecQual or ExecTargetList. - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, uniquestate, parent); - + UniqueState *uniquestate; + Plan *outerPlan; + char *uniqueAttr; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create new UniqueState for node + * ---------------- + */ + uniquestate = makeNode(UniqueState); + node->uniquestate = uniquestate; + uniqueAttr = node->uniqueAttr; + + /* ---------------- + * Miscellanious initialization + * + * + assign node's base_id + * + assign debugging hooks and + * + * Unique nodes have no ExprContext initialization because + * they never call ExecQual or ExecTargetList. + * ---------------- + */ + ExecAssignNodeBaseInfo(estate, uniquestate, parent); + #define UNIQUE_NSLOTS 1 - /* ------------ - * Tuple table initialization - * ------------ - */ - ExecInitResultTupleSlot(estate, uniquestate); - - /* ---------------- - * then initialize outer plan - * ---------------- - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* ---------------- - * unique nodes do no projections, so initialize - * projection info for this node appropriately - * ---------------- - */ - ExecAssignResultTypeFromOuterPlan((Plan *)node,uniquestate); - uniquestate->cs_ProjInfo = NULL; - - if (uniqueAttr) { - TupleDesc tupDesc; - int i = 0; - - tupDesc = ExecGetResultType(uniquestate); - /* the parser should have ensured that uniqueAttr is a legal attribute name*/ - while ( strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0) - i++; - node->uniqueAttrNum = i+1; /* attribute numbers start from 1 */ - } - else - node->uniqueAttrNum = InvalidAttrNumber; - - /* ---------------- - * all done. - * ---------------- - */ - return TRUE; + /* ------------ + * Tuple table initialization + * ------------ + */ + ExecInitResultTupleSlot(estate, uniquestate); + + /* ---------------- + * then initialize outer plan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * unique nodes do no projections, so initialize + * projection info for this node appropriately + * ---------------- + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, uniquestate); + uniquestate->cs_ProjInfo = NULL; + + if (uniqueAttr) + { + TupleDesc tupDesc; + int i = 0; + + tupDesc = ExecGetResultType(uniquestate); + + /* + * the parser should have ensured that uniqueAttr is a legal + * attribute name + */ + while (strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0) + i++; + node->uniqueAttrNum = i + 1; /* attribute numbers start from 1 */ + } + else + node->uniqueAttrNum = InvalidAttrNumber; + + /* ---------------- + * all done. + * ---------------- + */ + return TRUE; } int -ExecCountSlotsUnique(Unique *node) +ExecCountSlotsUnique(Unique * node) { - return ExecCountSlotsNode(outerPlan(node)) + + return ExecCountSlotsNode(outerPlan(node)) + ExecCountSlotsNode(innerPlan(node)) + - UNIQUE_NSLOTS; + UNIQUE_NSLOTS; } /* ---------------------------------------------------------------- - * ExecEndUnique + * ExecEndUnique * - * This shuts down the subplan and frees resources allocated - * to this node. + * This shuts down the subplan and frees resources allocated + * to this node. * ---------------------------------------------------------------- */ void -ExecEndUnique(Unique *node) +ExecEndUnique(Unique * node) { - UniqueState *uniquestate; - - uniquestate = node->uniquestate; - ExecEndNode(outerPlan((Plan *) node), (Plan*)node); - ExecClearTuple(uniquestate->cs_ResultTupleSlot); -} + UniqueState *uniquestate; + + uniquestate = node->uniquestate; + ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + ExecClearTuple(uniquestate->cs_ResultTupleSlot); +} diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 74ace81171a..1d05a752d24 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * spi.c-- - * Server Programming Interface + * Server Programming Interface * *------------------------------------------------------------------------- */ @@ -9,440 +9,447 @@ #include "access/printtup.h" #include "fmgr.h" -typedef struct { - QueryTreeList *qtlist; /* malloced */ - uint32 processed; /* by Executor */ - SPITupleTable *tuptable; - Portal portal; /* portal per procedure */ - MemoryContext savedcxt; - CommandId savedId; -} _SPI_connection; - -static Portal _SPI_portal = (Portal) NULL; +typedef struct +{ + QueryTreeList *qtlist; /* malloced */ + uint32 processed; /* by Executor */ + SPITupleTable *tuptable; + Portal portal; /* portal per procedure */ + MemoryContext savedcxt; + CommandId savedId; +} _SPI_connection; + +static Portal _SPI_portal = (Portal) NULL; static _SPI_connection *_SPI_stack = NULL; static _SPI_connection *_SPI_current = NULL; -static int _SPI_connected = -1; -static int _SPI_curid = -1; +static int _SPI_connected = -1; +static int _SPI_curid = -1; + +uint32 SPI_processed = 0; +SPITupleTable *SPI_tuptable; +int SPI_result; -uint32 SPI_processed = 0; -SPITupleTable *SPI_tuptable; -int SPI_result; +void spi_printtup(HeapTuple tuple, TupleDesc tupdesc); -void spi_printtup (HeapTuple tuple, TupleDesc tupdesc); +typedef struct +{ + QueryTreeList *qtlist; + List *ptlist; + int nargs; + Oid *argtypes; +} _SPI_plan; -typedef struct { - QueryTreeList *qtlist; - List *ptlist; - int nargs; - Oid *argtypes; -} _SPI_plan; +static int _SPI_execute(char *src, int tcount, _SPI_plan * plan); +static int _SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount); -static int _SPI_execute (char *src, int tcount, _SPI_plan *plan); -static int _SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount); #if 0 -static void _SPI_fetch (FetchStmt *stmt); +static void _SPI_fetch(FetchStmt * stmt); + #endif -static int _SPI_execute_plan (_SPI_plan *plan, - char **Values, char *Nulls, int tcount); +static int +_SPI_execute_plan(_SPI_plan * plan, + char **Values, char *Nulls, int tcount); -static _SPI_plan *_SPI_copy_plan (_SPI_plan *plan, bool local); +static _SPI_plan *_SPI_copy_plan(_SPI_plan * plan, bool local); -static int _SPI_begin_call (bool execmem); -static int _SPI_end_call (bool procmem); -static MemoryContext _SPI_execmem (void); -static MemoryContext _SPI_procmem (void); -static bool _SPI_checktuples (bool isRetrieveIntoRelation); +static int _SPI_begin_call(bool execmem); +static int _SPI_end_call(bool procmem); +static MemoryContext _SPI_execmem(void); +static MemoryContext _SPI_procmem(void); +static bool _SPI_checktuples(bool isRetrieveIntoRelation); #ifdef SPI_EXECUTOR_STATS -extern int ShowExecutorStats; -extern void ResetUsage (void); -extern void ShowUsage (void); +extern int ShowExecutorStats; +extern void ResetUsage(void); +extern void ShowUsage(void); + #endif int -SPI_connect () +SPI_connect() { - char pname[64]; - PortalVariableMemory pvmem; - - /* - * It's possible on startup and after commit/abort. - * In future we'll catch commit/abort in some way... - */ - strcpy (pname, "<SPI manager>"); - _SPI_portal = GetPortalByName (pname); - if ( !PortalIsValid (_SPI_portal) ) - { - if ( _SPI_stack != NULL ) /* there was abort */ - free (_SPI_stack); - _SPI_current = _SPI_stack = NULL; - _SPI_connected = _SPI_curid = -1; - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_portal = CreatePortal (pname); - if ( !PortalIsValid (_SPI_portal) ) - elog (FATAL, "SPI_connect: global initialization failed"); - } - - /* - * When procedure called by Executor _SPI_curid expected to be - * equal to _SPI_connected - */ - if ( _SPI_curid != _SPI_connected ) - return (SPI_ERROR_CONNECT); - - if ( _SPI_stack == NULL ) - { - if ( _SPI_connected != -1 ) - elog (FATAL, "SPI_connect: no connection(s) expected"); - _SPI_stack = (_SPI_connection *) malloc (sizeof (_SPI_connection)); - } - else - { - if ( _SPI_connected <= -1 ) - elog (FATAL, "SPI_connect: some connection(s) expected"); - _SPI_stack = (_SPI_connection *) realloc (_SPI_stack, - (_SPI_connected + 1) * sizeof (_SPI_connection)); - } - /* - * We' returning to procedure where _SPI_curid == _SPI_connected - 1 - */ - _SPI_connected++; - - _SPI_current = &(_SPI_stack[_SPI_connected]); - _SPI_current->qtlist = NULL; - _SPI_current->processed = 0; - _SPI_current->tuptable = NULL; - - /* Create Portal for this procedure ... */ - sprintf (pname, "<SPI %d>", _SPI_connected); - _SPI_current->portal = CreatePortal (pname); - if ( !PortalIsValid (_SPI_current->portal) ) - elog (FATAL, "SPI_connect: initialization failed"); - - /* ... and switch to Portal' Variable memory - procedure' context */ - pvmem = PortalGetVariableMemory (_SPI_current->portal); - _SPI_current->savedcxt = MemoryContextSwitchTo ((MemoryContext)pvmem); - - _SPI_current->savedId = GetScanCommandId (); - SetScanCommandId (GetCurrentCommandId ()); - - return (SPI_OK_CONNECT); - + char pname[64]; + PortalVariableMemory pvmem; + + /* + * It's possible on startup and after commit/abort. In future we'll + * catch commit/abort in some way... + */ + strcpy(pname, "<SPI manager>"); + _SPI_portal = GetPortalByName(pname); + if (!PortalIsValid(_SPI_portal)) + { + if (_SPI_stack != NULL) /* there was abort */ + free(_SPI_stack); + _SPI_current = _SPI_stack = NULL; + _SPI_connected = _SPI_curid = -1; + SPI_processed = 0; + SPI_tuptable = NULL; + _SPI_portal = CreatePortal(pname); + if (!PortalIsValid(_SPI_portal)) + elog(FATAL, "SPI_connect: global initialization failed"); + } + + /* + * When procedure called by Executor _SPI_curid expected to be equal + * to _SPI_connected + */ + if (_SPI_curid != _SPI_connected) + return (SPI_ERROR_CONNECT); + + if (_SPI_stack == NULL) + { + if (_SPI_connected != -1) + elog(FATAL, "SPI_connect: no connection(s) expected"); + _SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection)); + } + else + { + if (_SPI_connected <= -1) + elog(FATAL, "SPI_connect: some connection(s) expected"); + _SPI_stack = (_SPI_connection *) realloc(_SPI_stack, + (_SPI_connected + 1) * sizeof(_SPI_connection)); + } + + /* + * We' returning to procedure where _SPI_curid == _SPI_connected - 1 + */ + _SPI_connected++; + + _SPI_current = &(_SPI_stack[_SPI_connected]); + _SPI_current->qtlist = NULL; + _SPI_current->processed = 0; + _SPI_current->tuptable = NULL; + + /* Create Portal for this procedure ... */ + sprintf(pname, "<SPI %d>", _SPI_connected); + _SPI_current->portal = CreatePortal(pname); + if (!PortalIsValid(_SPI_current->portal)) + elog(FATAL, "SPI_connect: initialization failed"); + + /* ... and switch to Portal' Variable memory - procedure' context */ + pvmem = PortalGetVariableMemory(_SPI_current->portal); + _SPI_current->savedcxt = MemoryContextSwitchTo((MemoryContext) pvmem); + + _SPI_current->savedId = GetScanCommandId(); + SetScanCommandId(GetCurrentCommandId()); + + return (SPI_OK_CONNECT); + } int -SPI_finish () +SPI_finish() { - int res; - - res = _SPI_begin_call (false); /* live in procedure memory */ - if ( res < 0 ) - return (res); - - /* Restore memory context as it was before procedure call */ - MemoryContextSwitchTo (_SPI_current->savedcxt); - PortalDestroy (&(_SPI_current->portal)); - - SetScanCommandId (_SPI_current->savedId); - - /* - * After _SPI_begin_call _SPI_connected == _SPI_curid. - * Now we are closing connection to SPI and returning to upper - * Executor and so _SPI_connected must be equal to _SPI_curid. - */ - _SPI_connected--; - _SPI_curid--; - if ( _SPI_connected == -1 ) - { - free (_SPI_stack); - _SPI_stack = NULL; - } - else - { - _SPI_stack = (_SPI_connection *) realloc (_SPI_stack, - (_SPI_connected + 1) * sizeof (_SPI_connection)); - _SPI_current = &(_SPI_stack[_SPI_connected]); - } - - return (SPI_OK_FINISH); - + int res; + + res = _SPI_begin_call(false); /* live in procedure memory */ + if (res < 0) + return (res); + + /* Restore memory context as it was before procedure call */ + MemoryContextSwitchTo(_SPI_current->savedcxt); + PortalDestroy(&(_SPI_current->portal)); + + SetScanCommandId(_SPI_current->savedId); + + /* + * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are + * closing connection to SPI and returning to upper Executor and so + * _SPI_connected must be equal to _SPI_curid. + */ + _SPI_connected--; + _SPI_curid--; + if (_SPI_connected == -1) + { + free(_SPI_stack); + _SPI_stack = NULL; + } + else + { + _SPI_stack = (_SPI_connection *) realloc(_SPI_stack, + (_SPI_connected + 1) * sizeof(_SPI_connection)); + _SPI_current = &(_SPI_stack[_SPI_connected]); + } + + return (SPI_OK_FINISH); + } int -SPI_exec (char *src, int tcount) +SPI_exec(char *src, int tcount) { - int res; - - if ( src == NULL || tcount < 0 ) - return (SPI_ERROR_ARGUMENT); - - res = _SPI_begin_call (true); - if ( res < 0 ) - return (res); - - res = _SPI_execute (src, tcount, NULL); - - _SPI_end_call (true); - return (res); + int res; + + if (src == NULL || tcount < 0) + return (SPI_ERROR_ARGUMENT); + + res = _SPI_begin_call(true); + if (res < 0) + return (res); + + res = _SPI_execute(src, tcount, NULL); + + _SPI_end_call(true); + return (res); } -int -SPI_execp (void *plan, char **Values, char *Nulls, int tcount) +int +SPI_execp(void *plan, char **Values, char *Nulls, int tcount) { - int res; - - if ( plan == NULL || tcount < 0 ) - return (SPI_ERROR_ARGUMENT); - - if ( ((_SPI_plan *)plan)->nargs > 0 && - ( Values == NULL || Nulls == NULL ) ) - return (SPI_ERROR_PARAM); - - res = _SPI_begin_call (true); - if ( res < 0 ) - return (res); - - res = _SPI_execute_plan ((_SPI_plan *)plan, Values, Nulls, tcount); - - _SPI_end_call (true); - return (res); + int res; + + if (plan == NULL || tcount < 0) + return (SPI_ERROR_ARGUMENT); + + if (((_SPI_plan *) plan)->nargs > 0 && + (Values == NULL || Nulls == NULL)) + return (SPI_ERROR_PARAM); + + res = _SPI_begin_call(true); + if (res < 0) + return (res); + + res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount); + + _SPI_end_call(true); + return (res); } -void * -SPI_prepare (char *src, int nargs, Oid *argtypes) +void * +SPI_prepare(char *src, int nargs, Oid * argtypes) { - _SPI_plan *plan; - - if ( nargs < 0 || ( nargs > 0 && argtypes == NULL ) ) - { - SPI_result = SPI_ERROR_ARGUMENT; - return (NULL); - } - - SPI_result = _SPI_begin_call (true); - if ( SPI_result < 0 ) - return (NULL); - - plan = (_SPI_plan *) palloc (sizeof (_SPI_plan)); /* Executor context */ - plan->argtypes = argtypes; - plan->nargs = nargs; - - SPI_result = _SPI_execute (src, 0, plan); - - if ( SPI_result >= 0 ) /* copy plan to local space */ - plan = _SPI_copy_plan (plan, true); - else - plan = NULL; - - _SPI_end_call (true); - - return ((void *)plan); - + _SPI_plan *plan; + + if (nargs < 0 || (nargs > 0 && argtypes == NULL)) + { + SPI_result = SPI_ERROR_ARGUMENT; + return (NULL); + } + + SPI_result = _SPI_begin_call(true); + if (SPI_result < 0) + return (NULL); + + plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */ + plan->argtypes = argtypes; + plan->nargs = nargs; + + SPI_result = _SPI_execute(src, 0, plan); + + if (SPI_result >= 0) /* copy plan to local space */ + plan = _SPI_copy_plan(plan, true); + else + plan = NULL; + + _SPI_end_call(true); + + return ((void *) plan); + } -void * -SPI_saveplan (void *plan) +void * +SPI_saveplan(void *plan) { - _SPI_plan *newplan; - - if ( plan == NULL ) - { - SPI_result = SPI_ERROR_ARGUMENT; - return (NULL); - } - - SPI_result = _SPI_begin_call (false); /* don't change context */ - if ( SPI_result < 0 ) - return (NULL); - - newplan = _SPI_copy_plan ((_SPI_plan *)plan, false); - - _SPI_curid--; - SPI_result = 0; - - return ((void *)newplan); - + _SPI_plan *newplan; + + if (plan == NULL) + { + SPI_result = SPI_ERROR_ARGUMENT; + return (NULL); + } + + SPI_result = _SPI_begin_call(false); /* don't change context */ + if (SPI_result < 0) + return (NULL); + + newplan = _SPI_copy_plan((_SPI_plan *) plan, false); + + _SPI_curid--; + SPI_result = 0; + + return ((void *) newplan); + } int -SPI_fnumber (TupleDesc tupdesc, char *fname) +SPI_fnumber(TupleDesc tupdesc, char *fname) { - int res; - - if ( _SPI_curid + 1 != _SPI_connected ) - return (SPI_ERROR_UNCONNECTED); - - for (res = 0; res < tupdesc->natts; res++) - { - if ( strcmp (tupdesc->attrs[res]->attname.data, fname) == 0 ) - return (res + 1); - } - - return (SPI_ERROR_NOATTRIBUTE); + int res; + + if (_SPI_curid + 1 != _SPI_connected) + return (SPI_ERROR_UNCONNECTED); + + for (res = 0; res < tupdesc->natts; res++) + { + if (strcmp(tupdesc->attrs[res]->attname.data, fname) == 0) + return (res + 1); + } + + return (SPI_ERROR_NOATTRIBUTE); } -char * -SPI_getvalue (HeapTuple tuple, TupleDesc tupdesc, int fnumber) +char * +SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { - char *val; - bool isnull; - Oid foutoid; - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - if ( tuple->t_natts < fnumber || fnumber <= 0 ) - return (NULL); - - val = heap_getattr (tuple, InvalidBuffer, fnumber, tupdesc, &isnull); - if ( isnull ) - return (NULL); - foutoid = typtoout ((Oid) tupdesc->attrs[fnumber - 1]->atttypid); - if ( !OidIsValid (foutoid) ) - { - SPI_result = SPI_ERROR_NOOUTFUNC; - return (NULL); - } - - return (fmgr (foutoid, val, gettypelem (tupdesc->attrs[fnumber - 1]->atttypid))); + char *val; + bool isnull; + Oid foutoid; + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + if (tuple->t_natts < fnumber || fnumber <= 0) + return (NULL); + + val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, &isnull); + if (isnull) + return (NULL); + foutoid = typtoout((Oid) tupdesc->attrs[fnumber - 1]->atttypid); + if (!OidIsValid(foutoid)) + { + SPI_result = SPI_ERROR_NOOUTFUNC; + return (NULL); + } + + return (fmgr(foutoid, val, gettypelem(tupdesc->attrs[fnumber - 1]->atttypid))); } -char * -SPI_getbinval (HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull) +char * +SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool * isnull) { - char *val; - - *isnull = true; - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - if ( tuple->t_natts < fnumber || fnumber <= 0 ) - return (NULL); - - val = heap_getattr (tuple, InvalidBuffer, fnumber, tupdesc, isnull); - - return (val); + char *val; + + *isnull = true; + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + if (tuple->t_natts < fnumber || fnumber <= 0) + return (NULL); + + val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, isnull); + + return (val); } -char * -SPI_gettype (TupleDesc tupdesc, int fnumber) +char * +SPI_gettype(TupleDesc tupdesc, int fnumber) { - HeapTuple typeTuple; - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - if ( tupdesc->natts < fnumber || fnumber <= 0 ) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return (NULL); - } - - typeTuple = SearchSysCacheTuple (TYPOID, - ObjectIdGetDatum (tupdesc->attrs[fnumber - 1]->atttypid), - 0, 0, 0); - - if ( !HeapTupleIsValid (typeTuple) ) - { - SPI_result = SPI_ERROR_TYPUNKNOWN; - return (NULL); - } - - return (pstrdup (((TypeTupleForm) GETSTRUCT (typeTuple))->typname.data)); + HeapTuple typeTuple; + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + if (tupdesc->natts < fnumber || fnumber <= 0) + { + SPI_result = SPI_ERROR_NOATTRIBUTE; + return (NULL); + } + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(tupdesc->attrs[fnumber - 1]->atttypid), + 0, 0, 0); + + if (!HeapTupleIsValid(typeTuple)) + { + SPI_result = SPI_ERROR_TYPUNKNOWN; + return (NULL); + } + + return (pstrdup(((TypeTupleForm) GETSTRUCT(typeTuple))->typname.data)); } Oid -SPI_gettypeid (TupleDesc tupdesc, int fnumber) +SPI_gettypeid(TupleDesc tupdesc, int fnumber) { - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (InvalidOid); - } - - if ( tupdesc->natts < fnumber || fnumber <= 0 ) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return (InvalidOid); - } - - return (tupdesc->attrs[fnumber - 1]->atttypid); + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (InvalidOid); + } + + if (tupdesc->natts < fnumber || fnumber <= 0) + { + SPI_result = SPI_ERROR_NOATTRIBUTE; + return (InvalidOid); + } + + return (tupdesc->attrs[fnumber - 1]->atttypid); } -char * -SPI_getrelname (Relation rel) +char * +SPI_getrelname(Relation rel) { - - SPI_result = 0; - if ( _SPI_curid + 1 != _SPI_connected ) - { - SPI_result = SPI_ERROR_UNCONNECTED; - return (NULL); - } - - return (pstrdup (rel->rd_rel->relname.data)); + + SPI_result = 0; + if (_SPI_curid + 1 != _SPI_connected) + { + SPI_result = SPI_ERROR_UNCONNECTED; + return (NULL); + } + + return (pstrdup(rel->rd_rel->relname.data)); } /* * spi_printtup -- - * store tuple retrieved by Executor into SPITupleTable - * of current SPI procedure + * store tuple retrieved by Executor into SPITupleTable + * of current SPI procedure * */ void -spi_printtup (HeapTuple tuple, TupleDesc tupdesc) +spi_printtup(HeapTuple tuple, TupleDesc tupdesc) { - SPITupleTable *tuptable; - MemoryContext oldcxt; - - /* - * When called by Executor _SPI_curid expected to be - * equal to _SPI_connected - */ - if ( _SPI_curid != _SPI_connected || _SPI_connected < 0 ) - elog (FATAL, "SPI: improper call to spi_printtup"); - if ( _SPI_current != &(_SPI_stack[_SPI_curid]) ) - elog (FATAL, "SPI: stack corrupted in spi_printtup"); - - oldcxt = _SPI_procmem (); /* switch to procedure memory context */ - - tuptable = _SPI_current->tuptable; - if ( tuptable == NULL ) - { - _SPI_current->tuptable = tuptable = (SPITupleTable *) - palloc (sizeof (SPITupleTable)); - tuptable->alloced = tuptable->free = 128; - tuptable->vals = (HeapTuple *) palloc (tuptable->alloced * sizeof (HeapTuple)); - tuptable->tupdesc = CreateTupleDescCopy (tupdesc); - } - else if ( tuptable->free == 0 ) - { - tuptable->free = 256; - tuptable->alloced += tuptable->free; - tuptable->vals = (HeapTuple *) repalloc (tuptable->vals, - tuptable->alloced * sizeof (HeapTuple)); - } - - tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple (tuple); - (tuptable->free)--; - - MemoryContextSwitchTo (oldcxt); - return; + SPITupleTable *tuptable; + MemoryContext oldcxt; + + /* + * When called by Executor _SPI_curid expected to be equal to + * _SPI_connected + */ + if (_SPI_curid != _SPI_connected || _SPI_connected < 0) + elog(FATAL, "SPI: improper call to spi_printtup"); + if (_SPI_current != &(_SPI_stack[_SPI_curid])) + elog(FATAL, "SPI: stack corrupted in spi_printtup"); + + oldcxt = _SPI_procmem(); /* switch to procedure memory context */ + + tuptable = _SPI_current->tuptable; + if (tuptable == NULL) + { + _SPI_current->tuptable = tuptable = (SPITupleTable *) + palloc(sizeof(SPITupleTable)); + tuptable->alloced = tuptable->free = 128; + tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple)); + tuptable->tupdesc = CreateTupleDescCopy(tupdesc); + } + else if (tuptable->free == 0) + { + tuptable->free = 256; + tuptable->alloced += tuptable->free; + tuptable->vals = (HeapTuple *) repalloc(tuptable->vals, + tuptable->alloced * sizeof(HeapTuple)); + } + + tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple); + (tuptable->free)--; + + MemoryContextSwitchTo(oldcxt); + return; } /* @@ -450,330 +457,334 @@ spi_printtup (HeapTuple tuple, TupleDesc tupdesc) */ static int -_SPI_execute (char *src, int tcount, _SPI_plan *plan) +_SPI_execute(char *src, int tcount, _SPI_plan * plan) { - QueryTreeList *queryTree_list; - List *planTree_list; - List *ptlist; - QueryDesc *qdesc; - Query *queryTree; - Plan *planTree; - EState *state; - int qlen; - int nargs = 0; - Oid *argtypes = NULL; - int res; - int i; - - /* Increment CommandCounter to see changes made by now */ - CommandCounterIncrement (); - - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - _SPI_current->qtlist = NULL; - - if ( plan ) - { - nargs = plan->nargs; - argtypes = plan->argtypes; - } - ptlist = planTree_list = (List *) - pg_plan (src, argtypes, nargs, &queryTree_list, None); - - _SPI_current->qtlist = queryTree_list; - - qlen = queryTree_list->len; - for (i=0; ;i++) - { - queryTree = (Query*) (queryTree_list->qtrees[i]); - planTree = lfirst(planTree_list); - - planTree_list = lnext (planTree_list); - - if ( queryTree->commandType == CMD_UTILITY ) - { - if ( nodeTag (queryTree->utilityStmt ) == T_CopyStmt ) - { - CopyStmt *stmt = (CopyStmt *)(queryTree->utilityStmt); - - if ( stmt->filename == NULL ) - return (SPI_ERROR_COPY); - } - else if ( nodeTag (queryTree->utilityStmt ) == T_ClosePortalStmt || - nodeTag (queryTree->utilityStmt ) == T_FetchStmt ) - return (SPI_ERROR_CURSOR); - else if ( nodeTag (queryTree->utilityStmt ) == T_TransactionStmt ) - return (SPI_ERROR_TRANSACTION); - res = SPI_OK_UTILITY; - if ( plan == NULL ) - { - ProcessUtility (queryTree->utilityStmt, None); - if ( i < qlen - 1 ) - CommandCounterIncrement (); - else - return (res); - } - else if ( i >= qlen - 1 ) - break; - } - else if ( plan == NULL ) - { - qdesc = CreateQueryDesc (queryTree, planTree, - ( i < qlen - 1 ) ? None : SPI); - state = CreateExecutorState(); - res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount); - if ( res < 0 || i >= qlen - 1 ) - return (res); - CommandCounterIncrement (); + QueryTreeList *queryTree_list; + List *planTree_list; + List *ptlist; + QueryDesc *qdesc; + Query *queryTree; + Plan *planTree; + EState *state; + int qlen; + int nargs = 0; + Oid *argtypes = NULL; + int res; + int i; + + /* Increment CommandCounter to see changes made by now */ + CommandCounterIncrement(); + + SPI_processed = 0; + SPI_tuptable = NULL; + _SPI_current->tuptable = NULL; + _SPI_current->qtlist = NULL; + + if (plan) + { + nargs = plan->nargs; + argtypes = plan->argtypes; } - else + ptlist = planTree_list = (List *) + pg_plan(src, argtypes, nargs, &queryTree_list, None); + + _SPI_current->qtlist = queryTree_list; + + qlen = queryTree_list->len; + for (i = 0;; i++) { - qdesc = CreateQueryDesc (queryTree, planTree, - ( i < qlen - 1 ) ? None : SPI); - res = _SPI_pquery (qdesc, NULL, ( i < qlen - 1 ) ? 0 : tcount); - if ( res < 0 ) - return (res); - if ( i >= qlen - 1 ) - break; - } - } - - plan->qtlist = queryTree_list; - plan->ptlist = ptlist; - - return (res); - + queryTree = (Query *) (queryTree_list->qtrees[i]); + planTree = lfirst(planTree_list); + + planTree_list = lnext(planTree_list); + + if (queryTree->commandType == CMD_UTILITY) + { + if (nodeTag(queryTree->utilityStmt) == T_CopyStmt) + { + CopyStmt *stmt = (CopyStmt *) (queryTree->utilityStmt); + + if (stmt->filename == NULL) + return (SPI_ERROR_COPY); + } + else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt || + nodeTag(queryTree->utilityStmt) == T_FetchStmt) + return (SPI_ERROR_CURSOR); + else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt) + return (SPI_ERROR_TRANSACTION); + res = SPI_OK_UTILITY; + if (plan == NULL) + { + ProcessUtility(queryTree->utilityStmt, None); + if (i < qlen - 1) + CommandCounterIncrement(); + else + return (res); + } + else if (i >= qlen - 1) + break; + } + else if (plan == NULL) + { + qdesc = CreateQueryDesc(queryTree, planTree, + (i < qlen - 1) ? None : SPI); + state = CreateExecutorState(); + res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount); + if (res < 0 || i >= qlen - 1) + return (res); + CommandCounterIncrement(); + } + else + { + qdesc = CreateQueryDesc(queryTree, planTree, + (i < qlen - 1) ? None : SPI); + res = _SPI_pquery(qdesc, NULL, (i < qlen - 1) ? 0 : tcount); + if (res < 0) + return (res); + if (i >= qlen - 1) + break; + } + } + + plan->qtlist = queryTree_list; + plan->ptlist = ptlist; + + return (res); + } static int -_SPI_execute_plan (_SPI_plan *plan, char **Values, char *Nulls, int tcount) +_SPI_execute_plan(_SPI_plan * plan, char **Values, char *Nulls, int tcount) { - QueryTreeList *queryTree_list = plan->qtlist; - List *planTree_list = plan->ptlist; - QueryDesc *qdesc; - Query *queryTree; - Plan *planTree; - EState *state; - int nargs = plan->nargs; - int qlen = queryTree_list->len; - int res; - int i, k; - - /* Increment CommandCounter to see changes made by now */ - CommandCounterIncrement (); - - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - _SPI_current->qtlist = NULL; - - for (i=0; ;i++) - { - queryTree = (Query*) (queryTree_list->qtrees[i]); - planTree = lfirst(planTree_list); - - planTree_list = lnext (planTree_list); - - if ( queryTree->commandType == CMD_UTILITY ) - { - ProcessUtility (queryTree->utilityStmt, None); - if ( i < qlen - 1 ) - CommandCounterIncrement (); - else - return (SPI_OK_UTILITY); - } - else + QueryTreeList *queryTree_list = plan->qtlist; + List *planTree_list = plan->ptlist; + QueryDesc *qdesc; + Query *queryTree; + Plan *planTree; + EState *state; + int nargs = plan->nargs; + int qlen = queryTree_list->len; + int res; + int i, + k; + + /* Increment CommandCounter to see changes made by now */ + CommandCounterIncrement(); + + SPI_processed = 0; + SPI_tuptable = NULL; + _SPI_current->tuptable = NULL; + _SPI_current->qtlist = NULL; + + for (i = 0;; i++) { - qdesc = CreateQueryDesc (queryTree, planTree, - ( i < qlen - 1 ) ? None : SPI); - state = CreateExecutorState(); - if ( nargs > 0 ) - { - ParamListInfo paramLI = (ParamListInfo) palloc ((nargs + 1) * - sizeof (ParamListInfoData)); - state->es_param_list_info = paramLI; - for (k = 0; k < plan->nargs; paramLI++, k++) - { - paramLI->kind = PARAM_NUM; - paramLI->id = k+1; - paramLI->isnull = (Nulls[k] != 0); - paramLI->value = (Datum) Values[k]; - } - paramLI->kind = PARAM_INVALID; - } - else - state->es_param_list_info = NULL; - res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount); - if ( res < 0 || i >= qlen - 1 ) - return (res); - CommandCounterIncrement (); - } - } - - return (res); - + queryTree = (Query *) (queryTree_list->qtrees[i]); + planTree = lfirst(planTree_list); + + planTree_list = lnext(planTree_list); + + if (queryTree->commandType == CMD_UTILITY) + { + ProcessUtility(queryTree->utilityStmt, None); + if (i < qlen - 1) + CommandCounterIncrement(); + else + return (SPI_OK_UTILITY); + } + else + { + qdesc = CreateQueryDesc(queryTree, planTree, + (i < qlen - 1) ? None : SPI); + state = CreateExecutorState(); + if (nargs > 0) + { + ParamListInfo paramLI = (ParamListInfo) palloc((nargs + 1) * + sizeof(ParamListInfoData)); + + state->es_param_list_info = paramLI; + for (k = 0; k < plan->nargs; paramLI++, k++) + { + paramLI->kind = PARAM_NUM; + paramLI->id = k + 1; + paramLI->isnull = (Nulls[k] != 0); + paramLI->value = (Datum) Values[k]; + } + paramLI->kind = PARAM_INVALID; + } + else + state->es_param_list_info = NULL; + res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount); + if (res < 0 || i >= qlen - 1) + return (res); + CommandCounterIncrement(); + } + } + + return (res); + } static int -_SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount) +_SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount) { - Query *parseTree; - Plan *plan; - int operation; - TupleDesc tupdesc; - bool isRetrieveIntoPortal = false; - bool isRetrieveIntoRelation = false; - char* intoName = NULL; - int res; - - parseTree = queryDesc->parsetree; - plan = queryDesc->plantree; - operation = queryDesc->operation; - - switch (operation) - { - case CMD_SELECT: - res = SPI_OK_SELECT; - if (parseTree->isPortal) - { - isRetrieveIntoPortal = true; - intoName = parseTree->into; - parseTree->isBinary = false; /* */ - - return (SPI_ERROR_CURSOR); - - } - else if (parseTree->into != NULL) /* select into table */ - { - res = SPI_OK_SELINTO; - isRetrieveIntoRelation = true; - } - break; - case CMD_INSERT: - res = SPI_OK_INSERT; - break; - case CMD_DELETE: - res = SPI_OK_DELETE; - break; - case CMD_UPDATE: - res = SPI_OK_UPDATE; - break; - default: - return (SPI_ERROR_OPUNKNOWN); - } - - if ( state == NULL ) /* plan preparation */ - return (res); + Query *parseTree; + Plan *plan; + int operation; + TupleDesc tupdesc; + bool isRetrieveIntoPortal = false; + bool isRetrieveIntoRelation = false; + char *intoName = NULL; + int res; + + parseTree = queryDesc->parsetree; + plan = queryDesc->plantree; + operation = queryDesc->operation; + + switch (operation) + { + case CMD_SELECT: + res = SPI_OK_SELECT; + if (parseTree->isPortal) + { + isRetrieveIntoPortal = true; + intoName = parseTree->into; + parseTree->isBinary = false; /* */ + + return (SPI_ERROR_CURSOR); + + } + else if (parseTree->into != NULL) /* select into table */ + { + res = SPI_OK_SELINTO; + isRetrieveIntoRelation = true; + } + break; + case CMD_INSERT: + res = SPI_OK_INSERT; + break; + case CMD_DELETE: + res = SPI_OK_DELETE; + break; + case CMD_UPDATE: + res = SPI_OK_UPDATE; + break; + default: + return (SPI_ERROR_OPUNKNOWN); + } + + if (state == NULL) /* plan preparation */ + return (res); #ifdef SPI_EXECUTOR_STATS - if ( ShowExecutorStats ) - ResetUsage (); + if (ShowExecutorStats) + ResetUsage(); #endif - tupdesc = ExecutorStart (queryDesc, state); - - /* Don't work currently */ - if (isRetrieveIntoPortal) - { - ProcessPortal(intoName, - parseTree, - plan, - state, - tupdesc, - None); - return (SPI_OK_CURSOR); - } - - ExecutorRun (queryDesc, state, EXEC_RUN, tcount); - - _SPI_current->processed = state->es_processed; - if ( operation == CMD_SELECT && queryDesc->dest == SPI ) - { - if ( _SPI_checktuples (isRetrieveIntoRelation) ) - elog (FATAL, "SPI_select: # of processed tuples check failed"); - } - - ExecutorEnd (queryDesc, state); - + tupdesc = ExecutorStart(queryDesc, state); + + /* Don't work currently */ + if (isRetrieveIntoPortal) + { + ProcessPortal(intoName, + parseTree, + plan, + state, + tupdesc, + None); + return (SPI_OK_CURSOR); + } + + ExecutorRun(queryDesc, state, EXEC_RUN, tcount); + + _SPI_current->processed = state->es_processed; + if (operation == CMD_SELECT && queryDesc->dest == SPI) + { + if (_SPI_checktuples(isRetrieveIntoRelation)) + elog(FATAL, "SPI_select: # of processed tuples check failed"); + } + + ExecutorEnd(queryDesc, state); + #ifdef SPI_EXECUTOR_STATS - if ( ShowExecutorStats ) - { - fprintf (stderr, "! Executor Stats:\n"); - ShowUsage (); - } + if (ShowExecutorStats) + { + fprintf(stderr, "! Executor Stats:\n"); + ShowUsage(); + } #endif - - if ( queryDesc->dest == SPI ) - { - SPI_processed = _SPI_current->processed; - SPI_tuptable = _SPI_current->tuptable; - } - - return (res); + + if (queryDesc->dest == SPI) + { + SPI_processed = _SPI_current->processed; + SPI_tuptable = _SPI_current->tuptable; + } + + return (res); } #if 0 static void -_SPI_fetch (FetchStmt *stmt) +_SPI_fetch(FetchStmt * stmt) { - char *name = stmt->portalname; - int feature = ( stmt->direction == FORWARD ) ? EXEC_FOR : EXEC_BACK; - int count = stmt->howMany; - Portal portal; - QueryDesc *queryDesc; - EState *state; - MemoryContext context; - - if ( name == NULL) - elog (FATAL, "SPI_fetch from blank portal unsupported"); - - portal = GetPortalByName (name); - if ( !PortalIsValid (portal) ) - elog (FATAL, "SPI_fetch: portal \"%s\" not found", name); - - context = MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal)); - - queryDesc = PortalGetQueryDesc(portal); - state = PortalGetState(portal); - - ExecutorRun(queryDesc, state, feature, count); - - MemoryContextSwitchTo (context); /* switch to the normal Executor context */ - - _SPI_current->processed = state->es_processed; - if ( _SPI_checktuples (false) ) - elog (FATAL, "SPI_fetch: # of processed tuples check failed"); - - SPI_processed = _SPI_current->processed; - SPI_tuptable = _SPI_current->tuptable; - + char *name = stmt->portalname; + int feature = (stmt->direction == FORWARD) ? EXEC_FOR : EXEC_BACK; + int count = stmt->howMany; + Portal portal; + QueryDesc *queryDesc; + EState *state; + MemoryContext context; + + if (name == NULL) + elog(FATAL, "SPI_fetch from blank portal unsupported"); + + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + elog(FATAL, "SPI_fetch: portal \"%s\" not found", name); + + context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal)); + + queryDesc = PortalGetQueryDesc(portal); + state = PortalGetState(portal); + + ExecutorRun(queryDesc, state, feature, count); + + MemoryContextSwitchTo(context); /* switch to the normal Executor + * context */ + + _SPI_current->processed = state->es_processed; + if (_SPI_checktuples(false)) + elog(FATAL, "SPI_fetch: # of processed tuples check failed"); + + SPI_processed = _SPI_current->processed; + SPI_tuptable = _SPI_current->tuptable; + } + #endif -static MemoryContext -_SPI_execmem () +static MemoryContext +_SPI_execmem() { - MemoryContext oldcxt; - PortalHeapMemory phmem; - - phmem = PortalGetHeapMemory (_SPI_current->portal); - oldcxt = MemoryContextSwitchTo ((MemoryContext)phmem); - - return (oldcxt); - + MemoryContext oldcxt; + PortalHeapMemory phmem; + + phmem = PortalGetHeapMemory(_SPI_current->portal); + oldcxt = MemoryContextSwitchTo((MemoryContext) phmem); + + return (oldcxt); + } -static MemoryContext -_SPI_procmem () +static MemoryContext +_SPI_procmem() { - MemoryContext oldcxt; - PortalVariableMemory pvmem; - - pvmem = PortalGetVariableMemory (_SPI_current->portal); - oldcxt = MemoryContextSwitchTo ((MemoryContext)pvmem); - - return (oldcxt); - + MemoryContext oldcxt; + PortalVariableMemory pvmem; + + pvmem = PortalGetVariableMemory(_SPI_current->portal); + oldcxt = MemoryContextSwitchTo((MemoryContext) pvmem); + + return (oldcxt); + } /* @@ -781,108 +792,110 @@ _SPI_procmem () * */ static int -_SPI_begin_call (bool execmem) +_SPI_begin_call(bool execmem) { - if ( _SPI_curid + 1 != _SPI_connected ) - return (SPI_ERROR_UNCONNECTED); - _SPI_curid++; - if ( _SPI_current != &(_SPI_stack[_SPI_curid]) ) - elog (FATAL, "SPI: stack corrupted"); - - if ( execmem ) /* switch to the Executor memory context */ - { - _SPI_execmem (); - StartPortalAllocMode (DefaultAllocMode, 0); - } - - return (0); + if (_SPI_curid + 1 != _SPI_connected) + return (SPI_ERROR_UNCONNECTED); + _SPI_curid++; + if (_SPI_current != &(_SPI_stack[_SPI_curid])) + elog(FATAL, "SPI: stack corrupted"); + + if (execmem) /* switch to the Executor memory context */ + { + _SPI_execmem(); + StartPortalAllocMode(DefaultAllocMode, 0); + } + + return (0); } static int -_SPI_end_call (bool procmem) +_SPI_end_call(bool procmem) { - /* - * We' returning to procedure where _SPI_curid == _SPI_connected - 1 - */ - _SPI_curid--; - - if ( _SPI_current->qtlist) /* free _SPI_plan allocations */ - { - free (_SPI_current->qtlist->qtrees); - free (_SPI_current->qtlist); - _SPI_current->qtlist = NULL; - } - - if ( procmem ) /* switch to the procedure memory context */ - { /* but free Executor memory before */ - EndPortalAllocMode (); - _SPI_procmem (); - } - - return (0); + + /* + * We' returning to procedure where _SPI_curid == _SPI_connected - 1 + */ + _SPI_curid--; + + if (_SPI_current->qtlist) /* free _SPI_plan allocations */ + { + free(_SPI_current->qtlist->qtrees); + free(_SPI_current->qtlist); + _SPI_current->qtlist = NULL; + } + + if (procmem) /* switch to the procedure memory context */ + { /* but free Executor memory before */ + EndPortalAllocMode(); + _SPI_procmem(); + } + + return (0); } -static bool -_SPI_checktuples (bool isRetrieveIntoRelation) +static bool +_SPI_checktuples(bool isRetrieveIntoRelation) { - uint32 processed = _SPI_current->processed; - SPITupleTable *tuptable = _SPI_current->tuptable; - bool failed = false; - - if ( processed == 0 ) - { - if ( tuptable != NULL ) - failed = true; - } - else /* some tuples were processed */ - { - if ( tuptable == NULL ) /* spi_printtup was not called */ - { - if ( !isRetrieveIntoRelation ) - failed = true; - } - else if ( isRetrieveIntoRelation ) - failed = true; - else if ( processed != ( tuptable->alloced - tuptable->free ) ) - failed = true; - } - - return (failed); + uint32 processed = _SPI_current->processed; + SPITupleTable *tuptable = _SPI_current->tuptable; + bool failed = false; + + if (processed == 0) + { + if (tuptable != NULL) + failed = true; + } + else +/* some tuples were processed */ + { + if (tuptable == NULL) /* spi_printtup was not called */ + { + if (!isRetrieveIntoRelation) + failed = true; + } + else if (isRetrieveIntoRelation) + failed = true; + else if (processed != (tuptable->alloced - tuptable->free)) + failed = true; + } + + return (failed); } - + static _SPI_plan * -_SPI_copy_plan (_SPI_plan *plan, bool local) +_SPI_copy_plan(_SPI_plan * plan, bool local) { - _SPI_plan *newplan; - MemoryContext oldcxt; - int i; - - if ( local ) - oldcxt = MemoryContextSwitchTo ((MemoryContext) - PortalGetVariableMemory (_SPI_current->portal)); - else - oldcxt = MemoryContextSwitchTo (TopMemoryContext); - - newplan = (_SPI_plan *) palloc (sizeof (_SPI_plan)); - newplan->qtlist = (QueryTreeList*) palloc (sizeof (QueryTreeList)); - newplan->qtlist->len = plan->qtlist->len; - newplan->qtlist->qtrees = (Query**) palloc (plan->qtlist->len * - sizeof (Query*)); - for (i = 0; i < plan->qtlist->len; i++) - newplan->qtlist->qtrees[i] = (Query *) - copyObject (plan->qtlist->qtrees[i]); - - newplan->ptlist = (List *) copyObject (plan->ptlist); - newplan->nargs = plan->nargs; - if ( plan->nargs > 0 ) - { - newplan->argtypes = (Oid *) palloc (plan->nargs * sizeof (Oid)); - memcpy (newplan->argtypes, plan->argtypes, plan->nargs * sizeof (Oid)); - } - else - newplan->argtypes = NULL; - - MemoryContextSwitchTo (oldcxt); - - return (newplan); + _SPI_plan *newplan; + MemoryContext oldcxt; + int i; + + if (local) + oldcxt = MemoryContextSwitchTo((MemoryContext) + PortalGetVariableMemory(_SPI_current->portal)); + else + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + + newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); + newplan->qtlist = (QueryTreeList *) palloc(sizeof(QueryTreeList)); + newplan->qtlist->len = plan->qtlist->len; + newplan->qtlist->qtrees = (Query **) palloc(plan->qtlist->len * + sizeof(Query *)); + for (i = 0; i < plan->qtlist->len; i++) + newplan->qtlist->qtrees[i] = (Query *) + copyObject(plan->qtlist->qtrees[i]); + + newplan->ptlist = (List *) copyObject(plan->ptlist); + newplan->nargs = plan->nargs; + if (plan->nargs > 0) + { + newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid)); + memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid)); + } + else + newplan->argtypes = NULL; + + MemoryContextSwitchTo(oldcxt); + + return (newplan); } diff --git a/src/backend/lib/bit.c b/src/backend/lib/bit.c index 680f349a520..439566c0b75 100644 --- a/src/backend/lib/bit.c +++ b/src/backend/lib/bit.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * bit.c-- - * Standard bit array code. + * Standard bit array code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/lib/Attic/bit.c,v 1.4 1996/11/06 08:27:09 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/bit.c,v 1.5 1997/09/07 04:41:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,27 +22,26 @@ void BitArraySetBit(BitArray bitArray, BitIndex bitIndex) -{ - bitArray[bitIndex/BitsPerByte] +{ + bitArray[bitIndex / BitsPerByte] |= (1 << (BitsPerByte - (bitIndex % BitsPerByte) - 1)); - return; + return; } void BitArrayClearBit(BitArray bitArray, BitIndex bitIndex) { - bitArray[bitIndex/BitsPerByte] + bitArray[bitIndex / BitsPerByte] &= ~(1 << (BitsPerByte - (bitIndex % BitsPerByte) - 1)); - return; + return; } bool BitArrayBitIsSet(BitArray bitArray, BitIndex bitIndex) -{ - return( (bool) (((bitArray[bitIndex / BitsPerByte] & - (1 << (BitsPerByte - (bitIndex % BitsPerByte) - - 1) - ) - ) != 0 ) ? 1 : 0) ); +{ + return ((bool) (((bitArray[bitIndex / BitsPerByte] & + (1 << (BitsPerByte - (bitIndex % BitsPerByte) + - 1) + ) + ) != 0) ? 1 : 0)); } - diff --git a/src/backend/lib/dllist.c b/src/backend/lib/dllist.c index b8dd14a231b..70feee02bb9 100644 --- a/src/backend/lib/dllist.c +++ b/src/backend/lib/dllist.c @@ -1,15 +1,15 @@ /*------------------------------------------------------------------------- * * dllist.c-- - * this is a simple doubly linked list implementation - * replaces the old simplelists stuff - * the elements of the lists are void* + * this is a simple doubly linked list implementation + * replaces the old simplelists stuff + * the elements of the lists are void* * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/lib/dllist.c,v 1.5 1997/08/19 21:31:16 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/lib/dllist.c,v 1.6 1997/09/07 04:41:56 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -18,191 +18,197 @@ #include <lib/dllist.h> -Dllist* +Dllist * DLNewList(void) { - Dllist* l; + Dllist *l; - l = malloc(sizeof(Dllist)); - l->dll_head = 0; - l->dll_tail = 0; + l = malloc(sizeof(Dllist)); + l->dll_head = 0; + l->dll_tail = 0; - return l; + return l; } - /* free up a list and all the nodes in it*/ + /* free up a list and all the nodes in it */ void -DLFreeList(Dllist* l) +DLFreeList(Dllist * l) { - Dlelem* curr; + Dlelem *curr; - while ( (curr = DLRemHead(l)) != 0) - free(curr); + while ((curr = DLRemHead(l)) != 0) + free(curr); - free(l); + free(l); } -Dlelem* -DLNewElem(void* val) +Dlelem * +DLNewElem(void *val) { - Dlelem* e; - e = malloc(sizeof(Dlelem)); - e->dle_next = 0; - e->dle_prev = 0; - e->dle_val = val; - e->dle_list = 0; - return e; + Dlelem *e; + + e = malloc(sizeof(Dlelem)); + e->dle_next = 0; + e->dle_prev = 0; + e->dle_val = val; + e->dle_list = 0; + return e; } void -DLFreeElem(Dlelem* e) +DLFreeElem(Dlelem * e) { - free(e); + free(e); } -Dlelem* -DLGetHead(Dllist* l) +Dlelem * +DLGetHead(Dllist * l) { - return (l ? l->dll_head : 0); + return (l ? l->dll_head : 0); } /* get the value stored in the first element */ #ifdef NOT_USED -void* -DLGetHeadVal(Dllist* l) +void * +DLGetHeadVal(Dllist * l) { - Dlelem* e = DLGetHead(l); - - return (e ? e->dle_val : 0); + Dlelem *e = DLGetHead(l); + + return (e ? e->dle_val : 0); } + #endif -Dlelem* -DLGetTail(Dllist* l) +Dlelem * +DLGetTail(Dllist * l) { - return (l ? l->dll_tail : 0); + return (l ? l->dll_tail : 0); } /* get the value stored in the first element */ #ifdef NOT_USED -void* -DLGetTailVal(Dllist* l) +void * +DLGetTailVal(Dllist * l) { - Dlelem* e = DLGetTail(l); - - return (e ? e->dle_val : 0); + Dlelem *e = DLGetTail(l); + + return (e ? e->dle_val : 0); } + #endif -Dlelem* -DLGetPred(Dlelem* e) /* get predecessor */ +Dlelem * +DLGetPred(Dlelem * e) /* get predecessor */ { - return (e ? e->dle_prev : 0); + return (e ? e->dle_prev : 0); } -Dlelem* -DLGetSucc(Dlelem* e) /* get successor */ +Dlelem * +DLGetSucc(Dlelem * e) /* get successor */ { - return (e ? e->dle_next : 0); + return (e ? e->dle_next : 0); } void -DLRemove(Dlelem* e) +DLRemove(Dlelem * e) { - Dllist* l; + Dllist *l; - if (e->dle_prev) - e->dle_prev->dle_next = e->dle_next; - if (e->dle_next) - e->dle_next->dle_prev = e->dle_prev; + if (e->dle_prev) + e->dle_prev->dle_next = e->dle_next; + if (e->dle_next) + e->dle_next->dle_prev = e->dle_prev; - /* check to see if we're removing the head or tail */ - l = e->dle_list; - if (e == l->dll_head) - DLRemHead(l); - if (e == l->dll_tail) - DLRemTail(l); + /* check to see if we're removing the head or tail */ + l = e->dle_list; + if (e == l->dll_head) + DLRemHead(l); + if (e == l->dll_tail) + DLRemTail(l); } -void -DLAddHead(Dllist* l, Dlelem* e) +void +DLAddHead(Dllist * l, Dlelem * e) { - e->dle_list = l; - - if (l->dll_head) { - l->dll_head->dle_prev = e; - e->dle_next = l->dll_head; - } - e->dle_prev = 0; - l->dll_head = e; - - if (l->dll_tail == 0) /* if this is first element added */ - l->dll_tail = l->dll_head; + e->dle_list = l; + + if (l->dll_head) + { + l->dll_head->dle_prev = e; + e->dle_next = l->dll_head; + } + e->dle_prev = 0; + l->dll_head = e; + + if (l->dll_tail == 0) /* if this is first element added */ + l->dll_tail = l->dll_head; } void -DLAddTail(Dllist* l, Dlelem* e) +DLAddTail(Dllist * l, Dlelem * e) { - e->dle_list = l; - - if (l->dll_tail) { - l->dll_tail->dle_next = e; - e->dle_prev = l->dll_tail; - } - e->dle_next = 0; - l->dll_tail = e; - - if (l->dll_head == 0) /* if this is first element added */ - l->dll_head = l->dll_tail; + e->dle_list = l; + + if (l->dll_tail) + { + l->dll_tail->dle_next = e; + e->dle_prev = l->dll_tail; + } + e->dle_next = 0; + l->dll_tail = e; + + if (l->dll_head == 0) /* if this is first element added */ + l->dll_head = l->dll_tail; } -Dlelem* -DLRemHead(Dllist* l) +Dlelem * +DLRemHead(Dllist * l) { - /* remove and return the head */ - Dlelem* result; + /* remove and return the head */ + Dlelem *result; - if (l->dll_head == 0) - return 0; + if (l->dll_head == 0) + return 0; - result = l->dll_head; - if (l->dll_head->dle_next) { - l->dll_head->dle_next->dle_prev = 0; - } + result = l->dll_head; + if (l->dll_head->dle_next) + { + l->dll_head->dle_next->dle_prev = 0; + } - l->dll_head = l->dll_head->dle_next; + l->dll_head = l->dll_head->dle_next; - result->dle_next = 0; - result->dle_list = 0; - - if (result == l->dll_tail) /* if the head is also the tail */ - l->dll_tail = 0; + result->dle_next = 0; + result->dle_list = 0; - return result; + if (result == l->dll_tail) /* if the head is also the tail */ + l->dll_tail = 0; + + return result; } -Dlelem* -DLRemTail(Dllist* l) +Dlelem * +DLRemTail(Dllist * l) { - /* remove and return the tail */ - Dlelem* result; + /* remove and return the tail */ + Dlelem *result; - if (l->dll_tail == 0 ) - return 0; + if (l->dll_tail == 0) + return 0; - result = l->dll_tail; - if (l->dll_tail->dle_prev) { - l->dll_tail->dle_prev->dle_next = 0; - } - l->dll_tail = l->dll_tail->dle_prev; + result = l->dll_tail; + if (l->dll_tail->dle_prev) + { + l->dll_tail->dle_prev->dle_next = 0; + } + l->dll_tail = l->dll_tail->dle_prev; - result->dle_prev = 0; - result->dle_list = 0; + result->dle_prev = 0; + result->dle_list = 0; - if (result == l->dll_head) /* if the tail is also the head */ - l->dll_head = 0; + if (result == l->dll_head) /* if the tail is also the head */ + l->dll_head = 0; - return result; + return result; } - diff --git a/src/backend/lib/fstack.c b/src/backend/lib/fstack.c index 68f1505b167..f97d467fe92 100644 --- a/src/backend/lib/fstack.c +++ b/src/backend/lib/fstack.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * fstack.c-- - * Fixed format stack definitions. + * Fixed format stack definitions. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/lib/Attic/fstack.c,v 1.4 1997/06/06 22:02:37 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/fstack.c,v 1.5 1997/09/07 04:42:00 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,25 +21,25 @@ /* * FixedItemIsValid -- - * True iff item is valid. + * True iff item is valid. */ #define FixedItemIsValid(item) PointerIsValid(item) /* * FixedStackGetItemBase -- - * Returns base of enclosing structure. + * Returns base of enclosing structure. */ #define FixedStackGetItemBase(stack, item) \ - ((Pointer)((char *)(item) - (stack)->offset)) + ((Pointer)((char *)(item) - (stack)->offset)) /* * FixedStackGetItem -- - * Returns item of given pointer to enclosing structure. + * Returns item of given pointer to enclosing structure. */ #define FixedStackGetItem(stack, pointer) \ - ((FixedItem)((char *)(pointer) + (stack)->offset)) + ((FixedItem)((char *)(pointer) + (stack)->offset)) -#define FixedStackIsValid(stack) ((bool)PointerIsValid(stack)) +#define FixedStackIsValid(stack) ((bool)PointerIsValid(stack)) /* * External functions @@ -48,99 +48,105 @@ void FixedStackInit(FixedStack stack, Offset offset) { - AssertArg(PointerIsValid(stack)); - - stack->top = NULL; - stack->offset = offset; + AssertArg(PointerIsValid(stack)); + + stack->top = NULL; + stack->offset = offset; } Pointer FixedStackPop(FixedStack stack) { - Pointer pointer; - - AssertArg(FixedStackIsValid(stack)); - - if (!PointerIsValid(stack->top)) { - return (NULL); - } - - pointer = FixedStackGetItemBase(stack, stack->top); - stack->top = stack->top->next; - - return (pointer); + Pointer pointer; + + AssertArg(FixedStackIsValid(stack)); + + if (!PointerIsValid(stack->top)) + { + return (NULL); + } + + pointer = FixedStackGetItemBase(stack, stack->top); + stack->top = stack->top->next; + + return (pointer); } void FixedStackPush(FixedStack stack, Pointer pointer) { - FixedItem item = FixedStackGetItem(stack, pointer); - - AssertArg(FixedStackIsValid(stack)); - AssertArg(PointerIsValid(pointer)); - - item->next = stack->top; - stack->top = item; + FixedItem item = FixedStackGetItem(stack, pointer); + + AssertArg(FixedStackIsValid(stack)); + AssertArg(PointerIsValid(pointer)); + + item->next = stack->top; + stack->top = item; } -#ifndef NO_ASSERT_CHECKING +#ifndef NO_ASSERT_CHECKING /* * FixedStackContains -- - * True iff ordered stack contains given element. + * True iff ordered stack contains given element. * * Note: - * This is inefficient. It is intended for debugging use only. + * This is inefficient. It is intended for debugging use only. * * Exceptions: - * BadArg if stack is invalid. - * BadArg if pointer is invalid. + * BadArg if stack is invalid. + * BadArg if pointer is invalid. */ -static bool +static bool FixedStackContains(FixedStack stack, Pointer pointer) { - FixedItem next; - FixedItem item; - - AssertArg(FixedStackIsValid(stack)); - AssertArg(PointerIsValid(pointer)); - - item = FixedStackGetItem(stack, pointer); - - for (next = stack->top; FixedItemIsValid(next); next = next->next) { - if (next == item) { - return (true); + FixedItem next; + FixedItem item; + + AssertArg(FixedStackIsValid(stack)); + AssertArg(PointerIsValid(pointer)); + + item = FixedStackGetItem(stack, pointer); + + for (next = stack->top; FixedItemIsValid(next); next = next->next) + { + if (next == item) + { + return (true); + } } - } - return (false); + return (false); } + #endif Pointer FixedStackGetTop(FixedStack stack) { - AssertArg(FixedStackIsValid(stack)); - - if (!PointerIsValid(stack->top)) { - return (NULL); - } - - return (FixedStackGetItemBase(stack, stack->top)); + AssertArg(FixedStackIsValid(stack)); + + if (!PointerIsValid(stack->top)) + { + return (NULL); + } + + return (FixedStackGetItemBase(stack, stack->top)); } Pointer FixedStackGetNext(FixedStack stack, Pointer pointer) { - FixedItem item; - - /* AssertArg(FixedStackIsValid(stack)); */ - /* AssertArg(PointerIsValid(pointer)); */ - AssertArg(FixedStackContains(stack, pointer)); - - item = FixedStackGetItem(stack, pointer)->next; - - if (!PointerIsValid(item)) { - return (NULL); - } - - return(FixedStackGetItemBase(stack, item)); + FixedItem item; + + /* AssertArg(FixedStackIsValid(stack)); */ + /* AssertArg(PointerIsValid(pointer)); */ + AssertArg(FixedStackContains(stack, pointer)); + + item = FixedStackGetItem(stack, pointer)->next; + + if (!PointerIsValid(item)) + { + return (NULL); + } + + return (FixedStackGetItemBase(stack, item)); } diff --git a/src/backend/lib/hasht.c b/src/backend/lib/hasht.c index baceabfcf7a..4e12dcf30eb 100644 --- a/src/backend/lib/hasht.c +++ b/src/backend/lib/hasht.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * hasht.c-- - * hash table related functions that are not directly supported - * by the hashing packages under utils/hash. + * hash table related functions that are not directly supported + * by the hashing packages under utils/hash. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.4 1997/08/12 22:52:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.5 1997/09/07 04:42:03 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -19,29 +19,31 @@ #include <lib/hasht.h> /* ----------------------------------- - * HashTableWalk + * HashTableWalk * - * call function on every element in hashtable - * one extra argument, arg may be supplied + * call function on every element in hashtable + * one extra argument, arg may be supplied * ----------------------------------- */ void -HashTableWalk(HTAB *hashtable, HashtFunc function, int arg) +HashTableWalk(HTAB * hashtable, HashtFunc function, int arg) { - long *hashent; - long *data; - int keysize; - - keysize = hashtable->hctl->keysize; - hash_seq((HTAB *)NULL); - while ((hashent = hash_seq(hashtable)) != (long *) TRUE) { - if (hashent == NULL) - elog(FATAL, "error in HashTableWalk."); - /* - * XXX the corresponding hash table insertion does NOT - * LONGALIGN -- make sure the keysize is ok - */ - data = (long *) LONGALIGN((char*) hashent + keysize); - (*function)(data, arg); - } + long *hashent; + long *data; + int keysize; + + keysize = hashtable->hctl->keysize; + hash_seq((HTAB *) NULL); + while ((hashent = hash_seq(hashtable)) != (long *) TRUE) + { + if (hashent == NULL) + elog(FATAL, "error in HashTableWalk."); + + /* + * XXX the corresponding hash table insertion does NOT LONGALIGN + * -- make sure the keysize is ok + */ + data = (long *) LONGALIGN((char *) hashent + keysize); + (*function) (data, arg); + } } diff --git a/src/backend/lib/lispsort.c b/src/backend/lib/lispsort.c index 11acc5683b2..bf346ecc1a6 100644 --- a/src/backend/lib/lispsort.c +++ b/src/backend/lib/lispsort.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/lib/Attic/lispsort.c,v 1.4 1997/08/19 21:31:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/lispsort.c,v 1.5 1997/09/07 04:42:05 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,38 +24,41 @@ #ifdef NOT_USED /* -** lisp_qsort: Takes a lisp list as input, copies it into an array of lisp -** nodes which it sorts via qsort() with the comparison function -** as passed into lisp_qsort(), and returns a new list with -** the nodes sorted. The old list is *not* freed or modified (?) +** lisp_qsort: Takes a lisp list as input, copies it into an array of lisp +** nodes which it sorts via qsort() with the comparison function +** as passed into lisp_qsort(), and returns a new list with +** the nodes sorted. The old list is *not* freed or modified (?) */ -List *lisp_qsort(List *the_list, /* the list to be sorted */ - int (*compare)()) /* function to compare two nodes */ +List * +lisp_qsort(List * the_list, /* the list to be sorted */ + int (*compare) ()) /* function to compare two nodes */ { - int i; - size_t num; - List **nodearray; - List *tmp, *output; - - /* find size of list */ - num = length(the_list); - if (num < 2) - return(copyObject(the_list)); - - /* copy elements of the list into an array */ - nodearray = (List **) palloc(num * sizeof(List *)); - - for (tmp = the_list, i = 0; tmp != NIL; tmp = lnext(tmp), i++) - nodearray[i] = copyObject(lfirst(tmp)); - - /* sort the array */ - pg_qsort(nodearray, num, sizeof(List *), compare); - - /* lcons together the array elements */ - output = NIL; - for (i = num - 1; i >= 0; i--) - output = lcons(nodearray[i], output); - - return(output); + int i; + size_t num; + List **nodearray; + List *tmp, + *output; + + /* find size of list */ + num = length(the_list); + if (num < 2) + return (copyObject(the_list)); + + /* copy elements of the list into an array */ + nodearray = (List **) palloc(num * sizeof(List *)); + + for (tmp = the_list, i = 0; tmp != NIL; tmp = lnext(tmp), i++) + nodearray[i] = copyObject(lfirst(tmp)); + + /* sort the array */ + pg_qsort(nodearray, num, sizeof(List *), compare); + + /* lcons together the array elements */ + output = NIL; + for (i = num - 1; i >= 0; i--) + output = lcons(nodearray[i], output); + + return (output); } + #endif diff --git a/src/backend/lib/qsort.c b/src/backend/lib/qsort.c index e0f3cbbb2c2..ff2bbfa16d9 100644 --- a/src/backend/lib/qsort.c +++ b/src/backend/lib/qsort.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * qsort.c-- - * + * * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/lib/Attic/qsort.c,v 1.2 1996/11/06 08:27:15 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/qsort.c,v 1.3 1997/09/07 04:42:06 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -19,22 +19,22 @@ * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -45,8 +45,9 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)qsort.c 5.9 (Berkeley) 2/23/91"; -#endif /* LIBC_SCCS and not lint */ +static char sccsid[] = "@(#)qsort.c 5.9 (Berkeley) 2/23/91"; + +#endif /* LIBC_SCCS and not lint */ #include <sys/types.h> @@ -58,21 +59,22 @@ static char sccsid[] = "@(#)qsort.c 5.9 (Berkeley) 2/23/91"; * MTHRESH is the smallest partition for which we compare for a median * value instead of using the middle value. */ -#define MTHRESH 6 +#define MTHRESH 6 /* * THRESH is the minimum number of entries in a partition for continued * partitioning. */ -#define THRESH 4 +#define THRESH 4 -static void insertion_sort(char* bot, int nmemb, int size, int (*compar)()); -static void quick_sort(char* bot, int nmemb, int size, int (*compar)()); +static void insertion_sort(char *bot, int nmemb, int size, int (*compar) ()); +static void quick_sort(char *bot, int nmemb, int size, int (*compar) ()); -void pg_qsort(void *bot, - size_t nmemb, - size_t size, - int (*compar)(void *, void *)) +void +pg_qsort(void *bot, + size_t nmemb, + size_t size, + int (*compar) (void *, void *)) { if (nmemb <= 1) @@ -85,19 +87,19 @@ void pg_qsort(void *bot, } /* - * Swap two areas of size number of bytes. Although qsort(3) permits random + * Swap two areas of size number of bytes. Although qsort(3) permits random * blocks of memory to be sorted, sorting pointers is almost certainly the * common case (and, were it not, could easily be made so). Regardless, it * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer * arithmetic gets lost in the time required for comparison function calls. */ -#define SWAP(a, b) { \ - cnt = size; \ - do { \ - ch = *a; \ - *a++ = *b; \ - *b++ = ch; \ - } while (--cnt); \ +#define SWAP(a, b) { \ + cnt = size; \ + do { \ + ch = *a; \ + *a++ = *b; \ + *b++ = ch; \ + } while (--cnt); \ } /* @@ -114,24 +116,28 @@ void pg_qsort(void *bot, * Knuth, page 122, equation 26), since the quicksort algorithm does less * comparisons than the insertion sort. */ -#define SORT(bot, n) { \ - if (n > 1) \ - if (n == 2) { \ - t1 = bot + size; \ - if (compar(t1, bot) < 0) \ - SWAP(t1, bot); \ - } else \ - insertion_sort(bot, n, size, compar); \ +#define SORT(bot, n) { \ + if (n > 1) \ + if (n == 2) { \ + t1 = bot + size; \ + if (compar(t1, bot) < 0) \ + SWAP(t1, bot); \ + } else \ + insertion_sort(bot, n, size, compar); \ } static void -quick_sort(char* bot, int nmemb, int size, int (*compar)()) +quick_sort(char *bot, int nmemb, int size, int (*compar) ()) { - register int cnt; + register int cnt; register u_char ch; - register char *top, *mid, *t1, *t2; - register int n1, n2; - char *bsv; + register char *top, + *mid, + *t1, + *t2; + register int n1, + n2; + char *bsv; /* bot and nmemb must already be set. */ partition: @@ -145,7 +151,8 @@ partition: * Vol. 3, page 123, Eq. 28). This test order gets the equalities * right. */ - if (nmemb >= MTHRESH) { + if (nmemb >= MTHRESH) + { n1 = compar(bot, mid); n2 = compar(mid, top); if (n1 < 0 && n2 > 0) @@ -156,28 +163,33 @@ partition: t1 = mid; /* if mid element not selected, swap selection there */ - if (t1 != mid) { + if (t1 != mid) + { SWAP(t1, mid); mid -= size; } } /* Standard quicksort, Knuth, Vol. 3, page 116, Algorithm Q. */ -#define didswap n1 -#define newbot t1 -#define replace t2 +#define didswap n1 +#define newbot t1 +#define replace t2 didswap = 0; - for (bsv = bot;;) { + for (bsv = bot;;) + { for (; bot < mid && compar(bot, mid) <= 0; bot += size); - while (top > mid) { - if (compar(mid, top) <= 0) { + while (top > mid) + { + if (compar(mid, top) <= 0) + { top -= size; continue; } - newbot = bot + size; /* value of bot after swap */ + newbot = bot + size;/* value of bot after swap */ if (bot == mid) /* top <-> mid, mid == top */ replace = mid = top; - else { /* bot <-> top */ + else + { /* bot <-> top */ replace = top; top -= size; } @@ -191,7 +203,7 @@ partition: newbot = mid = bot; /* value of bot after swap */ top -= size; -swap: SWAP(bot, replace); +swap: SWAP(bot, replace); bot = newbot; didswap = 1; } @@ -200,14 +212,15 @@ swap: SWAP(bot, replace); * Quicksort behaves badly in the presence of data which is already * sorted (see Knuth, Vol. 3, page 119) going from O N lg N to O N^2. * To avoid this worst case behavior, if a re-partitioning occurs - * without swapping any elements, it is not further partitioned and - * is insert sorted. This wins big with almost sorted data sets and - * only loses if the data set is very strangely partitioned. A fix - * for those data sets would be to return prematurely if the insertion + * without swapping any elements, it is not further partitioned and is + * insert sorted. This wins big with almost sorted data sets and only + * loses if the data set is very strangely partitioned. A fix for + * those data sets would be to return prematurely if the insertion * sort routine is forced to make an excessive number of swaps, and * continue the partitioning. */ - if (!didswap) { + if (!didswap) + { insertion_sort(bsv, nmemb, size, compar); return; } @@ -216,34 +229,41 @@ swap: SWAP(bot, replace); * Re-partition or sort as necessary. Note that the mid element * itself is correctly positioned and can be ignored. */ -#define nlower n1 -#define nupper n2 +#define nlower n1 +#define nupper n2 bot = bsv; - nlower = (mid - bot) / size; /* size of lower partition */ + nlower = (mid - bot) / size;/* size of lower partition */ mid += size; - nupper = nmemb - nlower - 1; /* size of upper partition */ + nupper = nmemb - nlower - 1;/* size of upper partition */ /* * If must call recursively, do it on the smaller partition; this * bounds the stack to lg N entries. */ - if (nlower > nupper) { + if (nlower > nupper) + { if (nupper >= THRESH) quick_sort(mid, nupper, size, compar); - else { + else + { SORT(mid, nupper); - if (nlower < THRESH) { + if (nlower < THRESH) + { SORT(bot, nlower); return; } } nmemb = nlower; - } else { + } + else + { if (nlower >= THRESH) quick_sort(bot, nlower, size, compar); - else { + else + { SORT(bot, nlower); - if (nupper < THRESH) { + if (nupper < THRESH) + { SORT(mid, nupper); return; } @@ -255,30 +275,38 @@ swap: SWAP(bot, replace); } static void -insertion_sort(char* bot, int nmemb, int size, int (*compar)()) +insertion_sort(char *bot, int nmemb, int size, int (*compar) ()) { - register int cnt; + register int cnt; register u_char ch; - register char *s1, *s2, *t1, *t2, *top; + register char *s1, + *s2, + *t1, + *t2, + *top; /* - * A simple insertion sort (see Knuth, Vol. 3, page 81, Algorithm - * S). Insertion sort has the same worst case as most simple sorts - * (O N^2). It gets used here because it is (O N) in the case of - * sorted data. + * A simple insertion sort (see Knuth, Vol. 3, page 81, Algorithm S). + * Insertion sort has the same worst case as most simple sorts (O + * N^2). It gets used here because it is (O N) in the case of sorted + * data. */ top = bot + nmemb * size; - for (t1 = bot + size; t1 < top;) { + for (t1 = bot + size; t1 < top;) + { for (t2 = t1; (t2 -= size) >= bot && compar(t1, t2) < 0;); - if (t1 != (t2 += size)) { + if (t1 != (t2 += size)) + { /* Bubble bytes up through each element. */ - for (cnt = size; cnt--; ++t1) { + for (cnt = size; cnt--; ++t1) + { ch = *t1; for (s1 = s2 = t1; (s2 -= size) >= t2; s1 = s2) *s1 = *s2; *s1 = ch; } - } else + } + else t1 += size; } } diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c index 18cfc89f982..34108c04c72 100644 --- a/src/backend/lib/stringinfo.c +++ b/src/backend/lib/stringinfo.c @@ -1,15 +1,15 @@ /*------------------------------------------------------------------------- * * stringinfo.c-- - * These are routines that can be used to write informations to a string, - * without having to worry about string lengths, space allocation etc. - * Ideally the interface should look like the file i/o interface, + * These are routines that can be used to write informations to a string, + * without having to worry about string lengths, space allocation etc. + * Ideally the interface should look like the file i/o interface, * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/lib/stringinfo.c,v 1.3 1997/08/12 20:15:15 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/lib/stringinfo.c,v 1.4 1997/09/07 04:42:07 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,30 +30,33 @@ StringInfo makeStringInfo() { - StringInfo res; - long size; - - res = (StringInfo) palloc(sizeof(StringInfoData)); - if (res == NULL) { - elog(WARN, "makeStringInfo: Out of memory!"); - } - - size = 100; - res->data = palloc(size); - if (res->data == NULL) { - elog(WARN, - "makeStringInfo: Out of memory! (%ld bytes requested)", size); - } - res->maxlen = size; - res->len = 0; - /* - * NOTE: we must initialize `res->data' to the empty string because - * we use 'strcat' in 'appendStringInfo', which of course it always - * expects a null terminated string. - */ - res->data[0] = '\0'; - - return(res); + StringInfo res; + long size; + + res = (StringInfo) palloc(sizeof(StringInfoData)); + if (res == NULL) + { + elog(WARN, "makeStringInfo: Out of memory!"); + } + + size = 100; + res->data = palloc(size); + if (res->data == NULL) + { + elog(WARN, + "makeStringInfo: Out of memory! (%ld bytes requested)", size); + } + res->maxlen = size; + res->len = 0; + + /* + * NOTE: we must initialize `res->data' to the empty string because we + * use 'strcat' in 'appendStringInfo', which of course it always + * expects a null terminated string. + */ + res->data[0] = '\0'; + + return (res); } /*--------------------------------------------------------------------- @@ -69,48 +72,53 @@ makeStringInfo() void appendStringInfo(StringInfo str, char *buffer) { - int buflen, newlen; - char *s; - - Assert((str!=NULL)); - - /* - * do we have enough space to append the new string? - * (don't forget to count the null string terminating char!) - * If no, then reallocate some more. - */ - buflen = strlen(buffer); - if (buflen + str->len >= str->maxlen-1) { + int buflen, + newlen; + char *s; + + Assert((str != NULL)); + /* - * how much more space to allocate ? - * Let's say double the current space... - * However we must check if this is enough! + * do we have enough space to append the new string? (don't forget to + * count the null string terminating char!) If no, then reallocate + * some more. */ - newlen = 2 * str->len; - while (buflen + str->len >= newlen-1) { - newlen = 2 * newlen; + buflen = strlen(buffer); + if (buflen + str->len >= str->maxlen - 1) + { + + /* + * how much more space to allocate ? Let's say double the current + * space... However we must check if this is enough! + */ + newlen = 2 * str->len; + while (buflen + str->len >= newlen - 1) + { + newlen = 2 * newlen; + } + + /* + * allocate enough space. + */ + s = palloc(newlen); + if (s == NULL) + { + elog(WARN, + "appendStringInfo: Out of memory (%d bytes requested)", + newlen); + } + memmove(s, str->data, str->len + 1); + pfree(str->data); + str->maxlen = newlen; + str->data = s; } + /* - * allocate enough space. + * OK, we have enough space now, append 'buffer' at the end of the + * string & update the string length. NOTE: this is a text string + * (i.e. printable characters) so 'strcat' will do the job (no need to + * use 'bcopy' et all...) */ - s = palloc(newlen); - if (s==NULL) { - elog(WARN, - "appendStringInfo: Out of memory (%d bytes requested)", - newlen); - } - memmove(s, str->data, str->len+1); - pfree(str->data); - str->maxlen = newlen; - str->data = s; - } - - /* - * OK, we have enough space now, append 'buffer' at the - * end of the string & update the string length. - * NOTE: this is a text string (i.e. printable characters) - * so 'strcat' will do the job (no need to use 'bcopy' et all...) - */ - strcat(str->data, buffer); - str->len += buflen; + strcat(str->data, buffer); + str->len += buflen; } diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index abf59e25b17..ff6711d3b5c 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -1,65 +1,65 @@ /*------------------------------------------------------------------------- * * auth.c-- - * Routines to handle network authentication + * Routines to handle network authentication * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.14 1997/08/19 21:31:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.15 1997/09/07 04:42:09 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * - * backend (postmaster) routines: - * be_recvauth receive authentication information - * be_setauthsvc do/do not permit an authentication service - * be_getauthsvc is an authentication service permitted? + * backend (postmaster) routines: + * be_recvauth receive authentication information + * be_setauthsvc do/do not permit an authentication service + * be_getauthsvc is an authentication service permitted? * - * NOTES - * To add a new authentication system: - * 0. If you can't do your authentication over an existing socket, - * you lose -- get ready to hack around this framework instead of - * using it. Otherwise, you can assume you have an initialized - * and empty connection to work with. (Please don't leave leftover - * gunk in the connection after the authentication transactions, or - * the POSTGRES routines that follow will be very unhappy.) - * 1. Write a set of routines that: - * let a client figure out what user/principal name to use - * send authentication information (client side) - * receive authentication information (server side) - * You can include both routines in this file, using #ifdef FRONTEND - * to separate them. - * 2. Edit libpq/pqcomm.h and assign a MsgType for your protocol. - * 3. Edit the static "struct authsvc" array and the generic - * {be,fe}_{get,set}auth{name,svc} routines in this file to reflect - * the new service. You may have to change the arguments of these - * routines; they basically just reflect what Kerberos v4 needs. - * 4. Hack on src/{,bin}/Makefile.global and src/{backend,libpq}/Makefile - * to add library and CFLAGS hooks -- basically, grep the Makefile - * hierarchy for KRBVERS to see where you need to add things. + * NOTES + * To add a new authentication system: + * 0. If you can't do your authentication over an existing socket, + * you lose -- get ready to hack around this framework instead of + * using it. Otherwise, you can assume you have an initialized + * and empty connection to work with. (Please don't leave leftover + * gunk in the connection after the authentication transactions, or + * the POSTGRES routines that follow will be very unhappy.) + * 1. Write a set of routines that: + * let a client figure out what user/principal name to use + * send authentication information (client side) + * receive authentication information (server side) + * You can include both routines in this file, using #ifdef FRONTEND + * to separate them. + * 2. Edit libpq/pqcomm.h and assign a MsgType for your protocol. + * 3. Edit the static "struct authsvc" array and the generic + * {be,fe}_{get,set}auth{name,svc} routines in this file to reflect + * the new service. You may have to change the arguments of these + * routines; they basically just reflect what Kerberos v4 needs. + * 4. Hack on src/{,bin}/Makefile.global and src/{backend,libpq}/Makefile + * to add library and CFLAGS hooks -- basically, grep the Makefile + * hierarchy for KRBVERS to see where you need to add things. * - * Send mail to [email protected] if you have to make - * any changes to arguments, etc. Context diffs would be nice, too. + * Send mail to [email protected] if you have to make + * any changes to arguments, etc. Context diffs would be nice, too. * - * Someday, this cruft will go away and magically be replaced by a - * nice interface based on the GSS API or something. For now, though, - * there's no (stable) UNIX security API to work with... + * Someday, this cruft will go away and magically be replaced by a + * nice interface based on the GSS API or something. For now, though, + * there's no (stable) UNIX security API to work with... * */ #include <stdio.h> #include <string.h> -#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ +#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ #ifndef MAXHOSTNAMELEN -#include <netdb.h> /* for MAXHOSTNAMELEN on some */ +#include <netdb.h> /* for MAXHOSTNAMELEN on some */ #endif #include <pwd.h> -#include <ctype.h> /* isspace() declaration */ +#include <ctype.h> /* isspace() declaration */ -#include <sys/types.h> /* needed by in.h on Ultrix */ +#include <sys/types.h> /* needed by in.h on Ultrix */ #include <netinet/in.h> #include <arpa/inet.h> @@ -72,19 +72,19 @@ #include <libpq/hba.h> #include <libpq/password.h> -static int be_getauthsvc(MsgType msgtype); +static int be_getauthsvc(MsgType msgtype); /*---------------------------------------------------------------- * common definitions for generic fe/be routines *---------------------------------------------------------------- */ -struct authsvc { - char name[16]; /* service nickname (for command line) */ - MsgType msgtype; /* startup packet header type */ - int allowed; /* initially allowed (before command line - * option parsing)? - */ +struct authsvc +{ + char name[16]; /* service nickname (for command line) */ + MsgType msgtype; /* startup packet header type */ + int allowed; /* initially allowed (before command line + * option parsing)? */ }; /* @@ -99,9 +99,11 @@ struct authsvc { */ #if defined(HBA) -static int useHostBasedAuth = 1; +static int useHostBasedAuth = 1; + #else -static int useHostBasedAuth = 0; +static int useHostBasedAuth = 0; + #endif #if defined(KRB4) || defined(KRB5) || defined(HBA) @@ -111,19 +113,19 @@ static int useHostBasedAuth = 0; #endif static struct authsvc authsvcs[] = { - { "unauth", STARTUP_UNAUTH_MSG, UNAUTH_ALLOWED }, - { "hba", STARTUP_HBA_MSG, 1 }, - { "krb4", STARTUP_KRB4_MSG, 1 }, - { "krb5", STARTUP_KRB5_MSG, 1 }, -#if defined(KRB5) - { "kerberos", STARTUP_KRB5_MSG, 1 }, + {"unauth", STARTUP_UNAUTH_MSG, UNAUTH_ALLOWED}, + {"hba", STARTUP_HBA_MSG, 1}, + {"krb4", STARTUP_KRB4_MSG, 1}, + {"krb5", STARTUP_KRB5_MSG, 1}, +#if defined(KRB5) + {"kerberos", STARTUP_KRB5_MSG, 1}, #else - { "kerberos", STARTUP_KRB4_MSG, 1 }, + {"kerberos", STARTUP_KRB4_MSG, 1}, #endif - { "password", STARTUP_PASSWORD_MSG, 1 } + {"password", STARTUP_PASSWORD_MSG, 1} }; -static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); +static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); #ifdef KRB4 /* This has to be ifdef'd out because krb.h does exist. This needs @@ -138,11 +140,11 @@ static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); #ifdef FRONTEND /* moves to src/libpq/fe-auth.c */ -#else /* !FRONTEND */ +#else /* !FRONTEND */ /* * pg_krb4_recvauth -- server routine to receive authentication information - * from the client + * from the client * * Nothing unusual here, except that we compare the username obtained from * the client's setup packet to the authenticated name. (We have to retain @@ -151,77 +153,82 @@ static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); */ static int pg_krb4_recvauth(int sock, - struct sockaddr_in *laddr, - struct sockaddr_in *raddr, - char *username) + struct sockaddr_in * laddr, + struct sockaddr_in * raddr, + char *username) { - long krbopts = 0; /* one-way authentication */ - KTEXT_ST clttkt; - char instance[INST_SZ]; - AUTH_DAT auth_data; - Key_schedule key_sched; - char version[KRB_SENDAUTH_VLEN]; - int status; - - strcpy(instance, "*"); /* don't care, but arg gets expanded anyway */ - status = krb_recvauth(krbopts, - sock, - &clttkt, - PG_KRB_SRVNAM, - instance, - raddr, - laddr, - &auth_data, - PG_KRB_SRVTAB, - key_sched, - version); - if (status != KSUCCESS) { - sprintf(PQerrormsg, - "pg_krb4_recvauth: kerberos error: %s\n", - krb_err_txt[status]); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN)) { - sprintf(PQerrormsg, - "pg_krb4_recvauth: protocol version != \"%s\"\n", - PG_KRB4_VERSION); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - if (username && *username && - strncmp(username, auth_data.pname, NAMEDATALEN)) { - sprintf(PQerrormsg, - "pg_krb4_recvauth: name \"%s\" != \"%s\"\n", - username, - auth_data.pname); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - return(STATUS_OK); + long krbopts = 0;/* one-way authentication */ + KTEXT_ST clttkt; + char instance[INST_SZ]; + AUTH_DAT auth_data; + Key_schedule key_sched; + char version[KRB_SENDAUTH_VLEN]; + int status; + + strcpy(instance, "*"); /* don't care, but arg gets expanded + * anyway */ + status = krb_recvauth(krbopts, + sock, + &clttkt, + PG_KRB_SRVNAM, + instance, + raddr, + laddr, + &auth_data, + PG_KRB_SRVTAB, + key_sched, + version); + if (status != KSUCCESS) + { + sprintf(PQerrormsg, + "pg_krb4_recvauth: kerberos error: %s\n", + krb_err_txt[status]); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN)) + { + sprintf(PQerrormsg, + "pg_krb4_recvauth: protocol version != \"%s\"\n", + PG_KRB4_VERSION); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + if (username && *username && + strncmp(username, auth_data.pname, NAMEDATALEN)) + { + sprintf(PQerrormsg, + "pg_krb4_recvauth: name \"%s\" != \"%s\"\n", + username, + auth_data.pname); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + return (STATUS_OK); } -#endif /* !FRONTEND */ +#endif /* !FRONTEND */ #else static int pg_krb4_recvauth(int sock, - struct sockaddr_in *laddr, - struct sockaddr_in *raddr, - char *username) + struct sockaddr_in * laddr, + struct sockaddr_in * raddr, + char *username) { - sprintf(PQerrormsg, - "pg_krb4_recvauth: Kerberos not implemented on this " - "server.\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); + sprintf(PQerrormsg, + "pg_krb4_recvauth: Kerberos not implemented on this " + "server.\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); -return(STATUS_ERROR); + return (STATUS_ERROR); } -#endif /* KRB4 */ + +#endif /* KRB4 */ #ifdef KRB5 @@ -237,37 +244,37 @@ return(STATUS_ERROR); /* * pg_an_to_ln -- return the local name corresponding to an authentication - * name + * name * * XXX Assumes that the first aname component is the user name. This is NOT - * necessarily so, since an aname can actually be something out of your - * worst X.400 nightmare, like - * ORGANIZATION=U. C. Berkeley/NAME=Paul M. [email protected] - * Note that the MIT an_to_ln code does the same thing if you don't - * provide an aname mapping database...it may be a better idea to use - * krb5_an_to_ln, except that it punts if multiple components are found, - * and we can't afford to punt. + * necessarily so, since an aname can actually be something out of your + * worst X.400 nightmare, like + * ORGANIZATION=U. C. Berkeley/NAME=Paul M. [email protected] + * Note that the MIT an_to_ln code does the same thing if you don't + * provide an aname mapping database...it may be a better idea to use + * krb5_an_to_ln, except that it punts if multiple components are found, + * and we can't afford to punt. */ -static char * +static char * pg_an_to_ln(char *aname) { - char *p; - - if ((p = strchr(aname, '/')) || (p = strchr(aname, '@'))) - *p = '\0'; - return(aname); + char *p; + + if ((p = strchr(aname, '/')) || (p = strchr(aname, '@'))) + *p = '\0'; + return (aname); } #ifdef FRONTEND /* moves to src/libpq/fe-auth.c */ -#else /* !FRONTEND */ +#else /* !FRONTEND */ /* * pg_krb5_recvauth -- server routine to receive authentication information - * from the client + * from the client * * We still need to compare the username obtained from the client's setup - * packet to the authenticated name, as described in pg_krb4_recvauth. This + * packet to the authenticated name, as described in pg_krb4_recvauth. This * is a bit more problematic in v5, as described above in pg_an_to_ln. * * In addition, as described above in pg_krb5_sendauth, we still need to @@ -286,315 +293,348 @@ pg_an_to_ln(char *aname) */ static int pg_krb5_recvauth(int sock, - struct sockaddr_in *laddr, - struct sockaddr_in *raddr, - char *username) + struct sockaddr_in * laddr, + struct sockaddr_in * raddr, + char *username) { - char servbuf[MAXHOSTNAMELEN + 1 + - sizeof(PG_KRB_SRVNAM)]; - char *hostp, *kusername = (char *) NULL; - krb5_error_code code; - krb5_principal client, server; - krb5_address sender_addr; - krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL; - krb5_pointer keyprocarg = (krb5_pointer) NULL; - - /* - * Set up server side -- since we have no ticket file to make this - * easy, we construct our own name and parse it. See note on - * canonicalization above. - */ - strcpy(servbuf, PG_KRB_SRVNAM); - *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/'; - if (gethostname(++hostp, MAXHOSTNAMELEN) < 0) - strcpy(hostp, "localhost"); - if (hostp = strchr(hostp, '.')) - *hostp = '\0'; - if (code = krb5_parse_name(servbuf, &server)) { - sprintf(PQerrormsg, - "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n", - code); - com_err("pg_krb5_recvauth", code, "in krb5_parse_name"); - return(STATUS_ERROR); - } - - /* - * krb5_sendauth needs this to verify the address in the client - * authenticator. - */ - sender_addr.addrtype = raddr->sin_family; - sender_addr.length = sizeof(raddr->sin_addr); - sender_addr.contents = (krb5_octet *) &(raddr->sin_addr); - - if (strcmp(PG_KRB_SRVTAB, "")) { - keyproc = krb5_kt_read_service_key; - keyprocarg = PG_KRB_SRVTAB; - } - - if (code = krb5_recvauth((krb5_pointer) &sock, - PG_KRB5_VERSION, - server, - &sender_addr, - (krb5_pointer) NULL, - keyproc, - keyprocarg, - (char *) NULL, - (krb5_int32 *) NULL, - &client, - (krb5_ticket **) NULL, - (krb5_authenticator **) NULL)) { - sprintf(PQerrormsg, - "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n", - code); - com_err("pg_krb5_recvauth", code, "in krb5_recvauth"); + char servbuf[MAXHOSTNAMELEN + 1 + + sizeof(PG_KRB_SRVNAM)]; + char *hostp, + *kusername = (char *) NULL; + krb5_error_code code; + krb5_principal client, + server; + krb5_address sender_addr; + krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL; + krb5_pointer keyprocarg = (krb5_pointer) NULL; + + /* + * Set up server side -- since we have no ticket file to make this + * easy, we construct our own name and parse it. See note on + * canonicalization above. + */ + strcpy(servbuf, PG_KRB_SRVNAM); + *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/'; + if (gethostname(++hostp, MAXHOSTNAMELEN) < 0) + strcpy(hostp, "localhost"); + if (hostp = strchr(hostp, '.')) + *hostp = '\0'; + if (code = krb5_parse_name(servbuf, &server)) + { + sprintf(PQerrormsg, + "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n", + code); + com_err("pg_krb5_recvauth", code, "in krb5_parse_name"); + return (STATUS_ERROR); + } + + /* + * krb5_sendauth needs this to verify the address in the client + * authenticator. + */ + sender_addr.addrtype = raddr->sin_family; + sender_addr.length = sizeof(raddr->sin_addr); + sender_addr.contents = (krb5_octet *) & (raddr->sin_addr); + + if (strcmp(PG_KRB_SRVTAB, "")) + { + keyproc = krb5_kt_read_service_key; + keyprocarg = PG_KRB_SRVTAB; + } + + if (code = krb5_recvauth((krb5_pointer) & sock, + PG_KRB5_VERSION, + server, + &sender_addr, + (krb5_pointer) NULL, + keyproc, + keyprocarg, + (char *) NULL, + (krb5_int32 *) NULL, + &client, + (krb5_ticket **) NULL, + (krb5_authenticator **) NULL)) + { + sprintf(PQerrormsg, + "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n", + code); + com_err("pg_krb5_recvauth", code, "in krb5_recvauth"); + krb5_free_principal(server); + return (STATUS_ERROR); + } krb5_free_principal(server); - return(STATUS_ERROR); - } - krb5_free_principal(server); - - /* - * The "client" structure comes out of the ticket and is therefore - * authenticated. Use it to check the username obtained from the - * postmaster startup packet. - */ - if ((code = krb5_unparse_name(client, &kusername))) { - sprintf(PQerrormsg, - "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n", - code); - com_err("pg_krb5_recvauth", code, "in krb5_unparse_name"); + + /* + * The "client" structure comes out of the ticket and is therefore + * authenticated. Use it to check the username obtained from the + * postmaster startup packet. + */ + if ((code = krb5_unparse_name(client, &kusername))) + { + sprintf(PQerrormsg, + "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n", + code); + com_err("pg_krb5_recvauth", code, "in krb5_unparse_name"); + krb5_free_principal(client); + return (STATUS_ERROR); + } krb5_free_principal(client); - return(STATUS_ERROR); - } - krb5_free_principal(client); - if (!kusername) { - sprintf(PQerrormsg, - "pg_krb5_recvauth: could not decode username\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - kusername = pg_an_to_ln(kusername); - if (username && strncmp(username, kusername, NAMEDATALEN)) { - sprintf(PQerrormsg, - "pg_krb5_recvauth: name \"%s\" != \"%s\"\n", - username, kusername); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); + if (!kusername) + { + sprintf(PQerrormsg, + "pg_krb5_recvauth: could not decode username\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + kusername = pg_an_to_ln(kusername); + if (username && strncmp(username, kusername, NAMEDATALEN)) + { + sprintf(PQerrormsg, + "pg_krb5_recvauth: name \"%s\" != \"%s\"\n", + username, kusername); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + free(kusername); + return (STATUS_ERROR); + } free(kusername); - return(STATUS_ERROR); - } - free(kusername); - return(STATUS_OK); + return (STATUS_OK); } -#endif /* !FRONTEND */ +#endif /* !FRONTEND */ #else static int pg_krb5_recvauth(int sock, - struct sockaddr_in *laddr, - struct sockaddr_in *raddr, - char *username) + struct sockaddr_in * laddr, + struct sockaddr_in * raddr, + char *username) { - sprintf(PQerrormsg, - "pg_krb5_recvauth: Kerberos not implemented on this " - "server.\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); + sprintf(PQerrormsg, + "pg_krb5_recvauth: Kerberos not implemented on this " + "server.\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); -return(STATUS_ERROR); + return (STATUS_ERROR); } -#endif /* KRB5 */ + +#endif /* KRB5 */ static int -pg_password_recvauth(Port *port, char *database, char *DataDir) +pg_password_recvauth(Port * port, char *database, char *DataDir) { - PacketBuf buf; - char *user, *password; - - if(PacketReceive(port, &buf, BLOCKING) != STATUS_OK) { - sprintf(PQerrormsg, - "pg_password_recvauth: failed to receive authentication packet.\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return STATUS_ERROR; - } + PacketBuf buf; + char *user, + *password; + + if (PacketReceive(port, &buf, BLOCKING) != STATUS_OK) + { + sprintf(PQerrormsg, + "pg_password_recvauth: failed to receive authentication packet.\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return STATUS_ERROR; + } - user = buf.data; - password = buf.data + strlen(user) + 1; + user = buf.data; + password = buf.data + strlen(user) + 1; - return verify_password(user, password, port, database, DataDir); + return verify_password(user, password, port, database, DataDir); } /* * be_recvauth -- server demux routine for incoming authentication information */ int -be_recvauth(MsgType msgtype_arg, Port *port, char *username, StartupInfo* sp) +be_recvauth(MsgType msgtype_arg, Port * port, char *username, StartupInfo * sp) { - MsgType msgtype; - - /* A message type of STARTUP_MSG (which once upon a time was the only - startup message type) means user wants us to choose. "unauth" is - what used to be the only choice, but installation may choose "hba" - instead. - */ - if (msgtype_arg == STARTUP_MSG) { - if(useHostBasedAuth) - msgtype = STARTUP_HBA_MSG; - else - msgtype = STARTUP_UNAUTH_MSG; - } else - msgtype = msgtype_arg; - - - if (!username) { - sprintf(PQerrormsg, - "be_recvauth: no user name passed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - if (!port) { - sprintf(PQerrormsg, - "be_recvauth: no port structure passed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - - switch (msgtype) { - case STARTUP_KRB4_MSG: - if (!be_getauthsvc(msgtype)) { - sprintf(PQerrormsg, - "be_recvauth: krb4 authentication disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr, - username) != STATUS_OK) { - sprintf(PQerrormsg, - "be_recvauth: krb4 authentication failed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - break; - case STARTUP_KRB5_MSG: - if (!be_getauthsvc(msgtype)) { - sprintf(PQerrormsg, - "be_recvauth: krb5 authentication disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr, - username) != STATUS_OK) { - sprintf(PQerrormsg, - "be_recvauth: krb5 authentication failed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - break; - case STARTUP_UNAUTH_MSG: - if (!be_getauthsvc(msgtype)) { - sprintf(PQerrormsg, - "be_recvauth: " - "unauthenticated connections disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - break; - case STARTUP_HBA_MSG: - if (hba_recvauth(port, sp->database, sp->user, DataDir) != STATUS_OK) { - sprintf(PQerrormsg, - "be_recvauth: host-based authentication failed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - break; - case STARTUP_PASSWORD_MSG: - if(!be_getauthsvc(msgtype)) { - sprintf(PQerrormsg, - "be_recvauth: " - "plaintext password authentication disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); + MsgType msgtype; + + /* + * A message type of STARTUP_MSG (which once upon a time was the only + * startup message type) means user wants us to choose. "unauth" is + * what used to be the only choice, but installation may choose "hba" + * instead. + */ + if (msgtype_arg == STARTUP_MSG) + { + if (useHostBasedAuth) + msgtype = STARTUP_HBA_MSG; + else + msgtype = STARTUP_UNAUTH_MSG; + } + else + msgtype = msgtype_arg; + + + if (!username) + { + sprintf(PQerrormsg, + "be_recvauth: no user name passed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + if (!port) + { + sprintf(PQerrormsg, + "be_recvauth: no port structure passed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); } - if(pg_password_recvauth(port, sp->database, DataDir) != STATUS_OK) { - /* pg_password_recvauth or lower-level routines have already set */ - /* the error message */ - return(STATUS_ERROR); + + switch (msgtype) + { + case STARTUP_KRB4_MSG: + if (!be_getauthsvc(msgtype)) + { + sprintf(PQerrormsg, + "be_recvauth: krb4 authentication disallowed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr, + username) != STATUS_OK) + { + sprintf(PQerrormsg, + "be_recvauth: krb4 authentication failed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + break; + case STARTUP_KRB5_MSG: + if (!be_getauthsvc(msgtype)) + { + sprintf(PQerrormsg, + "be_recvauth: krb5 authentication disallowed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr, + username) != STATUS_OK) + { + sprintf(PQerrormsg, + "be_recvauth: krb5 authentication failed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + break; + case STARTUP_UNAUTH_MSG: + if (!be_getauthsvc(msgtype)) + { + sprintf(PQerrormsg, + "be_recvauth: " + "unauthenticated connections disallowed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + break; + case STARTUP_HBA_MSG: + if (hba_recvauth(port, sp->database, sp->user, DataDir) != STATUS_OK) + { + sprintf(PQerrormsg, + "be_recvauth: host-based authentication failed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + break; + case STARTUP_PASSWORD_MSG: + if (!be_getauthsvc(msgtype)) + { + sprintf(PQerrormsg, + "be_recvauth: " + "plaintext password authentication disallowed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + if (pg_password_recvauth(port, sp->database, DataDir) != STATUS_OK) + { + + /* + * pg_password_recvauth or lower-level routines have already + * set + */ + /* the error message */ + return (STATUS_ERROR); + } + break; + default: + sprintf(PQerrormsg, + "be_recvauth: unrecognized message type: %d\n", + msgtype); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); } - break; - default: - sprintf(PQerrormsg, - "be_recvauth: unrecognized message type: %d\n", - msgtype); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - return(STATUS_OK); + return (STATUS_OK); } /* * be_setauthsvc -- enable/disable the authentication services currently - * selected for use by the backend + * selected for use by the backend * be_getauthsvc -- returns whether a particular authentication system - * (indicated by its message type) is permitted by the - * current selections + * (indicated by its message type) is permitted by the + * current selections * * be_setauthsvc encodes the command-line syntax that - * -a "<service-name>" + * -a "<service-name>" * enables a service, whereas - * -a "no<service-name>" + * -a "no<service-name>" * disables it. */ void be_setauthsvc(char *name) { - int i, j; - int turnon = 1; - - if (!name) - return; - if (!strncmp("no", name, 2)) { - turnon = 0; - name += 2; - } - if (name[0] == '\0') - return; - for (i = 0; i < n_authsvcs; ++i) - if (!strcmp(name, authsvcs[i].name)) { - for (j = 0; j < n_authsvcs; ++j) - if (authsvcs[j].msgtype == authsvcs[i].msgtype) - authsvcs[j].allowed = turnon; - break; + int i, + j; + int turnon = 1; + + if (!name) + return; + if (!strncmp("no", name, 2)) + { + turnon = 0; + name += 2; } - if (i == n_authsvcs) { - sprintf(PQerrormsg, - "be_setauthsvc: invalid name %s, ignoring...\n", - name); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } - return; + if (name[0] == '\0') + return; + for (i = 0; i < n_authsvcs; ++i) + if (!strcmp(name, authsvcs[i].name)) + { + for (j = 0; j < n_authsvcs; ++j) + if (authsvcs[j].msgtype == authsvcs[i].msgtype) + authsvcs[j].allowed = turnon; + break; + } + if (i == n_authsvcs) + { + sprintf(PQerrormsg, + "be_setauthsvc: invalid name %s, ignoring...\n", + name); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + return; } static int be_getauthsvc(MsgType msgtype) { - int i; - - for (i = 0; i < n_authsvcs; ++i) - if (msgtype == authsvcs[i].msgtype) - return(authsvcs[i].allowed); - return(0); + int i; + + for (i = 0; i < n_authsvcs; ++i) + if (msgtype == authsvcs[i].msgtype) + return (authsvcs[i].allowed); + return (0); } diff --git a/src/backend/libpq/be-dumpdata.c b/src/backend/libpq/be-dumpdata.c index b47e79fa65c..db0a99141d6 100644 --- a/src/backend/libpq/be-dumpdata.c +++ b/src/backend/libpq/be-dumpdata.c @@ -1,32 +1,32 @@ /*------------------------------------------------------------------------- * * be-dumpdata.c-- - * support for collection of returned tuples from an internal - * PQ call into a backend buffer. + * support for collection of returned tuples from an internal + * PQ call into a backend buffer. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-dumpdata.c,v 1.5 1997/08/18 20:52:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-dumpdata.c,v 1.6 1997/09/07 04:42:12 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * be_portalinit - initialize backend portal administration - * be_portalpush - add a portal to the top of the portal stack - * be_portalpop - remove portal on the top of the stack & return it - * be_currentportal - return the top portal on the portal stack - * be_newportal - return a new portal. - * be_portalinit - initialize backend portal expected to hold results. - * be_printtup - add a tuple to a backend portal + * be_portalinit - initialize backend portal administration + * be_portalpush - add a portal to the top of the portal stack + * be_portalpop - remove portal on the top of the stack & return it + * be_currentportal - return the top portal on the portal stack + * be_newportal - return a new portal. + * be_portalinit - initialize backend portal expected to hold results. + * be_printtup - add a tuple to a backend portal * * NOTES - * Since backend user-defined operators can call queries - * which in turn call user-defined operators can call queries... - * we have to keep track of portals on a stack. BeginCommand() - * puts portals on the stack and the PQ functions remove them. + * Since backend user-defined operators can call queries + * which in turn call user-defined operators can call queries... + * we have to keep track of portals on a stack. BeginCommand() + * puts portals on the stack and the PQ functions remove them. * */ #include <string.h> @@ -48,274 +48,288 @@ #include <access/printtup.h> /* ---------------- - * backend portal stack for recursive PQexec calls + * backend portal stack for recursive PQexec calls * ---------------- */ -static Dllist *be_portalstack; +static Dllist *be_portalstack; /* ---------------- - * be_portalinit - initialize backend portal administration + * be_portalinit - initialize backend portal administration * - * This is called once from InitPostgres() to initialize - * the portal stack. + * This is called once from InitPostgres() to initialize + * the portal stack. * ---------------- */ void be_portalinit(void) { - be_portalstack = DLNewList(); + be_portalstack = DLNewList(); } /* ---------------- - * be_portalpush - add a portal to the top of the portal stack + * be_portalpush - add a portal to the top of the portal stack * - * used by BeginCommand() + * used by BeginCommand() * ---------------- */ void -be_portalpush(PortalEntry *entry) +be_portalpush(PortalEntry * entry) { - DLAddTail(be_portalstack, DLNewElem(entry)); + DLAddTail(be_portalstack, DLNewElem(entry)); } /* ---------------- - * be_portalpop - remove the portal on the top of the stack & return it + * be_portalpop - remove the portal on the top of the stack & return it * - * used by PQexec() + * used by PQexec() * ---------------- */ -PortalEntry * +PortalEntry * be_portalpop(void) { - PortalEntry *p; - Dlelem* elt; - elt = DLRemTail(be_portalstack); + PortalEntry *p; + Dlelem *elt; + + elt = DLRemTail(be_portalstack); + + p = (elt ? (PortalEntry *) DLE_VAL(elt) : NULL); + DLFreeElem(elt); + return p; - p = (elt ? (PortalEntry*)DLE_VAL(elt) : NULL); - DLFreeElem(elt); - return p; - } /* ---------------- - * be_currentportal - return the top portal on the portal stack + * be_currentportal - return the top portal on the portal stack * - * used by be_printtup() + * used by be_printtup() * ---------------- */ -PortalEntry * +PortalEntry * be_currentportal(void) { - Dlelem* elt; - elt = DLGetTail(be_portalstack); - return (elt ? (PortalEntry*)DLE_VAL(elt) : NULL); + Dlelem *elt; + + elt = DLGetTail(be_portalstack); + return (elt ? (PortalEntry *) DLE_VAL(elt) : NULL); } /* ---------------- - * be_newportal - return a new portal. + * be_newportal - return a new portal. * - * If the user-defined function does not specify a portal name, - * we generate a unique one. Names are generated from a combination - * of a postgres oid and an integer counter which is incremented - * every time we ask for a local portal. + * If the user-defined function does not specify a portal name, + * we generate a unique one. Names are generated from a combination + * of a postgres oid and an integer counter which is incremented + * every time we ask for a local portal. * - * used by BeginCommand() + * used by BeginCommand() * ---------------- */ -static Oid be_portaloid; +static Oid be_portaloid; static u_int be_portalcnt = 0; -PortalEntry * -be_newportal(void) +PortalEntry * +be_newportal(void) { - PortalEntry *entry; - char buf[PortalNameLength]; - - /* ---------------- - * generate a new name - * ---------------- - */ - if (be_portalcnt == 0) - be_portaloid = newoid(); - be_portalcnt++; - sprintf(buf, "be_%d_%d", be_portaloid, be_portalcnt); - - /* ---------------- - * initialize the new portal entry and keep track - * of the current memory context for be_printtup(). - * This is important - otherwise whatever we allocate - * will go away and the contents of the portal after - * PQexec() returns will be meaningless. - * ---------------- - */ - entry = pbuf_setup(buf); - entry->portalcxt = (Pointer) CurrentMemoryContext; - - return entry; + PortalEntry *entry; + char buf[PortalNameLength]; + + /* ---------------- + * generate a new name + * ---------------- + */ + if (be_portalcnt == 0) + be_portaloid = newoid(); + be_portalcnt++; + sprintf(buf, "be_%d_%d", be_portaloid, be_portalcnt); + + /* ---------------- + * initialize the new portal entry and keep track + * of the current memory context for be_printtup(). + * This is important - otherwise whatever we allocate + * will go away and the contents of the portal after + * PQexec() returns will be meaningless. + * ---------------- + */ + entry = pbuf_setup(buf); + entry->portalcxt = (Pointer) CurrentMemoryContext; + + return entry; } /* ---------------- - * be_typeinit - initialize backend portal expected to hold - * query results. + * be_typeinit - initialize backend portal expected to hold + * query results. * - * used by BeginCommand() + * used by BeginCommand() * ---------------- */ void -be_typeinit(PortalEntry *entry, - TupleDesc tupDesc, - int natts) +be_typeinit(PortalEntry * entry, + TupleDesc tupDesc, + int natts) { - PortalBuffer *portal; - GroupBuffer *group; - int i; - AttributeTupleForm *attrs = tupDesc->attrs; - - /* ---------------- - * add a new portal group to the portal - * ---------------- - */ - portal = entry->portal; - portal->no_groups++; - portal->groups = group = pbuf_addGroup(portal); - group->no_fields = natts; - - /* ---------------- - * initialize portal group type info - * ---------------- - */ - if (natts > 0) { - group->types = pbuf_addTypes(natts); - for (i = 0; i < natts; ++i) { - strncpy(group->types[i].name, attrs[i]->attname.data, NAMEDATALEN); - group->types[i].adtid = attrs[i]->atttypid; - group->types[i].adtsize = attrs[i]->attlen; + PortalBuffer *portal; + GroupBuffer *group; + int i; + AttributeTupleForm *attrs = tupDesc->attrs; + + /* ---------------- + * add a new portal group to the portal + * ---------------- + */ + portal = entry->portal; + portal->no_groups++; + portal->groups = group = pbuf_addGroup(portal); + group->no_fields = natts; + + /* ---------------- + * initialize portal group type info + * ---------------- + */ + if (natts > 0) + { + group->types = pbuf_addTypes(natts); + for (i = 0; i < natts; ++i) + { + strncpy(group->types[i].name, attrs[i]->attname.data, NAMEDATALEN); + group->types[i].adtid = attrs[i]->atttypid; + group->types[i].adtsize = attrs[i]->attlen; + } } - } } /* ---------------- - * be_printtup - add a tuple to a backend portal + * be_printtup - add a tuple to a backend portal * - * used indirectly by ExecRetrieve() + * used indirectly by ExecRetrieve() * - * This code is pretty much copied from printtup(), dump_type() - * and dump_data(). -cim 2/12/91 + * This code is pretty much copied from printtup(), dump_type() + * and dump_data(). -cim 2/12/91 * ---------------- */ void be_printtup(HeapTuple tuple, TupleDesc typeinfo) { - int i; - char *attr; - bool isnull; - Oid typoutput; - - PortalEntry *entry = NULL; - PortalBuffer *portal = NULL; - GroupBuffer *group = NULL ; - TupleBlock *tuples = NULL; - char **values; - int *lengths; - - MemoryContext savecxt; - - /* ---------------- - * get the current portal and group - * ---------------- - */ - entry = be_currentportal(); - portal = entry->portal; - group = portal->groups; - - /* ---------------- - * switch to the portal's memory context so that - * the tuples we allocate are returned to the user. - * ---------------- - */ - savecxt = MemoryContextSwitchTo((MemoryContext)entry->portalcxt); - - /* ---------------- - * If no tuple block yet, allocate one. - * If the current block is full, allocate another one. - * ---------------- - */ - if (group->tuples == NULL) { - tuples = group->tuples = pbuf_addTuples(); - tuples->tuple_index = 0; - } else { - tuples = group->tuples; - /* walk to the end of the linked list of TupleBlocks */ - while (tuples->next) - tuples = tuples->next; - /* now, tuples is the last TupleBlock, check to see if it is full. - If so, allocate a new TupleBlock and add it to the end of - the chain */ - - if (tuples->tuple_index == TupleBlockSize) { - tuples->next = pbuf_addTuples(); - tuples = tuples->next; - tuples->tuple_index = 0; + int i; + char *attr; + bool isnull; + Oid typoutput; + + PortalEntry *entry = NULL; + PortalBuffer *portal = NULL; + GroupBuffer *group = NULL; + TupleBlock *tuples = NULL; + char **values; + int *lengths; + + MemoryContext savecxt; + + /* ---------------- + * get the current portal and group + * ---------------- + */ + entry = be_currentportal(); + portal = entry->portal; + group = portal->groups; + + /* ---------------- + * switch to the portal's memory context so that + * the tuples we allocate are returned to the user. + * ---------------- + */ + savecxt = MemoryContextSwitchTo((MemoryContext) entry->portalcxt); + + /* ---------------- + * If no tuple block yet, allocate one. + * If the current block is full, allocate another one. + * ---------------- + */ + if (group->tuples == NULL) + { + tuples = group->tuples = pbuf_addTuples(); + tuples->tuple_index = 0; } - } - - /* ---------------- - * Allocate space for a tuple. - * ---------------- - */ - tuples->values[tuples->tuple_index] = pbuf_addTuple(tuple->t_natts); - tuples->lengths[tuples->tuple_index] = pbuf_addTupleValueLengths(tuple->t_natts); - /* ---------------- - * copy printable representations of the tuple's attributes - * to the portal. - * - * This seems silly, because the user's function which is calling - * PQexec() or PQfn() will probably just convert this back into the - * internal form anyways, but the point here is to provide a uniform - * libpq interface and this is how the fe libpq interface currently - * works. Pretty soon we'll have to add code to let the fe or be - * select the desired data representation and then deal with that. - * This should not be too hard, as there already exist typrecieve() - * and typsend() procedures for user-defined types (see pg_type.h) - * -cim 2/11/91 - * ---------------- - */ - - values = tuples->values[tuples->tuple_index]; - lengths = tuples->lengths[tuples->tuple_index]; - - for (i = 0; i < tuple->t_natts; i++) { - attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull); - typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); - - lengths[i] = typeinfo->attrs[i]->attlen; - - if (lengths[i] == -1) /* variable length attribute */ - if (!isnull) - lengths[i] = VARSIZE(attr)-VARHDRSZ; - else - lengths[i] = 0; - - if (!isnull && OidIsValid(typoutput)) { - values[i] = fmgr(typoutput, attr, gettypelem(typeinfo->attrs[i]->atttypid)); - } else - values[i] = NULL; - - } - - /* ---------------- - * increment tuple group counters - * ---------------- - */ - portal->no_tuples++; - group->no_tuples++; - tuples->tuple_index++; - - /* ---------------- - * return to the original memory context - * ---------------- - */ - MemoryContextSwitchTo(savecxt); + else + { + tuples = group->tuples; + /* walk to the end of the linked list of TupleBlocks */ + while (tuples->next) + tuples = tuples->next; + + /* + * now, tuples is the last TupleBlock, check to see if it is full. + * If so, allocate a new TupleBlock and add it to the end of the + * chain + */ + + if (tuples->tuple_index == TupleBlockSize) + { + tuples->next = pbuf_addTuples(); + tuples = tuples->next; + tuples->tuple_index = 0; + } + } + + /* ---------------- + * Allocate space for a tuple. + * ---------------- + */ + tuples->values[tuples->tuple_index] = pbuf_addTuple(tuple->t_natts); + tuples->lengths[tuples->tuple_index] = pbuf_addTupleValueLengths(tuple->t_natts); + /* ---------------- + * copy printable representations of the tuple's attributes + * to the portal. + * + * This seems silly, because the user's function which is calling + * PQexec() or PQfn() will probably just convert this back into the + * internal form anyways, but the point here is to provide a uniform + * libpq interface and this is how the fe libpq interface currently + * works. Pretty soon we'll have to add code to let the fe or be + * select the desired data representation and then deal with that. + * This should not be too hard, as there already exist typrecieve() + * and typsend() procedures for user-defined types (see pg_type.h) + * -cim 2/11/91 + * ---------------- + */ + + values = tuples->values[tuples->tuple_index]; + lengths = tuples->lengths[tuples->tuple_index]; + + for (i = 0; i < tuple->t_natts; i++) + { + attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull); + typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); + + lengths[i] = typeinfo->attrs[i]->attlen; + + if (lengths[i] == -1) /* variable length attribute */ + if (!isnull) + lengths[i] = VARSIZE(attr) - VARHDRSZ; + else + lengths[i] = 0; + + if (!isnull && OidIsValid(typoutput)) + { + values[i] = fmgr(typoutput, attr, gettypelem(typeinfo->attrs[i]->atttypid)); + } + else + values[i] = NULL; + + } + + /* ---------------- + * increment tuple group counters + * ---------------- + */ + portal->no_tuples++; + group->no_tuples++; + tuples->tuple_index++; + + /* ---------------- + * return to the original memory context + * ---------------- + */ + MemoryContextSwitchTo(savecxt); } diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index 58a827838db..e3a464b087f 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -1,24 +1,24 @@ /*------------------------------------------------------------------------- * * be-fsstubs.c-- - * support for filesystem operations on large objects + * support for filesystem operations on large objects * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.12 1997/08/12 22:52:48 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.13 1997/09/07 04:42:15 momjian Exp $ * * NOTES - * This should be moved to a more appropriate place. It is here - * for lack of a better place. + * This should be moved to a more appropriate place. It is here + * for lack of a better place. * - * Builtin functions for open/close/read/write operations on large objects. + * Builtin functions for open/close/read/write operations on large objects. * - * These functions operate in the current portal variable context, which - * means the large object descriptors hang around between transactions and - * are not deallocated until explicitly closed, or until the portal is - * closed. + * These functions operate in the current portal variable context, which + * means the large object descriptors hang around between transactions and + * are not deallocated until explicitly closed, or until the portal is + * closed. *------------------------------------------------------------------------- */ @@ -37,340 +37,364 @@ #include <utils/memutils.h> #include <lib/fstack.h> #include <utils/mcxt.h> -#include <storage/fd.h> /* for O_ */ +#include <storage/fd.h> /* for O_ */ #include <storage/large_object.h> #include <libpq/be-fsstubs.h> /*#define FSDB 1*/ #define MAX_LOBJ_FDS 256 -static LargeObjectDesc *cookies[MAX_LOBJ_FDS]; +static LargeObjectDesc *cookies[MAX_LOBJ_FDS]; static GlobalMemory fscxt = NULL; -static int newLOfd(LargeObjectDesc *lobjCookie); -static void deleteLOfd(int fd); +static int newLOfd(LargeObjectDesc * lobjCookie); +static void deleteLOfd(int fd); /***************************************************************************** - * File Interfaces for Large Objects + * File Interfaces for Large Objects *****************************************************************************/ int lo_open(Oid lobjId, int mode) { - LargeObjectDesc *lobjDesc; - int fd; - MemoryContext currentContext; - + LargeObjectDesc *lobjDesc; + int fd; + MemoryContext currentContext; + #if FSDB - elog(NOTICE,"LOopen(%d,%d)",lobjId,mode); + elog(NOTICE, "LOopen(%d,%d)", lobjId, mode); #endif - if (fscxt == NULL) { - fscxt = CreateGlobalMemory("Filesystem"); - } - currentContext = MemoryContextSwitchTo((MemoryContext)fscxt); + if (fscxt == NULL) + { + fscxt = CreateGlobalMemory("Filesystem"); + } + currentContext = MemoryContextSwitchTo((MemoryContext) fscxt); - lobjDesc = inv_open(lobjId, mode); - - if (lobjDesc == NULL) { /* lookup failed */ - MemoryContextSwitchTo(currentContext); -#if FSDB - elog(NOTICE,"cannot open large object %d", lobjId); + lobjDesc = inv_open(lobjId, mode); + + if (lobjDesc == NULL) + { /* lookup failed */ + MemoryContextSwitchTo(currentContext); +#if FSDB + elog(NOTICE, "cannot open large object %d", lobjId); #endif - return -1; - } - - fd = newLOfd(lobjDesc); + return -1; + } + + fd = newLOfd(lobjDesc); - /* switch context back to orig. */ - MemoryContextSwitchTo(currentContext); + /* switch context back to orig. */ + MemoryContextSwitchTo(currentContext); - return fd; + return fd; } int lo_close(int fd) { - MemoryContext currentContext; - - if (fd >= MAX_LOBJ_FDS) { - elog(WARN,"lo_close: large obj descriptor (%d) out of range", fd); - return -2; - } - if (cookies[fd] == NULL) { - elog(WARN,"lo_close: invalid large obj descriptor (%d)", fd); - return -3; - } + MemoryContext currentContext; + + if (fd >= MAX_LOBJ_FDS) + { + elog(WARN, "lo_close: large obj descriptor (%d) out of range", fd); + return -2; + } + if (cookies[fd] == NULL) + { + elog(WARN, "lo_close: invalid large obj descriptor (%d)", fd); + return -3; + } #if FSDB - elog(NOTICE,"LOclose(%d)",fd); + elog(NOTICE, "LOclose(%d)", fd); #endif - Assert(fscxt != NULL); - currentContext = MemoryContextSwitchTo((MemoryContext)fscxt); + Assert(fscxt != NULL); + currentContext = MemoryContextSwitchTo((MemoryContext) fscxt); - inv_close(cookies[fd]); + inv_close(cookies[fd]); - MemoryContextSwitchTo(currentContext); + MemoryContextSwitchTo(currentContext); - deleteLOfd(fd); - return 0; + deleteLOfd(fd); + return 0; } /* - * We assume the large object supports byte oriented reads and seeks so - * that our work is easier. + * We assume the large object supports byte oriented reads and seeks so + * that our work is easier. */ int lo_read(int fd, char *buf, int len) { - Assert(cookies[fd]!=NULL); - return inv_read(cookies[fd], buf, len); + Assert(cookies[fd] != NULL); + return inv_read(cookies[fd], buf, len); } int lo_write(int fd, char *buf, int len) { - Assert(cookies[fd]!=NULL); - return inv_write(cookies[fd], buf, len); + Assert(cookies[fd] != NULL); + return inv_write(cookies[fd], buf, len); } int lo_lseek(int fd, int offset, int whence) { - MemoryContext currentContext; - int ret; + MemoryContext currentContext; + int ret; - if (fd >= MAX_LOBJ_FDS) { - elog(WARN,"lo_seek: large obj descriptor (%d) out of range", fd); - return -2; - } + if (fd >= MAX_LOBJ_FDS) + { + elog(WARN, "lo_seek: large obj descriptor (%d) out of range", fd); + return -2; + } - currentContext = MemoryContextSwitchTo((MemoryContext)fscxt); + currentContext = MemoryContextSwitchTo((MemoryContext) fscxt); - ret = inv_seek(cookies[fd], offset, whence); + ret = inv_seek(cookies[fd], offset, whence); - MemoryContextSwitchTo(currentContext); + MemoryContextSwitchTo(currentContext); - return ret; + return ret; } Oid lo_creat(int mode) { - LargeObjectDesc *lobjDesc; - MemoryContext currentContext; - Oid lobjId; - - if (fscxt == NULL) { - fscxt = CreateGlobalMemory("Filesystem"); - } - - currentContext = MemoryContextSwitchTo((MemoryContext)fscxt); - - lobjDesc = inv_create(mode); - - if (lobjDesc == NULL) { + LargeObjectDesc *lobjDesc; + MemoryContext currentContext; + Oid lobjId; + + if (fscxt == NULL) + { + fscxt = CreateGlobalMemory("Filesystem"); + } + + currentContext = MemoryContextSwitchTo((MemoryContext) fscxt); + + lobjDesc = inv_create(mode); + + if (lobjDesc == NULL) + { + MemoryContextSwitchTo(currentContext); + return InvalidOid; + } + + lobjId = lobjDesc->heap_r->rd_id; + + inv_close(lobjDesc); + + /* switch context back to original memory context */ MemoryContextSwitchTo(currentContext); - return InvalidOid; - } - - lobjId = lobjDesc->heap_r->rd_id; - - inv_close(lobjDesc); - - /* switch context back to original memory context */ - MemoryContextSwitchTo(currentContext); - - return lobjId; + + return lobjId; } int lo_tell(int fd) { - if (fd >= MAX_LOBJ_FDS) { - elog(WARN,"lo_tell: large object descriptor (%d) out of range",fd); - return -2; - } - if (cookies[fd] == NULL) { - elog(WARN,"lo_tell: invalid large object descriptor (%d)",fd); - return -3; - } - return inv_tell(cookies[fd]); + if (fd >= MAX_LOBJ_FDS) + { + elog(WARN, "lo_tell: large object descriptor (%d) out of range", fd); + return -2; + } + if (cookies[fd] == NULL) + { + elog(WARN, "lo_tell: invalid large object descriptor (%d)", fd); + return -3; + } + return inv_tell(cookies[fd]); } int lo_unlink(Oid lobjId) { - return (inv_destroy(lobjId)); + return (inv_destroy(lobjId)); } /***************************************************************************** - * Read/Write using varlena + * Read/Write using varlena *****************************************************************************/ struct varlena * loread(int fd, int len) { - struct varlena *retval; - int totalread = 0; - - retval = (struct varlena *)palloc(sizeof(int32) + len); - totalread = lo_read(fd, VARDATA(retval), len); - VARSIZE(retval) = totalread + sizeof(int32); - - return retval; + struct varlena *retval; + int totalread = 0; + + retval = (struct varlena *) palloc(sizeof(int32) + len); + totalread = lo_read(fd, VARDATA(retval), len); + VARSIZE(retval) = totalread + sizeof(int32); + + return retval; } -int lowrite(int fd, struct varlena *wbuf) +int +lowrite(int fd, struct varlena * wbuf) { - int totalwritten; - int bytestowrite; - - bytestowrite = VARSIZE(wbuf) - sizeof(int32); - totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite); - return totalwritten; + int totalwritten; + int bytestowrite; + + bytestowrite = VARSIZE(wbuf) - sizeof(int32); + totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite); + return totalwritten; } /***************************************************************************** - * Import/Export of Large Object + * Import/Export of Large Object *****************************************************************************/ /* * lo_import - - * imports a file as an (inversion) large object. + * imports a file as an (inversion) large object. */ Oid -lo_import(text *filename) +lo_import(text * filename) { - int fd; - int nbytes, tmp; -#define BUFSIZE 1024 - char buf[BUFSIZE]; - char fnamebuf[8192]; - LargeObjectDesc *lobj; - Oid lobjOid; - - /* - * open the file to be read in - */ - strNcpy(fnamebuf, VARDATA(filename), VARSIZE(filename) - VARHDRSZ); - fd = open(fnamebuf, O_RDONLY, 0666); - if (fd < 0) { /* error */ - elog(WARN, "be_lo_import: can't open unix file\"%s\"\n", - fnamebuf); - } - - /* - * create an inversion "object" - */ - lobj = inv_create(INV_READ|INV_WRITE); - if (lobj == NULL) { - elog(WARN, "lo_import: can't create inv object for \"%s\"", - fnamebuf); - } - - /* - * the oid for the large object is just the oid of the relation - * XInv??? which contains the data. - */ - lobjOid = lobj->heap_r->rd_id; - - /* - * read in from the Unix file and write to the inversion file - */ - while ((nbytes = read(fd, buf, BUFSIZE)) > 0) { - tmp = inv_write(lobj, buf, nbytes); - if (tmp < nbytes) { - elog(WARN, "lo_import: error while reading \"%s\"", - fnamebuf); + int fd; + int nbytes, + tmp; + +#define BUFSIZE 1024 + char buf[BUFSIZE]; + char fnamebuf[8192]; + LargeObjectDesc *lobj; + Oid lobjOid; + + /* + * open the file to be read in + */ + strNcpy(fnamebuf, VARDATA(filename), VARSIZE(filename) - VARHDRSZ); + fd = open(fnamebuf, O_RDONLY, 0666); + if (fd < 0) + { /* error */ + elog(WARN, "be_lo_import: can't open unix file\"%s\"\n", + fnamebuf); } - } - close(fd); - inv_close(lobj); + /* + * create an inversion "object" + */ + lobj = inv_create(INV_READ | INV_WRITE); + if (lobj == NULL) + { + elog(WARN, "lo_import: can't create inv object for \"%s\"", + fnamebuf); + } - return lobjOid; + /* + * the oid for the large object is just the oid of the relation + * XInv??? which contains the data. + */ + lobjOid = lobj->heap_r->rd_id; + + /* + * read in from the Unix file and write to the inversion file + */ + while ((nbytes = read(fd, buf, BUFSIZE)) > 0) + { + tmp = inv_write(lobj, buf, nbytes); + if (tmp < nbytes) + { + elog(WARN, "lo_import: error while reading \"%s\"", + fnamebuf); + } + } + + close(fd); + inv_close(lobj); + + return lobjOid; } /* * lo_export - - * exports an (inversion) large object. + * exports an (inversion) large object. */ int4 -lo_export(Oid lobjId, text *filename) +lo_export(Oid lobjId, text * filename) { - int fd; - int nbytes, tmp; -#define BUFSIZE 1024 - char buf[BUFSIZE]; - char fnamebuf[8192]; - LargeObjectDesc *lobj; - mode_t oumask; - - /* - * create an inversion "object" - */ - lobj = inv_open(lobjId, INV_READ); - if (lobj == NULL) { - elog(WARN, "lo_export: can't open inv object %d", - lobjId); - } - - /* - * open the file to be written to - */ - oumask = umask((mode_t) 0); - strNcpy(fnamebuf, VARDATA(filename), VARSIZE(filename) - VARHDRSZ); - fd = open(fnamebuf, O_CREAT|O_WRONLY, 0666); - umask(oumask); - if (fd < 0) { /* error */ - elog(WARN, "lo_export: can't open unix file\"%s\"", - fnamebuf); - } - - /* - * read in from the Unix file and write to the inversion file - */ - while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) { - tmp = write(fd, buf, nbytes); - if (tmp < nbytes) { - elog(WARN, "lo_export: error while writing \"%s\"", - fnamebuf); + int fd; + int nbytes, + tmp; + +#define BUFSIZE 1024 + char buf[BUFSIZE]; + char fnamebuf[8192]; + LargeObjectDesc *lobj; + mode_t oumask; + + /* + * create an inversion "object" + */ + lobj = inv_open(lobjId, INV_READ); + if (lobj == NULL) + { + elog(WARN, "lo_export: can't open inv object %d", + lobjId); + } + + /* + * open the file to be written to + */ + oumask = umask((mode_t) 0); + strNcpy(fnamebuf, VARDATA(filename), VARSIZE(filename) - VARHDRSZ); + fd = open(fnamebuf, O_CREAT | O_WRONLY, 0666); + umask(oumask); + if (fd < 0) + { /* error */ + elog(WARN, "lo_export: can't open unix file\"%s\"", + fnamebuf); } - } - inv_close(lobj); - close(fd); + /* + * read in from the Unix file and write to the inversion file + */ + while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) + { + tmp = write(fd, buf, nbytes); + if (tmp < nbytes) + { + elog(WARN, "lo_export: error while writing \"%s\"", + fnamebuf); + } + } + + inv_close(lobj); + close(fd); - return 1; + return 1; } /***************************************************************************** - * Support routines for this file + * Support routines for this file *****************************************************************************/ static int -newLOfd(LargeObjectDesc *lobjCookie) +newLOfd(LargeObjectDesc * lobjCookie) { - int i; - - for (i = 0; i < MAX_LOBJ_FDS; i++) { - - if (cookies[i] == NULL) { - cookies[i] = lobjCookie; - return i; + int i; + + for (i = 0; i < MAX_LOBJ_FDS; i++) + { + + if (cookies[i] == NULL) + { + cookies[i] = lobjCookie; + return i; + } } - } - return -1; + return -1; } -static void +static void deleteLOfd(int fd) { - cookies[fd] = NULL; + cookies[fd] = NULL; } diff --git a/src/backend/libpq/be-pqexec.c b/src/backend/libpq/be-pqexec.c index caa710129a9..06185e4534f 100644 --- a/src/backend/libpq/be-pqexec.c +++ b/src/backend/libpq/be-pqexec.c @@ -1,24 +1,24 @@ /*------------------------------------------------------------------------- * * be-pqexec.c-- - * support for executing POSTGRES commands and functions from a - * user-defined function in a backend. + * support for executing POSTGRES commands and functions from a + * user-defined function in a backend. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.4 1997/08/19 21:31:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.5 1997/09/07 04:42:17 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * PQfn - call a POSTGRES function - * PQexec - execute a POSTGRES query - * + * PQfn - call a POSTGRES function + * PQexec - execute a POSTGRES query + * * NOTES - * These routines are compiled into the postgres backend. + * These routines are compiled into the postgres backend. */ #include <postgres.h> @@ -32,356 +32,395 @@ #include <utils/exc.h> #include <utils/builtins.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif -static char *strmake(char *str, int len); +static char *strmake(char *str, int len); /* ---------------------------------------------------------------- - * PQ interface routines + * PQ interface routines * ---------------------------------------------------------------- */ /* ---------------- - * PQfn - Send a function call to the POSTGRES backend. + * PQfn - Send a function call to the POSTGRES backend. * - * fnid : function id - * result_buf : pointer to result buffer (&int if integer) - * result_len : length of return value. - * result_is_int : If the result is an integer, this must be non-zero - * args : pointer to a NULL terminated arg array. - * (length, if integer, and result-pointer) - * nargs : # of arguments in args array. + * fnid : function id + * result_buf : pointer to result buffer (&int if integer) + * result_len : length of return value. + * result_is_int : If the result is an integer, this must be non-zero + * args : pointer to a NULL terminated arg array. + * (length, if integer, and result-pointer) + * nargs : # of arguments in args array. * - * This code scavanged from HandleFunctionRequest() in tcop/fastpath.h + * This code scavanged from HandleFunctionRequest() in tcop/fastpath.h * ---------------- */ -char * +char * PQfn(int fnid, - int *result_buf, /* can't use void, dec compiler barfs */ - int result_len, - int result_is_int, - PQArgBlock *args, - int nargs) + int *result_buf, /* can't use void, dec compiler barfs */ + int result_len, + int result_is_int, + PQArgBlock * args, + int nargs) { - char *retval; /* XXX - should be datum, maybe ? */ - char *arg[8]; - int i; - - /* ---------------- - * fill args[] array - * ---------------- - */ - for (i = 0; i < nargs; i++) { - if (args[i].len == VAR_LENGTH_ARG) { - arg[i] = (char*) args[i].u.ptr; - } else if (args[i].len > 4) { - elog(WARN,"arg_length of argument %d too long",i); - } else { - arg[i] = (char*)args[i].u.integer; + char *retval; /* XXX - should be datum, maybe ? */ + char *arg[8]; + int i; + + /* ---------------- + * fill args[] array + * ---------------- + */ + for (i = 0; i < nargs; i++) + { + if (args[i].len == VAR_LENGTH_ARG) + { + arg[i] = (char *) args[i].u.ptr; + } + else if (args[i].len > 4) + { + elog(WARN, "arg_length of argument %d too long", i); + } + else + { + arg[i] = (char *) args[i].u.integer; + } } - } - - /* ---------------- - * call the postgres function manager - * ---------------- - */ - retval = (char *) - fmgr(fnid, arg[0], arg[1], arg[2], arg[3], - arg[4], arg[5], arg[6], arg[7]); - - /* ---------------- - * put the result in the buffer the user specified and - * return the proper code. - * ---------------- - */ - if (retval == (char *) NULL) /* void retval */ - return "0"; - - if (result_is_int) { - *result_buf = (int) retval; - } else { - memmove(result_buf, retval, result_len); - } - return "G"; + + /* ---------------- + * call the postgres function manager + * ---------------- + */ + retval = (char *) + fmgr(fnid, arg[0], arg[1], arg[2], arg[3], + arg[4], arg[5], arg[6], arg[7]); + + /* ---------------- + * put the result in the buffer the user specified and + * return the proper code. + * ---------------- + */ + if (retval == (char *) NULL)/* void retval */ + return "0"; + + if (result_is_int) + { + *result_buf = (int) retval; + } + else + { + memmove(result_buf, retval, result_len); + } + return "G"; } /* ---------------- - * PQexec - Send a query to the POSTGRES backend + * PQexec - Send a query to the POSTGRES backend * - * The return value is a string. - * If 0 or more tuples fetched from the backend, return "P portal-name". - * If a query is does not return tuples, return "C query-command". - * If there is an error: return "E error-message". + * The return value is a string. + * If 0 or more tuples fetched from the backend, return "P portal-name". + * If a query is does not return tuples, return "C query-command". + * If there is an error: return "E error-message". * - * Note: if we get a serious error or an elog(WARN), then PQexec never - * returns because the system longjmp's back to the main loop. + * Note: if we get a serious error or an elog(WARN), then PQexec never + * returns because the system longjmp's back to the main loop. * ---------------- */ -char * +char * PQexec(char *query) { - PortalEntry *entry = NULL; - char *result = NULL; - - /* ---------------- - * create a new portal and put it on top of the portal stack. - * ---------------- - */ - entry = (PortalEntry *) be_newportal(); - be_portalpush(entry); - - /* ---------------- - * pg_eval_dest will put the query results in a portal which will - * end up on the top of the portal stack. - * ---------------- - */ - pg_eval_dest(query, (char **) NULL, (Oid *) NULL, 0, Local); - - /* ---------------- - * pop the portal off the portal stack and return the - * result. Note if result is null, we return C. - * ---------------- - */ - entry = (PortalEntry *) be_portalpop(); - result = entry->result; - if (result == NULL) { - char *PQE = "Cnull PQexec result"; - result = pstrdup(PQE); - } - - if (result[0] != 'P') + PortalEntry *entry = NULL; + char *result = NULL; + + /* ---------------- + * create a new portal and put it on top of the portal stack. + * ---------------- + */ + entry = (PortalEntry *) be_newportal(); + be_portalpush(entry); + + /* ---------------- + * pg_eval_dest will put the query results in a portal which will + * end up on the top of the portal stack. + * ---------------- + */ + pg_eval_dest(query, (char **) NULL, (Oid *) NULL, 0, Local); + + /* ---------------- + * pop the portal off the portal stack and return the + * result. Note if result is null, we return C. + * ---------------- + */ + entry = (PortalEntry *) be_portalpop(); + result = entry->result; + if (result == NULL) { - /* some successful command was executed, - but it's not one where we return the portal name so - here we should be sure to clear out the portal - (since the caller has no handle on it) - */ - pbuf_close(entry->name); - + char *PQE = "Cnull PQexec result"; + + result = pstrdup(PQE); } - return result; + + if (result[0] != 'P') + { + + /* + * some successful command was executed, but it's not one where we + * return the portal name so here we should be sure to clear out + * the portal (since the caller has no handle on it) + */ + pbuf_close(entry->name); + + } + return result; } /* ---------------------------------------------------------------- - * pqtest support + * pqtest support * ---------------------------------------------------------------- */ /* ---------------- - * pqtest_PQexec takes a text query and returns the number of - * tuples it returns. Note: there is no need to PQclear() - * here - the memory will go away at end transaction. + * pqtest_PQexec takes a text query and returns the number of + * tuples it returns. Note: there is no need to PQclear() + * here - the memory will go away at end transaction. * ---------------- */ int pqtest_PQexec(char *q) { - PortalBuffer *a; - char *res; - int t; - - /* ---------------- - * execute the postgres query - * ---------------- - */ - res = PQexec(q); - - /* ---------------- - * return number of tuples in portal or 0 if command returns no tuples. - * ---------------- - */ - t = 0; - switch(res[0]) { - case 'P': - a = PQparray(&res[1]); - if (a == NULL) - elog(WARN, "pqtest_PQexec: PQparray could not find portal %s", - res); - - t = PQntuples(a); - break; - case 'C': - break; - default: - elog(NOTICE, "pqtest_PQexec: PQexec(%s) returns %s", q, res); - break; - } - - return t; + PortalBuffer *a; + char *res; + int t; + + /* ---------------- + * execute the postgres query + * ---------------- + */ + res = PQexec(q); + + /* ---------------- + * return number of tuples in portal or 0 if command returns no tuples. + * ---------------- + */ + t = 0; + switch (res[0]) + { + case 'P': + a = PQparray(&res[1]); + if (a == NULL) + elog(WARN, "pqtest_PQexec: PQparray could not find portal %s", + res); + + t = PQntuples(a); + break; + case 'C': + break; + default: + elog(NOTICE, "pqtest_PQexec: PQexec(%s) returns %s", q, res); + break; + } + + return t; } /* ---------------- - * utilities for pqtest_PQfn() + * utilities for pqtest_PQfn() * ---------------- */ -static char * +static char * strmake(char *str, int len) { - char *newstr; - if (str == NULL) return NULL; - if (len <= 0) len = strlen(str); - - newstr = (char *) palloc((unsigned) len+1); - strNcpy(newstr, str, len); - newstr[len] = (char) 0; - return newstr; + char *newstr; + + if (str == NULL) + return NULL; + if (len <= 0) + len = strlen(str); + + newstr = (char *) palloc((unsigned) len + 1); + strNcpy(newstr, str, len); + newstr[len] = (char) 0; + return newstr; } #define SKIP 0 #define SCAN 1 -static char spacestr[] = " "; +static char spacestr[] = " "; static int strparse(char *s, char **fields, int *offsets, int maxfields) { - int len = strlen(s); - char *cp = s, *end = cp + len, *ep; - int parsed = 0; - int mode = SKIP, i = 0; - - if (*(end - 1) == '\n') end--; - - for (i=0; i<maxfields; i++) - fields[i] = spacestr; - - i = 0; - while (!parsed) { - if (mode == SKIP) { - - while ((cp < end) && - (*cp == ' ' || *cp == '\t')) - cp++; - if (cp < end) mode = SCAN; - else parsed = 1; - - } else { - - ep = cp; - while ((ep < end) && (*ep != ' ' && *ep != '\t')) - ep++; - - if (ep < end) mode = SKIP; - else parsed = 1; - - fields[i] = strmake(cp, ep - cp); - if (offsets != NULL) - offsets[i] = cp - s; - - i++; - cp = ep; - if (i > maxfields) - parsed = 1; - + int len = strlen(s); + char *cp = s, + *end = cp + len, + *ep; + int parsed = 0; + int mode = SKIP, + i = 0; + + if (*(end - 1) == '\n') + end--; + + for (i = 0; i < maxfields; i++) + fields[i] = spacestr; + + i = 0; + while (!parsed) + { + if (mode == SKIP) + { + + while ((cp < end) && + (*cp == ' ' || *cp == '\t')) + cp++; + if (cp < end) + mode = SCAN; + else + parsed = 1; + + } + else + { + + ep = cp; + while ((ep < end) && (*ep != ' ' && *ep != '\t')) + ep++; + + if (ep < end) + mode = SKIP; + else + parsed = 1; + + fields[i] = strmake(cp, ep - cp); + if (offsets != NULL) + offsets[i] = cp - s; + + i++; + cp = ep; + if (i > maxfields) + parsed = 1; + + } } - } - return i; + return i; } /* ---------------- - * pqtest_PQfn converts it's string into a PQArgBlock and - * calls the specified function, which is assumed to return - * an integer value. + * pqtest_PQfn converts it's string into a PQArgBlock and + * calls the specified function, which is assumed to return + * an integer value. * ---------------- */ int pqtest_PQfn(char *q) { - int k, j, i, v, f, offsets; - char *fields[8]; - PQArgBlock pqargs[7]; - int res; - char *pqres; - - /* ---------------- - * parse q into fields - * ---------------- - */ - i = strparse(q, fields, &offsets, 8); - printf("pqtest_PQfn: strparse returns %d fields\n", i); /* debug */ - if (i == 0) - return -1; - - /* ---------------- - * get the function id - * ---------------- - */ - f = atoi(fields[0]); - printf("pqtest_PQfn: func is %d\n", f); /* debug */ - if (f == 0) - return -1; - - /* ---------------- - * build a PQArgBlock - * ---------------- - */ - for (j=1; j<i && j<8; j++) { - k = j-1; - v = atoi(fields[j]); - if (v != 0 || (v == 0 && fields[j][0] == '0')) { - pqargs[k].len = 4; - pqargs[k].u.integer = v; - printf("pqtest_PQfn: arg %d is int %d\n", k, v); /* debug */ - } else { - pqargs[k].len = VAR_LENGTH_ARG; - pqargs[k].u.ptr = (int *) textin(fields[j]); - printf("pqtest_PQfn: arg %d is text %s\n", k, fields[j]); /*debug*/ + int k, + j, + i, + v, + f, + offsets; + char *fields[8]; + PQArgBlock pqargs[7]; + int res; + char *pqres; + + /* ---------------- + * parse q into fields + * ---------------- + */ + i = strparse(q, fields, &offsets, 8); + printf("pqtest_PQfn: strparse returns %d fields\n", i); /* debug */ + if (i == 0) + return -1; + + /* ---------------- + * get the function id + * ---------------- + */ + f = atoi(fields[0]); + printf("pqtest_PQfn: func is %d\n", f); /* debug */ + if (f == 0) + return -1; + + /* ---------------- + * build a PQArgBlock + * ---------------- + */ + for (j = 1; j < i && j < 8; j++) + { + k = j - 1; + v = atoi(fields[j]); + if (v != 0 || (v == 0 && fields[j][0] == '0')) + { + pqargs[k].len = 4; + pqargs[k].u.integer = v; + printf("pqtest_PQfn: arg %d is int %d\n", k, v); /* debug */ + } + else + { + pqargs[k].len = VAR_LENGTH_ARG; + pqargs[k].u.ptr = (int *) textin(fields[j]); + printf("pqtest_PQfn: arg %d is text %s\n", k, fields[j]); /* debug */ + } + } + + /* ---------------- + * call PQfn + * ---------------- + */ + pqres = PQfn(f, &res, 4, 1, pqargs, i - 1); + printf("pqtest_PQfn: pqres is %s\n", pqres); /* debug */ + + /* ---------------- + * free memory used + * ---------------- + */ + for (j = 0; j < i; j++) + { + pfree(fields[j]); + if (pqargs[j].len == VAR_LENGTH_ARG) + pfree(pqargs[j].u.ptr); } - } - - /* ---------------- - * call PQfn - * ---------------- - */ - pqres = PQfn(f, &res, 4, 1, pqargs, i-1); - printf("pqtest_PQfn: pqres is %s\n", pqres); /* debug */ - - /* ---------------- - * free memory used - * ---------------- - */ - for (j=0; j<i; j++) { - pfree(fields[j]); - if (pqargs[j].len == VAR_LENGTH_ARG) - pfree(pqargs[j].u.ptr); - } - - /* ---------------- - * return result - * ---------------- - */ - printf("pqtest_PQfn: res is %d\n", res); /* debugg */ - return res; + + /* ---------------- + * return result + * ---------------- + */ + printf("pqtest_PQfn: res is %d\n", res); /* debugg */ + return res; } /* ---------------- - * pqtest looks at the first character of it's test argument - * and decides which of pqtest_PQexec or pqtest_PQfn to call. + * pqtest looks at the first character of it's test argument + * and decides which of pqtest_PQexec or pqtest_PQfn to call. * ---------------- */ int32 -pqtest(struct varlena *vlena) +pqtest(struct varlena * vlena) { - char *q; - - /* ---------------- - * get the query - * ---------------- - */ - q = textout(vlena); - if (q == NULL) - return -1; - - switch(q[0]) { - case '%': - return pqtest_PQfn(&q[1]); - break; - default: - return pqtest_PQexec(q); - break; - } - return(0); + char *q; + + /* ---------------- + * get the query + * ---------------- + */ + q = textout(vlena); + if (q == NULL) + return -1; + + switch (q[0]) + { + case '%': + return pqtest_PQfn(&q[1]); + break; + default: + return pqtest_PQexec(q); + break; + } + return (0); } diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 0cc2bbf6ac9..014eca14fa3 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * hba.c-- - * Routines to handle host based authentication (that's the scheme - * wherein you authenticate a user by seeing what IP address the system - * says he comes from and possibly using ident). + * Routines to handle host based authentication (that's the scheme + * wherein you authenticate a user by seeing what IP address the system + * says he comes from and possibly using ident). * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.19 1997/08/27 03:48:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.20 1997/09/07 04:42:21 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -27,263 +27,356 @@ #include <libpq/libpq.h> #include <libpq/pqcomm.h> #include <libpq/hba.h> -#include <port/inet_aton.h> /* For inet_aton() */ +#include <port/inet_aton.h> /* For inet_aton() */ #include <storage/fd.h> /* Some standard C libraries, including GNU, have an isblank() function. Others, including Solaris, do not. So we have our own. */ -static bool -isblank(const char c) { - return(c == ' ' || c == 9 /* tab */); +static bool +isblank(const char c) +{ + return (c == ' ' || c == 9 /* tab */ ); } -static void -next_token(FILE *fp, char *buf, const int bufsz) { +static void +next_token(FILE * fp, char *buf, const int bufsz) +{ /*-------------------------------------------------------------------------- Grab one token out of fp. Tokens are strings of non-blank characters bounded by blank characters, beginning of line, and end - of line. Blank means space or tab. Return the token as *buf. + of line. Blank means space or tab. Return the token as *buf. Leave file positioned to character immediately after the token or EOF, whichever comes first. If no more tokens on line, return null string as *buf and position file to beginning of next line or EOF, - whichever comes first. + whichever comes first. --------------------------------------------------------------------------*/ - int c; - char *eb = buf+(bufsz-1); - - /* Move over inital token-delimiting blanks */ - while (isblank(c = getc(fp))) ; - - if (c != '\n') { - /* build a token in buf of next characters up to EOF, eol, or blank. */ - while (c != EOF && c != '\n' && !isblank(c)) { - if (buf < eb) *buf++ = c; - c = getc(fp); - /* Put back the char right after the token (putting back EOF is ok) */ - } - ungetc(c, fp); - } - *buf = '\0'; + int c; + char *eb = buf + (bufsz - 1); + + /* Move over inital token-delimiting blanks */ + while (isblank(c = getc(fp))); + + if (c != '\n') + { + + /* + * build a token in buf of next characters up to EOF, eol, or + * blank. + */ + while (c != EOF && c != '\n' && !isblank(c)) + { + if (buf < eb) + *buf++ = c; + c = getc(fp); + + /* + * Put back the char right after the token (putting back EOF + * is ok) + */ + } + ungetc(c, fp); + } + *buf = '\0'; } static void -read_through_eol(FILE *file) { - int c; - do - c = getc(file); - while (c != '\n' && c != EOF); +read_through_eol(FILE * file) +{ + int c; + + do + c = getc(file); + while (c != '\n' && c != EOF); } static void -read_hba_entry2(FILE *file, enum Userauth *userauth_p, char usermap_name[], - bool *error_p, bool *matches_p, bool find_password_entries) { +read_hba_entry2(FILE * file, enum Userauth * userauth_p, char usermap_name[], + bool * error_p, bool * matches_p, bool find_password_entries) +{ /*-------------------------------------------------------------------------- Read from file FILE the rest of a host record, after the mask field, and return the interpretation of it as *userauth_p, usermap_name, and *error_p. ---------------------------------------------------------------------------*/ - char buf[MAX_TOKEN]; - - bool userauth_valid; - - /* Get authentication type token. */ - next_token(file, buf, sizeof(buf)); - userauth_valid = false; - if (buf[0] == '\0') { - *error_p = true; - } else { - userauth_valid = true; - if(strcmp(buf, "trust") == 0) { - *userauth_p = Trust; - } else if(strcmp(buf, "ident") == 0) { - *userauth_p = Ident; - } else if(strcmp(buf, "password") == 0) { - *userauth_p = Password; - } else { - userauth_valid = false; - } - - if((find_password_entries && strcmp(buf, "password") == 0) || - (!find_password_entries && strcmp(buf, "password") != 0)) { - *matches_p = true; - } else { - *matches_p = false; - } - } - - if(!userauth_valid || !*matches_p || *error_p) { - if (!userauth_valid) { - *error_p = true; - } - read_through_eol(file); - } else { - /* Get the map name token, if any */ - next_token(file, buf, sizeof(buf)); - if (buf[0] == '\0') { - *error_p = false; - usermap_name[0] = '\0'; - } else { - strncpy(usermap_name, buf, USERMAP_NAME_SIZE); - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') { - *error_p = true; - read_through_eol(file); - } else *error_p = false; - } - } + char buf[MAX_TOKEN]; + + bool userauth_valid; + + /* Get authentication type token. */ + next_token(file, buf, sizeof(buf)); + userauth_valid = false; + if (buf[0] == '\0') + { + *error_p = true; + } + else + { + userauth_valid = true; + if (strcmp(buf, "trust") == 0) + { + *userauth_p = Trust; + } + else if (strcmp(buf, "ident") == 0) + { + *userauth_p = Ident; + } + else if (strcmp(buf, "password") == 0) + { + *userauth_p = Password; + } + else + { + userauth_valid = false; + } + + if ((find_password_entries && strcmp(buf, "password") == 0) || + (!find_password_entries && strcmp(buf, "password") != 0)) + { + *matches_p = true; + } + else + { + *matches_p = false; + } + } + + if (!userauth_valid || !*matches_p || *error_p) + { + if (!userauth_valid) + { + *error_p = true; + } + read_through_eol(file); + } + else + { + /* Get the map name token, if any */ + next_token(file, buf, sizeof(buf)); + if (buf[0] == '\0') + { + *error_p = false; + usermap_name[0] = '\0'; + } + else + { + strncpy(usermap_name, buf, USERMAP_NAME_SIZE); + next_token(file, buf, sizeof(buf)); + if (buf[0] != '\0') + { + *error_p = true; + read_through_eol(file); + } + else + *error_p = false; + } + } } static void -process_hba_record(FILE *file, - const struct in_addr ip_addr, const char database[], - bool *matches_p, bool *error_p, - enum Userauth *userauth_p, char usermap_name[], - bool find_password_entries) { +process_hba_record(FILE * file, + const struct in_addr ip_addr, const char database[], + bool * matches_p, bool * error_p, + enum Userauth * userauth_p, char usermap_name[], + bool find_password_entries) +{ /*--------------------------------------------------------------------------- Process the non-comment record in the config file that is next on the file. See if it applies to a connection to a host with IP address "ip_addr" to a database named "database[]". If so, return *matches_p true and *userauth_p and usermap_name[] as the values from the entry. If not, return matches_p false. If the record has a syntax error, - return *error_p true, after issuing a message to stderr. If no error, + return *error_p true, after issuing a message to stderr. If no error, leave *error_p as it was. ---------------------------------------------------------------------------*/ - char buf[MAX_TOKEN]; /* A token from the record */ - - /* Read the record type field */ - next_token(file, buf, sizeof(buf)); - if (buf[0] == '\0') *matches_p = false; - else { - /* if this isn't a "host" record, it can't match. */ - if (strcmp(buf, "host") != 0) { - *matches_p = false; - read_through_eol(file); - } else { - /* It's a "host" record. Read the database name field. */ - next_token(file, buf, sizeof(buf)); - if (buf[0] == '\0') *matches_p = false; - else { - /* If this record isn't for our database, ignore it. */ - if (strcmp(buf, database) != 0 && strcmp(buf, "all") != 0) { - *matches_p = false; - read_through_eol(file); - } else { - /* Read the IP address field */ - next_token(file, buf, sizeof(buf)); - if (buf[0] == '\0') *matches_p = false; - else { - int valid; /* Field is valid dotted decimal */ - /* Remember the IP address field and go get mask field */ - struct in_addr file_ip_addr; /* IP address field value */ - - valid = inet_aton(buf, &file_ip_addr); - if (!valid) { - *matches_p = false; - read_through_eol(file); - } else { - /* Read the mask field */ - next_token(file, buf, sizeof(buf)); - if (buf[0] == '\0') *matches_p = false; - else { - struct in_addr mask; - /* Got mask. Now see if this record is for our host. */ - valid = inet_aton(buf, &mask); - if (!valid) { - *matches_p = false; - read_through_eol(file); - } else { - if (((file_ip_addr.s_addr ^ ip_addr.s_addr) & mask.s_addr) - != 0x0000) { - *matches_p = false; - read_through_eol(file); - } else { - /* This is the record we're looking for. Read - the rest of the info from it. - */ - read_hba_entry2(file, userauth_p, usermap_name, - error_p, matches_p, find_password_entries); - if (*error_p) { - sprintf(PQerrormsg, - "process_hba_record: invalid syntax in " - "hba config file " - "for host record for IP address %s\n", - inet_ntoa(file_ip_addr)); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } - } - } - } - } - } - } - } - } - } + char buf[MAX_TOKEN]; /* A token from the record */ + + /* Read the record type field */ + next_token(file, buf, sizeof(buf)); + if (buf[0] == '\0') + *matches_p = false; + else + { + /* if this isn't a "host" record, it can't match. */ + if (strcmp(buf, "host") != 0) + { + *matches_p = false; + read_through_eol(file); + } + else + { + /* It's a "host" record. Read the database name field. */ + next_token(file, buf, sizeof(buf)); + if (buf[0] == '\0') + *matches_p = false; + else + { + /* If this record isn't for our database, ignore it. */ + if (strcmp(buf, database) != 0 && strcmp(buf, "all") != 0) + { + *matches_p = false; + read_through_eol(file); + } + else + { + /* Read the IP address field */ + next_token(file, buf, sizeof(buf)); + if (buf[0] == '\0') + *matches_p = false; + else + { + int valid; /* Field is valid dotted + * decimal */ + + /* + * Remember the IP address field and go get mask + * field + */ + struct in_addr file_ip_addr; /* IP address field + * value */ + + valid = inet_aton(buf, &file_ip_addr); + if (!valid) + { + *matches_p = false; + read_through_eol(file); + } + else + { + /* Read the mask field */ + next_token(file, buf, sizeof(buf)); + if (buf[0] == '\0') + *matches_p = false; + else + { + struct in_addr mask; + + /* + * Got mask. Now see if this record is + * for our host. + */ + valid = inet_aton(buf, &mask); + if (!valid) + { + *matches_p = false; + read_through_eol(file); + } + else + { + if (((file_ip_addr.s_addr ^ ip_addr.s_addr) & mask.s_addr) + != 0x0000) + { + *matches_p = false; + read_through_eol(file); + } + else + { + + /* + * This is the record we're + * looking for. Read the rest of + * the info from it. + */ + read_hba_entry2(file, userauth_p, usermap_name, + error_p, matches_p, find_password_entries); + if (*error_p) + { + sprintf(PQerrormsg, + "process_hba_record: invalid syntax in " + "hba config file " + "for host record for IP address %s\n", + inet_ntoa(file_ip_addr)); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + } + } + } + } + } + } + } + } + } } static void -process_open_config_file(FILE *file, - const struct in_addr ip_addr, const char database[], - bool *host_ok_p, enum Userauth *userauth_p, - char usermap_name[], bool find_password_entries) { +process_open_config_file(FILE * file, + const struct in_addr ip_addr, const char database[], + bool * host_ok_p, enum Userauth * userauth_p, + char usermap_name[], bool find_password_entries) +{ /*--------------------------------------------------------------------------- This function does the same thing as find_hba_entry, only with the config file already open on stream descriptor "file". ----------------------------------------------------------------------------*/ - bool found_entry; - /* We've processed a record that applies to our connection */ - bool error; - /* Said record has invalid syntax. */ - bool eof; /* We've reached the end of the file we're reading */ - - found_entry = false; /* initial value */ - error = false; /* initial value */ - eof = false; /* initial value */ - while (!eof && !found_entry && !error) { - /* Process a line from the config file */ - - int c; /* a character read from the file */ - - c = getc(file); ungetc(c, file); - if (c == EOF) eof = true; - else { - if (c == '#') read_through_eol(file); - else { - process_hba_record(file, ip_addr, database, - &found_entry, &error, userauth_p, usermap_name, - find_password_entries); - } - } - } - if (found_entry) { - if (error) *host_ok_p = false; - else *host_ok_p = true; - } else *host_ok_p = false; -} + bool found_entry; + + /* We've processed a record that applies to our connection */ + bool error; + + /* Said record has invalid syntax. */ + bool eof; /* We've reached the end of the file we're + * reading */ + + found_entry = false; /* initial value */ + error = false; /* initial value */ + eof = false; /* initial value */ + while (!eof && !found_entry && !error) + { + /* Process a line from the config file */ + + int c; /* a character read from the file */ + + c = getc(file); + ungetc(c, file); + if (c == EOF) + eof = true; + else + { + if (c == '#') + read_through_eol(file); + else + { + process_hba_record(file, ip_addr, database, + &found_entry, &error, userauth_p, usermap_name, + find_password_entries); + } + } + } + if (found_entry) + { + if (error) + *host_ok_p = false; + else + *host_ok_p = true; + } + else + *host_ok_p = false; +} void -find_hba_entry(const char DataDir[], const struct in_addr ip_addr, - const char database[], - bool *host_ok_p, enum Userauth *userauth_p, - char usermap_name[], bool find_password_entries) { +find_hba_entry(const char DataDir[], const struct in_addr ip_addr, + const char database[], + bool * host_ok_p, enum Userauth * userauth_p, + char usermap_name[], bool find_password_entries) +{ /*-------------------------------------------------------------------------- Read the config file and find an entry that allows connection from - host "ip_addr" to database "database". If not found, return - *host_ok_p == false. If found, return *userauth_p and *usermap_name + host "ip_addr" to database "database". If not found, return + *host_ok_p == false. If found, return *userauth_p and *usermap_name representing the contents of that entry. When a record has invalid syntax, we either ignore it or reject the @@ -298,133 +391,167 @@ find_hba_entry(const char DataDir[], const struct in_addr ip_addr, follow directions and just installed his old hba file in the new database system. ----------------------------------------------------------------------------*/ - int fd; - - FILE *file; /* The config file we have to read */ - - char *old_conf_file; - /* The name of old config file that better not exist. */ - - /* Fail if config file by old name exists. */ - - - /* put together the full pathname to the old config file */ - old_conf_file = (char *) malloc((strlen(DataDir) + - strlen(OLD_CONF_FILE)+2)*sizeof(char)); - sprintf(old_conf_file, "%s/%s", DataDir, OLD_CONF_FILE); - - if ((fd = open(old_conf_file,O_RDONLY,0)) != -1) { - /* Old config file exists. Tell this guy he needs to upgrade. */ - close(fd); - sprintf(PQerrormsg, - "A file exists by the name used for host-based authentication " - "in prior releases of Postgres (%s). The name and format of " - "the configuration file have changed, so this file should be " - "converted.\n", - old_conf_file); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } else { - char *conf_file; /* The name of the config file we have to read */ - - /* put together the full pathname to the config file */ - conf_file = (char *) malloc((strlen(DataDir) + - strlen(CONF_FILE)+2)*sizeof(char)); - sprintf(conf_file, "%s/%s", DataDir, CONF_FILE); - - file = AllocateFile(conf_file, "r"); - if (file == NULL) { - /* The open of the config file failed. */ - - *host_ok_p = false; - - sprintf(PQerrormsg, - "find_hba_entry: Host-based authentication config file " - "does not exist or permissions are not setup correctly! " - "Unable to open file \"%s\".\n", - conf_file); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } else { - process_open_config_file(file, ip_addr, database, host_ok_p, userauth_p, - usermap_name, find_password_entries); - FreeFile(file); - } - free(conf_file); - } - free(old_conf_file); - return; +---------------------------------------------------------------------------*/ + int fd; + + FILE *file; /* The config file we have to read */ + + char *old_conf_file; + + /* The name of old config file that better not exist. */ + + /* Fail if config file by old name exists. */ + + + /* put together the full pathname to the old config file */ + old_conf_file = (char *) malloc((strlen(DataDir) + + strlen(OLD_CONF_FILE) + 2) * sizeof(char)); + sprintf(old_conf_file, "%s/%s", DataDir, OLD_CONF_FILE); + + if ((fd = open(old_conf_file, O_RDONLY, 0)) != -1) + { + /* Old config file exists. Tell this guy he needs to upgrade. */ + close(fd); + sprintf(PQerrormsg, + "A file exists by the name used for host-based authentication " + "in prior releases of Postgres (%s). The name and format of " + "the configuration file have changed, so this file should be " + "converted.\n", + old_conf_file); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + else + { + char *conf_file; /* The name of the config file we + * have to read */ + + /* put together the full pathname to the config file */ + conf_file = (char *) malloc((strlen(DataDir) + + strlen(CONF_FILE) + 2) * sizeof(char)); + sprintf(conf_file, "%s/%s", DataDir, CONF_FILE); + + file = AllocateFile(conf_file, "r"); + if (file == NULL) + { + /* The open of the config file failed. */ + + *host_ok_p = false; + + sprintf(PQerrormsg, + "find_hba_entry: Host-based authentication config file " + "does not exist or permissions are not setup correctly! " + "Unable to open file \"%s\".\n", + conf_file); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + else + { + process_open_config_file(file, ip_addr, database, host_ok_p, userauth_p, + usermap_name, find_password_entries); + FreeFile(file); + } + free(conf_file); + } + free(old_conf_file); + return; } static void -interpret_ident_response(char ident_response[], - bool *error_p, char ident_username[]) { +interpret_ident_response(char ident_response[], + bool * error_p, char ident_username[]) +{ /*---------------------------------------------------------------------------- Parse the string "ident_response[]" as a response from a query to an Ident - server. If it's a normal response indicating a username, return + server. If it's a normal response indicating a username, return *error_p == false and the username as ident_username[]. If it's anything else, return *error_p == true and ident_username[] undefined. ----------------------------------------------------------------------------*/ - char *cursor; /* Cursor into ident_response[] */ - - cursor = &ident_response[0]; - - /* Ident's response, in the telnet tradition, should end in crlf (\r\n). */ - if (strlen(ident_response) < 2) *error_p = true; - else if (ident_response[strlen(ident_response)-2] != '\r') *error_p = true; - else { - while (*cursor != ':' && *cursor != '\r') cursor++; /* skip port field */ - - if (*cursor != ':') *error_p = true; - else { - /* We're positioned to colon before response type field */ - char response_type[80]; - int i; /* Index into response_type[] */ - cursor++; /* Go over colon */ - while (isblank(*cursor)) cursor++; /* skip blanks */ - i = 0; - while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) - && i < sizeof(response_type)-1) - response_type[i++] = *cursor++; - response_type[i] = '\0'; - while (isblank(*cursor)) cursor++; /* skip blanks */ - if (strcmp(response_type, "USERID") != 0) - *error_p = true; - else { - /* It's a USERID response. Good. "cursor" should be pointing to - the colon that precedes the operating system type. - */ - if (*cursor != ':') *error_p = true; - else { - cursor++; /* Go over colon */ - /* Skip over operating system field. */ - while (*cursor != ':' && *cursor != '\r') cursor++; - if (*cursor != ':') *error_p = true; - else { - int i; /* Index into ident_username[] */ - cursor ++; /* Go over colon */ - while (isblank(*cursor)) cursor++; /* skip blanks */ - /* Rest of line is username. Copy it over. */ - i = 0; - while (*cursor != '\r' && i < IDENT_USERNAME_MAX) - ident_username[i++] = *cursor++; - ident_username[i] = '\0'; - *error_p = false; - } - } - } - } - } + char *cursor; /* Cursor into ident_response[] */ + + cursor = &ident_response[0]; + + /* + * Ident's response, in the telnet tradition, should end in crlf + * (\r\n). + */ + if (strlen(ident_response) < 2) + *error_p = true; + else if (ident_response[strlen(ident_response) - 2] != '\r') + *error_p = true; + else + { + while (*cursor != ':' && *cursor != '\r') + cursor++; /* skip port field */ + + if (*cursor != ':') + *error_p = true; + else + { + /* We're positioned to colon before response type field */ + char response_type[80]; + int i; /* Index into response_type[] */ + + cursor++; /* Go over colon */ + while (isblank(*cursor)) + cursor++; /* skip blanks */ + i = 0; + while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) + && i < sizeof(response_type) - 1) + response_type[i++] = *cursor++; + response_type[i] = '\0'; + while (isblank(*cursor)) + cursor++; /* skip blanks */ + if (strcmp(response_type, "USERID") != 0) + *error_p = true; + else + { + + /* + * It's a USERID response. Good. "cursor" should be + * pointing to the colon that precedes the operating + * system type. + */ + if (*cursor != ':') + *error_p = true; + else + { + cursor++; /* Go over colon */ + /* Skip over operating system field. */ + while (*cursor != ':' && *cursor != '\r') + cursor++; + if (*cursor != ':') + *error_p = true; + else + { + int i; /* Index into + * ident_username[] */ + + cursor++; /* Go over colon */ + while (isblank(*cursor)) + cursor++; /* skip blanks */ + /* Rest of line is username. Copy it over. */ + i = 0; + while (*cursor != '\r' && i < IDENT_USERNAME_MAX) + ident_username[i++] = *cursor++; + ident_username[i] = '\0'; + *error_p = false; + } + } + } + } + } } static void ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, - const ushort remote_port, const ushort local_port, - bool *ident_failed, char ident_username[]) { + const ushort remote_port, const ushort local_port, + bool * ident_failed, char ident_username[]) +{ /*-------------------------------------------------------------------------- Talk to the ident server on host "remote_ip_addr" and find out who owns the tcp connection from his port "remote_port" to port @@ -437,169 +564,204 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, *ident_failed == true (and ident_username[] undefined). ----------------------------------------------------------------------------*/ - int sock_fd; - /* File descriptor for socket on which we talk to Ident */ - - int rc; /* Return code from a locally called function */ - - sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - if (sock_fd == -1) { - sprintf(PQerrormsg, - "Failed to create socket on which to talk to Ident server. " - "socket() returned errno = %s (%d)\n", - strerror(errno), errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } else { - struct sockaddr_in ident_server; - /* Socket address of Ident server on the system from which client - is attempting to connect to us. - */ - ident_server.sin_family = AF_INET; - ident_server.sin_port = htons(IDENT_PORT); - ident_server.sin_addr = remote_ip_addr; - rc = connect(sock_fd, - (struct sockaddr *) &ident_server, sizeof(ident_server)); - if (rc != 0) { - sprintf(PQerrormsg, - "Unable to connect to Ident server on the host which is " - "trying to connect to Postgres " - "(IP address %s, Port %d). " - "errno = %s (%d)\n", - inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - *ident_failed = true; - } else { - char ident_query[80]; - /* The query we send to the Ident server */ - sprintf(ident_query, "%d,%d\n", - ntohs(remote_port), ntohs(local_port)); - rc = send(sock_fd, ident_query, strlen(ident_query), 0); - if (rc < 0) { - sprintf(PQerrormsg, - "Unable to send query to Ident server on the host which is " - "trying to connect to Postgres (Host %s, Port %d)," - "even though we successfully connected to it. " - "errno = %s (%d)\n", - inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - *ident_failed = true; - } else { - char ident_response[80+IDENT_USERNAME_MAX]; - rc = recv(sock_fd, ident_response, sizeof(ident_response)-1, 0); - if (rc < 0) { - sprintf(PQerrormsg, - "Unable to receive response from Ident server " - "on the host which is " - "trying to connect to Postgres (Host %s, Port %d)," - "even though we successfully sent our query to it. " - "errno = %s (%d)\n", - inet_ntoa(remote_ip_addr), IDENT_PORT, - strerror(errno), errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - *ident_failed = true; - } else { - bool error; /* response from Ident is garbage. */ - ident_response[rc] = '\0'; - interpret_ident_response(ident_response, &error, ident_username); - *ident_failed = error; - } - } - close(sock_fd); - } - } + int sock_fd; + + /* File descriptor for socket on which we talk to Ident */ + + int rc; /* Return code from a locally called + * function */ + + sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (sock_fd == -1) + { + sprintf(PQerrormsg, + "Failed to create socket on which to talk to Ident server. " + "socket() returned errno = %s (%d)\n", + strerror(errno), errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + else + { + struct sockaddr_in ident_server; + + /* + * Socket address of Ident server on the system from which client + * is attempting to connect to us. + */ + ident_server.sin_family = AF_INET; + ident_server.sin_port = htons(IDENT_PORT); + ident_server.sin_addr = remote_ip_addr; + rc = connect(sock_fd, + (struct sockaddr *) & ident_server, sizeof(ident_server)); + if (rc != 0) + { + sprintf(PQerrormsg, + "Unable to connect to Ident server on the host which is " + "trying to connect to Postgres " + "(IP address %s, Port %d). " + "errno = %s (%d)\n", + inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + *ident_failed = true; + } + else + { + char ident_query[80]; + + /* The query we send to the Ident server */ + sprintf(ident_query, "%d,%d\n", + ntohs(remote_port), ntohs(local_port)); + rc = send(sock_fd, ident_query, strlen(ident_query), 0); + if (rc < 0) + { + sprintf(PQerrormsg, + "Unable to send query to Ident server on the host which is " + "trying to connect to Postgres (Host %s, Port %d)," + "even though we successfully connected to it. " + "errno = %s (%d)\n", + inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + *ident_failed = true; + } + else + { + char ident_response[80 + IDENT_USERNAME_MAX]; + + rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0); + if (rc < 0) + { + sprintf(PQerrormsg, + "Unable to receive response from Ident server " + "on the host which is " + "trying to connect to Postgres (Host %s, Port %d)," + "even though we successfully sent our query to it. " + "errno = %s (%d)\n", + inet_ntoa(remote_ip_addr), IDENT_PORT, + strerror(errno), errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + *ident_failed = true; + } + else + { + bool error; /* response from Ident is + * garbage. */ + + ident_response[rc] = '\0'; + interpret_ident_response(ident_response, &error, ident_username); + *ident_failed = error; + } + } + close(sock_fd); + } + } } static void -parse_map_record(FILE *file, - char file_map[], char file_pguser[], char file_iuser[]) { +parse_map_record(FILE * file, + char file_map[], char file_pguser[], char file_iuser[]) +{ /*--------------------------------------------------------------------------- Take the noncomment line which is next on file "file" and interpret it as a line in a usermap file. Specifically, return the first - 3 tokens as file_map, file_iuser, and file_pguser, respectively. If + 3 tokens as file_map, file_iuser, and file_pguser, respectively. If there are fewer than 3 tokens, return null strings for the missing ones. ---------------------------------------------------------------------------*/ - char buf[MAX_TOKEN]; - /* A token read from the file */ - - /* Set defaults in case fields not in file */ - file_map[0] = '\0'; - file_pguser[0] = '\0'; - file_iuser[0] = '\0'; - - next_token(file, buf, sizeof(buf)); - if (buf != '\0') { - strcpy(file_map, buf); - next_token(file, buf, sizeof(buf)); - if (buf != '\0') { - strcpy(file_iuser, buf); - next_token(file, buf, sizeof(buf)); - if (buf != '\0') { - strcpy(file_pguser, buf); - read_through_eol(file); - } - } - } + char buf[MAX_TOKEN]; + + /* A token read from the file */ + + /* Set defaults in case fields not in file */ + file_map[0] = '\0'; + file_pguser[0] = '\0'; + file_iuser[0] = '\0'; + + next_token(file, buf, sizeof(buf)); + if (buf != '\0') + { + strcpy(file_map, buf); + next_token(file, buf, sizeof(buf)); + if (buf != '\0') + { + strcpy(file_iuser, buf); + next_token(file, buf, sizeof(buf)); + if (buf != '\0') + { + strcpy(file_pguser, buf); + read_through_eol(file); + } + } + } } static void -verify_against_open_usermap(FILE *file, - const char pguser[], - const char ident_username[], - const char usermap_name[], - bool *checks_out_p) { +verify_against_open_usermap(FILE * file, + const char pguser[], + const char ident_username[], + const char usermap_name[], + bool * checks_out_p) +{ /*-------------------------------------------------------------------------- This function does the same thing as verify_against_usermap, only with the config file already open on stream descriptor "file". ---------------------------------------------------------------------------*/ - bool match; /* We found a matching entry in the map file */ - bool eof; /* We've reached the end of the file we're reading */ - - match = false; /* initial value */ - eof = false; /* initial value */ - while (!eof && !match) { - /* Process a line from the map file */ - - int c; /* a character read from the file */ - - c = getc(file); ungetc(c, file); - if (c == EOF) eof = true; - else { - if (c == '#') read_through_eol(file); - else { - /* The following are fields read from a record of the file */ - char file_map[MAX_TOKEN+1]; - char file_pguser[MAX_TOKEN+1]; - char file_iuser[MAX_TOKEN+1]; - - parse_map_record(file, file_map, file_pguser, file_iuser); - if (strcmp(file_map, usermap_name) == 0 && - strcmp(file_pguser, pguser) == 0 && - strcmp(file_iuser, ident_username) == 0) - match = true; - } - } - } - *checks_out_p = match; + bool match; /* We found a matching entry in the map + * file */ + bool eof; /* We've reached the end of the file we're + * reading */ + + match = false; /* initial value */ + eof = false; /* initial value */ + while (!eof && !match) + { + /* Process a line from the map file */ + + int c; /* a character read from the file */ + + c = getc(file); + ungetc(c, file); + if (c == EOF) + eof = true; + else + { + if (c == '#') + read_through_eol(file); + else + { + /* The following are fields read from a record of the file */ + char file_map[MAX_TOKEN + 1]; + char file_pguser[MAX_TOKEN + 1]; + char file_iuser[MAX_TOKEN + 1]; + + parse_map_record(file, file_map, file_pguser, file_iuser); + if (strcmp(file_map, usermap_name) == 0 && + strcmp(file_pguser, pguser) == 0 && + strcmp(file_iuser, ident_username) == 0) + match = true; + } + } + } + *checks_out_p = match; } static void verify_against_usermap(const char DataDir[], - const char pguser[], - const char ident_username[], - const char usermap_name[], - bool *checks_out_p) { + const char pguser[], + const char ident_username[], + const char usermap_name[], + bool * checks_out_p) +{ /*-------------------------------------------------------------------------- See if the user with ident username "ident_username" is allowed to act as Postgres user "pguser" according to usermap "usermap_name". Look @@ -610,151 +772,194 @@ verify_against_usermap(const char DataDir[], "ident_username" in order to be authorized. Iff authorized, return *checks_out_p == true. - + --------------------------------------------------------------------------*/ - if (usermap_name[0] == '\0') { - *checks_out_p = false; - sprintf(PQerrormsg, - "verify_against_usermap: hba configuration file does not " - "have the usermap field filled in in the entry that pertains " - "to this connection. That field is essential for Ident-based " - "authentication.\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } else if (strcmp(usermap_name, "sameuser") == 0) { - if (strcmp(ident_username, pguser) == 0) *checks_out_p = true; - else *checks_out_p = false; - } else { - FILE *file; /* The map file we have to read */ - - char *map_file; /* The name of the map file we have to read */ - - /* put together the full pathname to the map file */ - map_file = (char *) malloc((strlen(DataDir) + - strlen(MAP_FILE)+2)*sizeof(char)); - sprintf(map_file, "%s/%s", DataDir, MAP_FILE); - - file = AllocateFile(map_file, "r"); - if (file == NULL) { - /* The open of the map file failed. */ - - *checks_out_p = false; - - sprintf(PQerrormsg, - "verify_against_usermap: usermap file for Ident-based " - "authentication " - "does not exist or permissions are not setup correctly! " - "Unable to open file \"%s\".\n", - map_file); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } else { - verify_against_open_usermap(file, - pguser, ident_username, usermap_name, - checks_out_p); - FreeFile(file); - } - free(map_file); - - - } + if (usermap_name[0] == '\0') + { + *checks_out_p = false; + sprintf(PQerrormsg, + "verify_against_usermap: hba configuration file does not " + "have the usermap field filled in in the entry that pertains " + "to this connection. That field is essential for Ident-based " + "authentication.\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + else if (strcmp(usermap_name, "sameuser") == 0) + { + if (strcmp(ident_username, pguser) == 0) + *checks_out_p = true; + else + *checks_out_p = false; + } + else + { + FILE *file; /* The map file we have to read */ + + char *map_file; /* The name of the map file we + * have to read */ + + /* put together the full pathname to the map file */ + map_file = (char *) malloc((strlen(DataDir) + + strlen(MAP_FILE) + 2) * sizeof(char)); + sprintf(map_file, "%s/%s", DataDir, MAP_FILE); + + file = AllocateFile(map_file, "r"); + if (file == NULL) + { + /* The open of the map file failed. */ + + *checks_out_p = false; + + sprintf(PQerrormsg, + "verify_against_usermap: usermap file for Ident-based " + "authentication " + "does not exist or permissions are not setup correctly! " + "Unable to open file \"%s\".\n", + map_file); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + else + { + verify_against_open_usermap(file, + pguser, ident_username, usermap_name, + checks_out_p); + FreeFile(file); + } + free(map_file); + + + } } static void -authident(const char DataDir[], - const Port port, const char postgres_username[], - const char usermap_name[], - bool *authentic_p) { +authident(const char DataDir[], + const Port port, const char postgres_username[], + const char usermap_name[], + bool * authentic_p) +{ /*--------------------------------------------------------------------------- - Talk to the ident server on the remote host and find out who owns the + Talk to the ident server on the remote host and find out who owns the connection described by "port". Then look in the usermap file under - the usermap usermap_name[] and see if that user is equivalent to + the usermap usermap_name[] and see if that user is equivalent to Postgres user user[]. Return *authentic_p true iff yes. ---------------------------------------------------------------------------*/ - bool ident_failed; - /* We were unable to get ident to give us a username */ - char ident_username[IDENT_USERNAME_MAX+1]; - /* The username returned by ident */ - - ident(port.raddr.sin_addr, port.laddr.sin_addr, - port.raddr.sin_port, port.laddr.sin_port, - &ident_failed, ident_username); - - if (ident_failed) *authentic_p = false; - else { - bool checks_out; - verify_against_usermap(DataDir, - postgres_username, ident_username, usermap_name, - &checks_out); - if (checks_out) *authentic_p = true; - else *authentic_p = false; - } + bool ident_failed; + + /* We were unable to get ident to give us a username */ + char ident_username[IDENT_USERNAME_MAX + 1]; + + /* The username returned by ident */ + + ident(port.raddr.sin_addr, port.laddr.sin_addr, + port.raddr.sin_port, port.laddr.sin_port, + &ident_failed, ident_username); + + if (ident_failed) + *authentic_p = false; + else + { + bool checks_out; + + verify_against_usermap(DataDir, + postgres_username, ident_username, usermap_name, + &checks_out); + if (checks_out) + *authentic_p = true; + else + *authentic_p = false; + } } extern int -hba_recvauth(const Port *port, const char database[], const char user[], - const char DataDir[]) { +hba_recvauth(const Port * port, const char database[], const char user[], + const char DataDir[]) +{ /*--------------------------------------------------------------------------- Determine if the TCP connection described by "port" is with someone allowed to act as user "user" and access database "database". Return STATUS_OK if yes; STATUS_ERROR if not. -----------------------------------------------------------------------------*/ - bool host_ok; - /* There's an entry for this database and remote host in the pg_hba file */ - char usermap_name[USERMAP_NAME_SIZE+1]; - /* The name of the map pg_hba specifies for this connection (or special - value "SAMEUSER") - */ - enum Userauth userauth; - /* The type of user authentication pg_hba specifies for this connection */ - int retvalue; - /* Our eventual return value */ - - - find_hba_entry(DataDir, port->raddr.sin_addr, database, - &host_ok, &userauth, usermap_name, - false /* don't find password entries of type 'password' */); - - if (!host_ok) retvalue = STATUS_ERROR; - else { - switch (userauth) { - case Trust: - retvalue = STATUS_OK; - break; - case Ident: { - /* Here's where we need to call up ident and authenticate the user */ - - bool authentic; /* He is who he says he is. */ - - authident(DataDir, *port, user, usermap_name, &authentic); - - if (authentic) retvalue = STATUS_OK; - else retvalue = STATUS_ERROR; - } - break; - default: - retvalue = STATUS_ERROR; - Assert(false); - } - } - return(retvalue); +----------------------------------------------------------------------------*/ + bool host_ok; + + /* + * There's an entry for this database and remote host in the pg_hba + * file + */ + char usermap_name[USERMAP_NAME_SIZE + 1]; + + /* + * The name of the map pg_hba specifies for this connection (or + * special value "SAMEUSER") + */ + enum Userauth userauth; + + /* + * The type of user authentication pg_hba specifies for this + * connection + */ + int retvalue; + + /* Our eventual return value */ + + + find_hba_entry(DataDir, port->raddr.sin_addr, database, + &host_ok, &userauth, usermap_name, + false /* don't find password entries of type + 'password' */ ); + + if (!host_ok) + retvalue = STATUS_ERROR; + else + { + switch (userauth) + { + case Trust: + retvalue = STATUS_OK; + break; + case Ident: + { + + /* + * Here's where we need to call up ident and authenticate + * the user + */ + + bool authentic; /* He is who he says he + * is. */ + + authident(DataDir, *port, user, usermap_name, &authentic); + + if (authentic) + retvalue = STATUS_OK; + else + retvalue = STATUS_ERROR; + } + break; + default: + retvalue = STATUS_ERROR; + Assert(false); + } + } + return (retvalue); } /*---------------------------------------------------------------- * This version of hba was written by Bryan Henderson - * in September 1996 for Release 6.0. It changed the format of the + * in September 1996 for Release 6.0. It changed the format of the * hba file and added ident function. * * Here are some notes about the original host based authentication - * the preceded this one. + * the preceded this one. * * based on the securelib package originally written by William * LeFebvre, EECS Department, Northwestern University @@ -765,4 +970,3 @@ hba_recvauth(const Port *port, const char database[], const char user[], * -----------------------------------------------------------------*/ - diff --git a/src/backend/libpq/password.c b/src/backend/libpq/password.c index 346d59e8bf6..1efc2e668a3 100644 --- a/src/backend/libpq/password.c +++ b/src/backend/libpq/password.c @@ -6,106 +6,116 @@ #include <string.h> #include <unistd.h> #ifdef HAVE_CRYPT_H -# include <crypt.h> +#include <crypt.h> #endif int -verify_password(char *user, char *password, Port *port, - char *database, char *DataDir) +verify_password(char *user, char *password, Port * port, + char *database, char *DataDir) { - bool host_ok; - enum Userauth userauth; - char pw_file_name[PWFILE_NAME_SIZE+1]; - - char *pw_file_fullname; - FILE *pw_file; - - char pw_file_line[255]; - char *p, *test_user, *test_pw; - char salt[3]; - - find_hba_entry(DataDir, port->raddr.sin_addr, database, - &host_ok, &userauth, pw_file_name, true); + bool host_ok; + enum Userauth userauth; + char pw_file_name[PWFILE_NAME_SIZE + 1]; + + char *pw_file_fullname; + FILE *pw_file; + + char pw_file_line[255]; + char *p, + *test_user, + *test_pw; + char salt[3]; + + find_hba_entry(DataDir, port->raddr.sin_addr, database, + &host_ok, &userauth, pw_file_name, true); + + if (!host_ok) + { + sprintf(PQerrormsg, + "verify_password: couldn't find entry for connecting host\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return STATUS_ERROR; + } - if(!host_ok) { - sprintf(PQerrormsg, - "verify_password: couldn't find entry for connecting host\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return STATUS_ERROR; - } + if (userauth != Password) + { + sprintf(PQerrormsg, + "verify_password: couldn't find entry of type 'password' " + "for this host\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return STATUS_ERROR; + } - if(userauth != Password) { - sprintf(PQerrormsg, - "verify_password: couldn't find entry of type 'password' " - "for this host\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return STATUS_ERROR; - } + if (!pw_file_name || pw_file_name[0] == '\0') + { + sprintf(PQerrormsg, + "verify_password: no password file specified\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return STATUS_ERROR; + } - if(!pw_file_name || pw_file_name[0] == '\0') { - sprintf(PQerrormsg, - "verify_password: no password file specified\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return STATUS_ERROR; - } + pw_file_fullname = (char *) malloc(strlen(DataDir) + strlen(pw_file_name) + 2); + strcpy(pw_file_fullname, DataDir); + strcat(pw_file_fullname, "/"); + strcat(pw_file_fullname, pw_file_name); + + pw_file = AllocateFile(pw_file_fullname, "r"); + if (!pw_file) + { + sprintf(PQerrormsg, + "verify_password: couldn't open password file '%s'\n", + pw_file_fullname); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return STATUS_ERROR; + } - pw_file_fullname = (char *)malloc(strlen(DataDir) + strlen(pw_file_name) + 2); - strcpy(pw_file_fullname, DataDir); - strcat(pw_file_fullname, "/"); - strcat(pw_file_fullname, pw_file_name); + while (!feof(pw_file)) + { + fgets(pw_file_line, 255, pw_file); + p = pw_file_line; + + test_user = strtok(p, ":"); + test_pw = strtok(NULL, ":"); + if (!test_user || !test_pw || + test_user[0] == '\0' || test_pw[0] == '\0') + { + continue; + } + + /* kill the newline */ + if (test_pw[strlen(test_pw) - 1] == '\n') + test_pw[strlen(test_pw) - 1] = '\0'; + + strNcpy(salt, test_pw, 2); + + if (strcmp(user, test_user) == 0) + { + /* we're outta here one way or the other. */ + FreeFile(pw_file); + + if (strcmp(crypt(password, salt), test_pw) == 0) + { + /* it matched. */ + return STATUS_OK; + } + + sprintf(PQerrormsg, + "verify_password: password mismatch for '%s'.\n", + user); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return STATUS_ERROR; + } + } - pw_file = AllocateFile(pw_file_fullname, "r"); - if(!pw_file) { sprintf(PQerrormsg, - "verify_password: couldn't open password file '%s'\n", - pw_file_fullname); + "verify_password: user '%s' not found in password file.\n", + user); fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); + pqdebug("%s", PQerrormsg); return STATUS_ERROR; - } - - while(!feof(pw_file)) { - fgets(pw_file_line, 255, pw_file); - p = pw_file_line; - - test_user = strtok(p, ":"); - test_pw = strtok(NULL, ":"); - if(!test_user || !test_pw || - test_user[0] == '\0' || test_pw[0] == '\0') { - continue; - } - - /* kill the newline */ - if (test_pw[strlen(test_pw)-1] == '\n') - test_pw[strlen(test_pw)-1] = '\0'; - - strNcpy(salt, test_pw, 2); - - if(strcmp(user, test_user) == 0) { - /* we're outta here one way or the other. */ - FreeFile(pw_file); - - if(strcmp(crypt(password, salt), test_pw) == 0) { - /* it matched. */ - return STATUS_OK; - } - - sprintf(PQerrormsg, - "verify_password: password mismatch for '%s'.\n", - user); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return STATUS_ERROR; - } - } - - sprintf(PQerrormsg, - "verify_password: user '%s' not found in password file.\n", - user); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return STATUS_ERROR; } diff --git a/src/backend/libpq/portal.c b/src/backend/libpq/portal.c index 301773be9f5..c3c8fe55e38 100644 --- a/src/backend/libpq/portal.c +++ b/src/backend/libpq/portal.c @@ -1,642 +1,655 @@ /*------------------------------------------------------------------------- * * portal.c-- - * generalized portal support routines + * generalized portal support routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/portal.c,v 1.6 1997/08/12 22:52:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/portal.c,v 1.7 1997/09/07 04:42:23 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * INTERFACE ROUTINES - * PQnportals - Return the number of open portals. - * PQpnames - Return all the portal names - * PQparray - Return the portal buffer given a portal name - * PQrulep - Return 1 if an asynchronous portal - * PQntuples - Return the number of tuples in a portal buffer - * PQninstances - same as PQntuples using object terminology - * PQngroups - Return the number of tuple groups in a portal buffer - * PQntuplesGroup - Return the number of tuples in a tuple group - * PQninstancesGroup - same as PQntuplesGroup using object terminology - * PQnfieldsGroup - Return the number of fields in a tuple group - * PQfnumberGroup - Return field number given (group index, field name) - * PQftypeGroup - Return field type given (group index, field index) - * PQfsizeGroup - Return field size given (group index, field index) - * PQfnameGroup - Return field name given (group index, field index) - * PQgroup - Return the tuple group that a particular tuple is in - * PQgetgroup - Return the index of the group that a tuple is in - * PQnfields - Return the number of fields in a tuple - * PQfnumber - Return the field index of a field name in a tuple - * PQfname - Return the name of a field - * PQftype - Return the type of a field - * PQfsize - Return the size of a field - * PQftype - Return the type of a field - * PQsametype - Return 1 if the two tuples have the same type - * PQgetvalue - Return an attribute (field) value - * PQgetlength - Return an attribute (field) length - * PQclear - free storage claimed by named portal - * PQnotifies - Return a list of relations on which notification - * has occurred. - * PQremoveNotify - Remove this notification from the list. + * INTERFACE ROUTINES + * PQnportals - Return the number of open portals. + * PQpnames - Return all the portal names + * PQparray - Return the portal buffer given a portal name + * PQrulep - Return 1 if an asynchronous portal + * PQntuples - Return the number of tuples in a portal buffer + * PQninstances - same as PQntuples using object terminology + * PQngroups - Return the number of tuple groups in a portal buffer + * PQntuplesGroup - Return the number of tuples in a tuple group + * PQninstancesGroup - same as PQntuplesGroup using object terminology + * PQnfieldsGroup - Return the number of fields in a tuple group + * PQfnumberGroup - Return field number given (group index, field name) + * PQftypeGroup - Return field type given (group index, field index) + * PQfsizeGroup - Return field size given (group index, field index) + * PQfnameGroup - Return field name given (group index, field index) + * PQgroup - Return the tuple group that a particular tuple is in + * PQgetgroup - Return the index of the group that a tuple is in + * PQnfields - Return the number of fields in a tuple + * PQfnumber - Return the field index of a field name in a tuple + * PQfname - Return the name of a field + * PQftype - Return the type of a field + * PQfsize - Return the size of a field + * PQftype - Return the type of a field + * PQsametype - Return 1 if the two tuples have the same type + * PQgetvalue - Return an attribute (field) value + * PQgetlength - Return an attribute (field) length + * PQclear - free storage claimed by named portal + * PQnotifies - Return a list of relations on which notification + * has occurred. + * PQremoveNotify - Remove this notification from the list. * - * NOTES - * These functions may be used by both frontend routines which - * communicate with a backend or by user-defined functions which - * are compiled or dynamically loaded into a backend. + * NOTES + * These functions may be used by both frontend routines which + * communicate with a backend or by user-defined functions which + * are compiled or dynamically loaded into a backend. * - * the portals[] array should be organized as a hash table for - * quick portal-by-name lookup. + * the portals[] array should be organized as a hash table for + * quick portal-by-name lookup. * - * Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal" - * see utils/mmgr/portalmem.c for why. -cim 2/22/91 + * Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal" + * see utils/mmgr/portalmem.c for why. -cim 2/22/91 * */ -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include <string.h> #include <postgres.h> #include <lib/dllist.h> -#include <libpq/libpq.h> /* where the declarations go */ +#include <libpq/libpq.h> /* where the declarations go */ #include <utils/exc.h> /* ---------------------------------------------------------------- - * Helper routines for PQ portal interface routines below + * Helper routines for PQ portal interface routines below * ---------------------------------------------------------------- */ static int in_range(char *msg, int value, int min, int max) { - if (value < min || value >= max) { - sprintf(PQerrormsg, "FATAL: %s, %d is not in range [%d,%d)\n", - msg, value, min, max); - pqdebug("%s", PQerrormsg); - fputs(PQerrormsg, stderr); - return(0); - } - return(1); + if (value < min || value >= max) + { + sprintf(PQerrormsg, "FATAL: %s, %d is not in range [%d,%d)\n", + msg, value, min, max); + pqdebug("%s", PQerrormsg); + fputs(PQerrormsg, stderr); + return (0); + } + return (1); } static int valid_pointer(char *msg, void *ptr) { - if (!ptr) { - sprintf(PQerrormsg, "FATAL: %s\n", msg); - pqdebug("%s", PQerrormsg); - fputs(PQerrormsg, stderr); - return(0); - } - return(1); + if (!ptr) + { + sprintf(PQerrormsg, "FATAL: %s\n", msg); + pqdebug("%s", PQerrormsg); + fputs(PQerrormsg, stderr); + return (0); + } + return (1); } /* ---------------------------------------------------------------- - * PQ portal interface routines + * PQ portal interface routines * ---------------------------------------------------------------- */ /* -------------------------------- - * PQnportals - Return the number of open portals. - * If rule_p, only return asynchronous portals. + * PQnportals - Return the number of open portals. + * If rule_p, only return asynchronous portals. * -------------------------------- */ int PQnportals(int rule_p) { - int i, n = 0; - - for (i = 0; i < portals_array_size; ++i) { - if (portals[i] && portals[i]->portal) { - if (!rule_p || portals[i]->portal->rule_p) { - ++n; - } + int i, + n = 0; + + for (i = 0; i < portals_array_size; ++i) + { + if (portals[i] && portals[i]->portal) + { + if (!rule_p || portals[i]->portal->rule_p) + { + ++n; + } + } } - } - return(n); + return (n); } /* -------------------------------- - * PQpnames - Return all the portal names - * If rule_p, only return asynchronous portals. + * PQpnames - Return all the portal names + * If rule_p, only return asynchronous portals. * - * the caller must have allocated sufficient memory for char** pnames - * (an array of PQnportals strings of length PortalNameLength). + * the caller must have allocated sufficient memory for char** pnames + * (an array of PQnportals strings of length PortalNameLength). * - * notice that this assumes that the user is calling PQnportals and - * PQpnames with the same rule_p argument, and with no intervening - * portal closures. if not, you can get in heap big trouble.. + * notice that this assumes that the user is calling PQnportals and + * PQpnames with the same rule_p argument, and with no intervening + * portal closures. if not, you can get in heap big trouble.. * -------------------------------- */ void PQpnames(char **pnames, int rule_p) { - int i, cur_pname = 0; - - if (!valid_pointer("PQpnames: invalid name buffer", pnames)) - return; - - for (i = 0; i < portals_array_size; ++i) { - if (portals[i] && portals[i]->portal) { - if (!rule_p || portals[i]->portal->rule_p) { - strncpy(pnames[cur_pname], portals[i]->name, PortalNameLength); - ++cur_pname; - } + int i, + cur_pname = 0; + + if (!valid_pointer("PQpnames: invalid name buffer", pnames)) + return; + + for (i = 0; i < portals_array_size; ++i) + { + if (portals[i] && portals[i]->portal) + { + if (!rule_p || portals[i]->portal->rule_p) + { + strncpy(pnames[cur_pname], portals[i]->name, PortalNameLength); + ++cur_pname; + } + } } - } } /* -------------------------------- - * PQparray - Return the portal buffer given a portal name + * PQparray - Return the portal buffer given a portal name * -------------------------------- */ -PortalBuffer * +PortalBuffer * PQparray(char *pname) { - int i; - - if (!valid_pointer("PQparray: invalid name buffer", pname)) - return NULL; - - if ((i = pbuf_getIndex(pname)) < 0) - return((PortalBuffer *) NULL); - return(portals[i]->portal); + int i; + + if (!valid_pointer("PQparray: invalid name buffer", pname)) + return NULL; + + if ((i = pbuf_getIndex(pname)) < 0) + return ((PortalBuffer *) NULL); + return (portals[i]->portal); } /* -------------------------------- - * PQrulep - Return 1 if an asynchronous portal + * PQrulep - Return 1 if an asynchronous portal * -------------------------------- */ int -PQrulep(PortalBuffer *portal) +PQrulep(PortalBuffer * portal) { - if (!valid_pointer("PQrulep: invalid portal pointer", portal)) - return(-1); - - return(portal->rule_p); + if (!valid_pointer("PQrulep: invalid portal pointer", portal)) + return (-1); + + return (portal->rule_p); } /* -------------------------------- - * PQntuples - Return the number of tuples in a portal buffer + * PQntuples - Return the number of tuples in a portal buffer * -------------------------------- */ int -PQntuples(PortalBuffer *portal) +PQntuples(PortalBuffer * portal) { - if (!valid_pointer("PQntuples: invalid portal pointer", portal)) - return(-1); - - return(portal->no_tuples); + if (!valid_pointer("PQntuples: invalid portal pointer", portal)) + return (-1); + + return (portal->no_tuples); } int -PQninstances(PortalBuffer *portal) +PQninstances(PortalBuffer * portal) { - return(PQntuples(portal)); + return (PQntuples(portal)); } /* -------------------------------- - * PQngroups - Return the number of tuple groups in a portal buffer + * PQngroups - Return the number of tuple groups in a portal buffer * -------------------------------- */ int -PQngroups(PortalBuffer *portal) +PQngroups(PortalBuffer * portal) { - if (!valid_pointer("PQngroups: invalid portal pointer", portal)) - return(-1); - - return(portal->no_groups); + if (!valid_pointer("PQngroups: invalid portal pointer", portal)) + return (-1); + + return (portal->no_groups); } /* -------------------------------- - * PQntuplesGroup - Return the number of tuples in a tuple group + * PQntuplesGroup - Return the number of tuples in a tuple group * -------------------------------- */ int -PQntuplesGroup(PortalBuffer *portal, int group_index) +PQntuplesGroup(PortalBuffer * portal, int group_index) { - GroupBuffer *gbp; - - if (!valid_pointer("PQntuplesGroup: invalid portal pointer", portal) || - !in_range("PQntuplesGroup: group index", - group_index, 0, portal->no_groups)) - return(-1); + GroupBuffer *gbp; - gbp = pbuf_findGroup(portal, group_index); - if (gbp) - return(gbp->no_tuples); - return(-1); + if (!valid_pointer("PQntuplesGroup: invalid portal pointer", portal) || + !in_range("PQntuplesGroup: group index", + group_index, 0, portal->no_groups)) + return (-1); + + gbp = pbuf_findGroup(portal, group_index); + if (gbp) + return (gbp->no_tuples); + return (-1); } int -PQninstancesGroup(PortalBuffer *portal, int group_index) +PQninstancesGroup(PortalBuffer * portal, int group_index) { - return(PQntuplesGroup(portal, group_index)); + return (PQntuplesGroup(portal, group_index)); } /* -------------------------------- - * PQnfieldsGroup - Return the number of fields in a tuple group + * PQnfieldsGroup - Return the number of fields in a tuple group * -------------------------------- */ int -PQnfieldsGroup(PortalBuffer *portal, int group_index) +PQnfieldsGroup(PortalBuffer * portal, int group_index) { - GroupBuffer *gbp; - - if (!valid_pointer("PQnfieldsGroup: invalid portal pointer", portal) || - !in_range("PQnfieldsGroup: group index", - group_index, 0, portal->no_groups)) - return(-1); - gbp = pbuf_findGroup(portal, group_index); - if (gbp) - return(gbp->no_fields); - return(-1); + GroupBuffer *gbp; + + if (!valid_pointer("PQnfieldsGroup: invalid portal pointer", portal) || + !in_range("PQnfieldsGroup: group index", + group_index, 0, portal->no_groups)) + return (-1); + gbp = pbuf_findGroup(portal, group_index); + if (gbp) + return (gbp->no_fields); + return (-1); } /* -------------------------------- - * PQfnumberGroup - Return the field number (index) given - * the group index and the field name + * PQfnumberGroup - Return the field number (index) given + * the group index and the field name * -------------------------------- */ int -PQfnumberGroup(PortalBuffer *portal, int group_index, char *field_name) -{ - GroupBuffer *gbp; - - if (!valid_pointer("PQfnumberGroup: invalid portal pointer", portal) || - !valid_pointer("PQfnumberGroup: invalid field name pointer", - field_name) || - !in_range("PQfnumberGroup: group index", - group_index, 0, portal->no_groups)) - return(-1); - gbp = pbuf_findGroup(portal, group_index); - if (gbp) - return(pbuf_findFnumber(gbp, field_name)); - return(-1); +PQfnumberGroup(PortalBuffer * portal, int group_index, char *field_name) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfnumberGroup: invalid portal pointer", portal) || + !valid_pointer("PQfnumberGroup: invalid field name pointer", + field_name) || + !in_range("PQfnumberGroup: group index", + group_index, 0, portal->no_groups)) + return (-1); + gbp = pbuf_findGroup(portal, group_index); + if (gbp) + return (pbuf_findFnumber(gbp, field_name)); + return (-1); } /* -------------------------------- - * PQfnameGroup - Return the field (attribute) name given - * the group index and field index. + * PQfnameGroup - Return the field (attribute) name given + * the group index and field index. * -------------------------------- */ -char * -PQfnameGroup(PortalBuffer *portal, int group_index, int field_number) -{ - GroupBuffer *gbp; - - if (!valid_pointer("PQfnameGroup: invalid portal pointer", portal) || - !in_range("PQfnameGroup: group index", - group_index, 0, portal->no_groups)) - return((char *) NULL); - - if ((gbp = pbuf_findGroup(portal, group_index)) && - in_range("PQfnameGroup: field number", - field_number, 0, gbp->no_fields)) - return(pbuf_findFname(gbp, field_number)); - return((char *) NULL); +char * +PQfnameGroup(PortalBuffer * portal, int group_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfnameGroup: invalid portal pointer", portal) || + !in_range("PQfnameGroup: group index", + group_index, 0, portal->no_groups)) + return ((char *) NULL); + + if ((gbp = pbuf_findGroup(portal, group_index)) && + in_range("PQfnameGroup: field number", + field_number, 0, gbp->no_fields)) + return (pbuf_findFname(gbp, field_number)); + return ((char *) NULL); } /* -------------------------------- - * PQftypeGroup - Return the type of a field given - * the group index and field index + * PQftypeGroup - Return the type of a field given + * the group index and field index * -------------------------------- */ int -PQftypeGroup(PortalBuffer *portal, int group_index, int field_number) +PQftypeGroup(PortalBuffer * portal, int group_index, int field_number) { - GroupBuffer *gbp; - - if (!valid_pointer("PQftypeGroup: invalid portal pointer", portal) || - !in_range("PQftypeGroup: group index", - group_index, 0, portal->no_groups)) - return(-1); - - if ((gbp = pbuf_findGroup(portal, group_index)) && - in_range("PQftypeGroup: field number", field_number, 0, gbp->no_fields)) - return(gbp->types[field_number].adtid); - return(-1); + GroupBuffer *gbp; + + if (!valid_pointer("PQftypeGroup: invalid portal pointer", portal) || + !in_range("PQftypeGroup: group index", + group_index, 0, portal->no_groups)) + return (-1); + + if ((gbp = pbuf_findGroup(portal, group_index)) && + in_range("PQftypeGroup: field number", field_number, 0, gbp->no_fields)) + return (gbp->types[field_number].adtid); + return (-1); } /* -------------------------------- - * PQfsizeGroup - Return the size of a field given - * the group index and field index + * PQfsizeGroup - Return the size of a field given + * the group index and field index * -------------------------------- */ int -PQfsizeGroup(PortalBuffer *portal, int group_index, int field_number) +PQfsizeGroup(PortalBuffer * portal, int group_index, int field_number) { - GroupBuffer *gbp; - - if (!valid_pointer("PQfsizeGroup: invalid portal pointer", portal) || - !in_range("PQfsizeGroup: tuple index", - group_index, 0, portal->no_groups)) - return(-1); - - if ((gbp = pbuf_findGroup(portal, group_index)) && - in_range("PQfsizeGroup: field number", field_number, 0, gbp->no_fields)) - return(gbp->types[field_number].adtsize); - return(-1); + GroupBuffer *gbp; + + if (!valid_pointer("PQfsizeGroup: invalid portal pointer", portal) || + !in_range("PQfsizeGroup: tuple index", + group_index, 0, portal->no_groups)) + return (-1); + + if ((gbp = pbuf_findGroup(portal, group_index)) && + in_range("PQfsizeGroup: field number", field_number, 0, gbp->no_fields)) + return (gbp->types[field_number].adtsize); + return (-1); } /* -------------------------------- - * PQgroup - Return the tuple group that a particular tuple is in + * PQgroup - Return the tuple group that a particular tuple is in * -------------------------------- */ -GroupBuffer * -PQgroup(PortalBuffer *portal, int tuple_index) -{ - GroupBuffer *gbp; - int tuple_count = 0; - - if (!valid_pointer("PQgroup: invalid portal pointer", portal) || - !in_range("PQgroup: tuple index", - tuple_index, 0, portal->no_tuples)) - return((GroupBuffer *) NULL); - - for (gbp = portal->groups; - gbp && tuple_index >= (tuple_count += gbp->no_tuples); - gbp = gbp->next) - ; - if (!in_range("PQgroup: tuple not found: tuple index", - tuple_index, 0, tuple_count)) - return((GroupBuffer *) NULL); - return(gbp); +GroupBuffer * +PQgroup(PortalBuffer * portal, int tuple_index) +{ + GroupBuffer *gbp; + int tuple_count = 0; + + if (!valid_pointer("PQgroup: invalid portal pointer", portal) || + !in_range("PQgroup: tuple index", + tuple_index, 0, portal->no_tuples)) + return ((GroupBuffer *) NULL); + + for (gbp = portal->groups; + gbp && tuple_index >= (tuple_count += gbp->no_tuples); + gbp = gbp->next) + ; + if (!in_range("PQgroup: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return ((GroupBuffer *) NULL); + return (gbp); } /* -------------------------------- - * PQgetgroup - Return the index of the group that a - * particular tuple is in + * PQgetgroup - Return the index of the group that a + * particular tuple is in * -------------------------------- */ int -PQgetgroup(PortalBuffer *portal, int tuple_index) -{ - GroupBuffer *gbp; - int tuple_count = 0, group_count = 0; - - if (!valid_pointer("PQgetgroup: invalid portal pointer", portal) || - !in_range("PQgetgroup: tuple index", - tuple_index, 0, portal->no_tuples)) - return(-1); - - for (gbp = portal->groups; - gbp && tuple_index >= (tuple_count += gbp->no_tuples); - gbp = gbp->next) - ++group_count; - if (!gbp || !in_range("PQgetgroup: tuple not found: tuple index", - tuple_index, 0, tuple_count)) - return(-1); - return(group_count); +PQgetgroup(PortalBuffer * portal, int tuple_index) +{ + GroupBuffer *gbp; + int tuple_count = 0, + group_count = 0; + + if (!valid_pointer("PQgetgroup: invalid portal pointer", portal) || + !in_range("PQgetgroup: tuple index", + tuple_index, 0, portal->no_tuples)) + return (-1); + + for (gbp = portal->groups; + gbp && tuple_index >= (tuple_count += gbp->no_tuples); + gbp = gbp->next) + ++group_count; + if (!gbp || !in_range("PQgetgroup: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return (-1); + return (group_count); } /* -------------------------------- - * PQnfields - Return the number of fields in a tuple + * PQnfields - Return the number of fields in a tuple * -------------------------------- */ int -PQnfields(PortalBuffer *portal, int tuple_index) +PQnfields(PortalBuffer * portal, int tuple_index) { - GroupBuffer *gbp; - - if (!valid_pointer("PQnfields: invalid portal pointer", portal) || - !in_range("PQnfields: tuple index", - tuple_index, 0, portal->no_tuples)) - return(-1); - gbp = PQgroup(portal, tuple_index); - if (gbp) - return(gbp->no_fields); - return(-1); + GroupBuffer *gbp; + + if (!valid_pointer("PQnfields: invalid portal pointer", portal) || + !in_range("PQnfields: tuple index", + tuple_index, 0, portal->no_tuples)) + return (-1); + gbp = PQgroup(portal, tuple_index); + if (gbp) + return (gbp->no_fields); + return (-1); } /* -------------------------------- - * PQfnumber - Return the field index of a given - * field name within a tuple. + * PQfnumber - Return the field index of a given + * field name within a tuple. * -------------------------------- */ int -PQfnumber(PortalBuffer *portal, int tuple_index, char *field_name) +PQfnumber(PortalBuffer * portal, int tuple_index, char *field_name) { - GroupBuffer *gbp; - - if (!valid_pointer("PQfnumber: invalid portal pointer", portal) || + GroupBuffer *gbp; + + if (!valid_pointer("PQfnumber: invalid portal pointer", portal) || !valid_pointer("PQfnumber: invalid field name pointer", field_name) || - !in_range("PQfnumber: tuple index", - tuple_index, 0, portal->no_tuples)) - return(-1); - gbp = PQgroup(portal, tuple_index); - if (gbp) - return(pbuf_findFnumber(gbp, field_name)); - return(-1); + !in_range("PQfnumber: tuple index", + tuple_index, 0, portal->no_tuples)) + return (-1); + gbp = PQgroup(portal, tuple_index); + if (gbp) + return (pbuf_findFnumber(gbp, field_name)); + return (-1); } /* -------------------------------- - * PQfname - Return the name of a field + * PQfname - Return the name of a field * -------------------------------- */ -char * -PQfname(PortalBuffer *portal, int tuple_index, int field_number) -{ - GroupBuffer *gbp; - - if (!valid_pointer("PQfname: invalid portal pointer", portal) || - !in_range("PQfname: tuple index", - tuple_index, 0, portal->no_tuples)) - return((char *) NULL); - - if ((gbp = PQgroup(portal, tuple_index)) && - in_range("PQfname: field number", - field_number, 0, gbp->no_fields)) - return(pbuf_findFname(gbp, field_number)); - return((char *) NULL); +char * +PQfname(PortalBuffer * portal, int tuple_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfname: invalid portal pointer", portal) || + !in_range("PQfname: tuple index", + tuple_index, 0, portal->no_tuples)) + return ((char *) NULL); + + if ((gbp = PQgroup(portal, tuple_index)) && + in_range("PQfname: field number", + field_number, 0, gbp->no_fields)) + return (pbuf_findFname(gbp, field_number)); + return ((char *) NULL); } /* -------------------------------- - * PQftype - Return the type of a field + * PQftype - Return the type of a field * -------------------------------- */ int -PQftype(PortalBuffer *portal, int tuple_index, int field_number) +PQftype(PortalBuffer * portal, int tuple_index, int field_number) { - GroupBuffer *gbp; - - if (!valid_pointer("PQftype: invalid portal pointer", portal) || - !in_range("PQfname: tuple index", - tuple_index, 0, portal->no_tuples)) - return(-1); - - if ((gbp = PQgroup(portal, tuple_index)) && - in_range("PQftype: field number", field_number, 0, gbp->no_fields)) - return(gbp->types[field_number].adtid); - return(-1); + GroupBuffer *gbp; + + if (!valid_pointer("PQftype: invalid portal pointer", portal) || + !in_range("PQfname: tuple index", + tuple_index, 0, portal->no_tuples)) + return (-1); + + if ((gbp = PQgroup(portal, tuple_index)) && + in_range("PQftype: field number", field_number, 0, gbp->no_fields)) + return (gbp->types[field_number].adtid); + return (-1); } /* -------------------------------- - * PQfsize - Return the size of a field + * PQfsize - Return the size of a field * -------------------------------- */ int -PQfsize(PortalBuffer *portal, int tuple_index, int field_number) +PQfsize(PortalBuffer * portal, int tuple_index, int field_number) { - GroupBuffer *gbp; - - if (!valid_pointer("PQfsize: invalid portal pointer", portal) || - !in_range("PQfsize: tuple index", - tuple_index, 0, portal->no_tuples)) - return(-1); - - if ((gbp = PQgroup(portal, tuple_index)) && - in_range("PQfsize: field number", field_number, 0, gbp->no_fields)) - return(gbp->types[field_number].adtsize); - return(-1); + GroupBuffer *gbp; + + if (!valid_pointer("PQfsize: invalid portal pointer", portal) || + !in_range("PQfsize: tuple index", + tuple_index, 0, portal->no_tuples)) + return (-1); + + if ((gbp = PQgroup(portal, tuple_index)) && + in_range("PQfsize: field number", field_number, 0, gbp->no_fields)) + return (gbp->types[field_number].adtsize); + return (-1); } - + /* -------------------------------- - * PQsametype - Return 1 if the two tuples have the same type - * (in the same group) + * PQsametype - Return 1 if the two tuples have the same type + * (in the same group) * -------------------------------- */ int -PQsametype(PortalBuffer *portal, int tuple_index1, int tuple_index2) -{ - GroupBuffer *gbp1, *gbp2; - - if (!valid_pointer("PQsametype: invalid portal pointer", portal) || - !in_range("PQsametype: tuple index 1", - tuple_index1, 0, portal->no_tuples) || - !in_range("PQsametype: tuple index 2", - tuple_index2, 0, portal->no_tuples)) - return(-1); - - gbp1 = PQgroup(portal, tuple_index1); - gbp2 = PQgroup(portal, tuple_index2); - if (gbp1 && gbp2) - return(gbp1 == gbp2); - return(-1); +PQsametype(PortalBuffer * portal, int tuple_index1, int tuple_index2) +{ + GroupBuffer *gbp1, + *gbp2; + + if (!valid_pointer("PQsametype: invalid portal pointer", portal) || + !in_range("PQsametype: tuple index 1", + tuple_index1, 0, portal->no_tuples) || + !in_range("PQsametype: tuple index 2", + tuple_index2, 0, portal->no_tuples)) + return (-1); + + gbp1 = PQgroup(portal, tuple_index1); + gbp2 = PQgroup(portal, tuple_index2); + if (gbp1 && gbp2) + return (gbp1 == gbp2); + return (-1); } static TupleBlock * -PQGetTupleBlock(PortalBuffer *portal, - int tuple_index, - int *tuple_offset) -{ - GroupBuffer *gbp; - TupleBlock *tbp; - int tuple_count = 0; - - if (!valid_pointer("PQGetTupleBlock: invalid portal pointer", portal) || - !valid_pointer("PQGetTupleBlock: invalid offset pointer", - tuple_offset) || - !in_range("PQGetTupleBlock: tuple index", - tuple_index, 0, portal->no_tuples)) - return((TupleBlock *) NULL); - - for (gbp = portal->groups; - gbp && tuple_index >= (tuple_count += gbp->no_tuples); - gbp = gbp->next) - ; - if (!gbp || - !in_range("PQGetTupleBlock: tuple not found: tuple index", - tuple_index, 0, tuple_count)) - return((TupleBlock *) NULL); - tuple_count -= gbp->no_tuples; - for (tbp = gbp->tuples; - tbp && tuple_index >= (tuple_count += TupleBlockSize); - tbp = tbp->next) - ; - if (!tbp || - !in_range("PQGetTupleBlock: tuple not found: tuple index", - tuple_index, 0, tuple_count)) - return((TupleBlock *) NULL); - tuple_count -= TupleBlockSize; - - *tuple_offset = tuple_index - tuple_count; - return(tbp); +PQGetTupleBlock(PortalBuffer * portal, + int tuple_index, + int *tuple_offset) +{ + GroupBuffer *gbp; + TupleBlock *tbp; + int tuple_count = 0; + + if (!valid_pointer("PQGetTupleBlock: invalid portal pointer", portal) || + !valid_pointer("PQGetTupleBlock: invalid offset pointer", + tuple_offset) || + !in_range("PQGetTupleBlock: tuple index", + tuple_index, 0, portal->no_tuples)) + return ((TupleBlock *) NULL); + + for (gbp = portal->groups; + gbp && tuple_index >= (tuple_count += gbp->no_tuples); + gbp = gbp->next) + ; + if (!gbp || + !in_range("PQGetTupleBlock: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return ((TupleBlock *) NULL); + tuple_count -= gbp->no_tuples; + for (tbp = gbp->tuples; + tbp && tuple_index >= (tuple_count += TupleBlockSize); + tbp = tbp->next) + ; + if (!tbp || + !in_range("PQGetTupleBlock: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return ((TupleBlock *) NULL); + tuple_count -= TupleBlockSize; + + *tuple_offset = tuple_index - tuple_count; + return (tbp); } /* -------------------------------- - * PQgetvalue - Return an attribute (field) value + * PQgetvalue - Return an attribute (field) value * -------------------------------- */ -char * -PQgetvalue(PortalBuffer *portal, - int tuple_index, - int field_number) +char * +PQgetvalue(PortalBuffer * portal, + int tuple_index, + int field_number) { - TupleBlock *tbp; - int tuple_offset; + TupleBlock *tbp; + int tuple_offset; - tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); - if (tbp) - return(tbp->values[tuple_offset][field_number]); - return((char *) NULL); + tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); + if (tbp) + return (tbp->values[tuple_offset][field_number]); + return ((char *) NULL); } /* -------------------------------- - * PQgetAttr - Return an attribute (field) value - * this differs from PQgetvalue in that the value returned is - * a copy. The CALLER is responsible for free'ing the data returned. + * PQgetAttr - Return an attribute (field) value + * this differs from PQgetvalue in that the value returned is + * a copy. The CALLER is responsible for free'ing the data returned. * -------------------------------- */ -char * -PQgetAttr(PortalBuffer *portal, - int tuple_index, - int field_number) -{ - TupleBlock *tbp; - int tuple_offset; - int len; - char* result = NULL; - - tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); - if (tbp) { - len = tbp->lengths[tuple_offset][field_number]; - result = malloc(len + 1); - memcpy(result, - tbp->values[tuple_offset][field_number], - len); - result[len] = '\0'; - } - return result; +char * +PQgetAttr(PortalBuffer * portal, + int tuple_index, + int field_number) +{ + TupleBlock *tbp; + int tuple_offset; + int len; + char *result = NULL; + + tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); + if (tbp) + { + len = tbp->lengths[tuple_offset][field_number]; + result = malloc(len + 1); + memcpy(result, + tbp->values[tuple_offset][field_number], + len); + result[len] = '\0'; + } + return result; } /* -------------------------------- - * PQgetlength - Return an attribute (field) length + * PQgetlength - Return an attribute (field) length * -------------------------------- */ int -PQgetlength(PortalBuffer *portal, - int tuple_index, - int field_number) +PQgetlength(PortalBuffer * portal, + int tuple_index, + int field_number) { - TupleBlock *tbp; - int tuple_offset; + TupleBlock *tbp; + int tuple_offset; - tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); - if (tbp) - return(tbp->lengths[tuple_offset][field_number]); - return(-1); + tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); + if (tbp) + return (tbp->lengths[tuple_offset][field_number]); + return (-1); } /* ---------------- - * PQclear - free storage claimed by named portal + * PQclear - free storage claimed by named portal * ---------------- */ void PQclear(char *pname) -{ - if (!valid_pointer("PQclear: invalid portal name pointer", pname)) - return; - pbuf_close(pname); +{ + if (!valid_pointer("PQclear: invalid portal name pointer", pname)) + return; + pbuf_close(pname); } /* @@ -644,74 +657,81 @@ PQclear(char *pname) * This is going away with pending rewrite of comm. code... */ /* static SLList pqNotifyList;*/ -static Dllist *pqNotifyList = NULL; +static Dllist *pqNotifyList = NULL; /* remove invalid notifies before returning */ void PQcleanNotify() { - Dlelem *e, *next; - PQNotifyList *p; - - e = DLGetHead(pqNotifyList); - - while (e) { - next = DLGetSucc(e); - p = (PQNotifyList*)DLE_VAL(e); - if (p->valid == 0) { - DLRemove(e); - DLFreeElem(e); - pfree(p); - } - e = next; - } + Dlelem *e, + *next; + PQNotifyList *p; + + e = DLGetHead(pqNotifyList); + + while (e) + { + next = DLGetSucc(e); + p = (PQNotifyList *) DLE_VAL(e); + if (p->valid == 0) + { + DLRemove(e); + DLFreeElem(e); + pfree(p); + } + e = next; + } } void PQnotifies_init() { - Dlelem *e; - PQNotifyList *p; - - if (pqNotifyList == NULL) { - pqNotifyList = DLNewList(); - } - else { - /* clean all notifies */ - for (e = DLGetHead(pqNotifyList); e != NULL; e = DLGetSucc(e)) { - p = (PQNotifyList*)DLE_VAL(e); - p->valid = 0; + Dlelem *e; + PQNotifyList *p; + + if (pqNotifyList == NULL) + { + pqNotifyList = DLNewList(); + } + else + { + /* clean all notifies */ + for (e = DLGetHead(pqNotifyList); e != NULL; e = DLGetSucc(e)) + { + p = (PQNotifyList *) DLE_VAL(e); + p->valid = 0; + } + PQcleanNotify(); } - PQcleanNotify(); - } } -PQNotifyList * +PQNotifyList * PQnotifies() { - Dlelem *e; - PQcleanNotify(); - e = DLGetHead(pqNotifyList); - return (e ? (PQNotifyList*)DLE_VAL(e) : NULL); + Dlelem *e; + + PQcleanNotify(); + e = DLGetHead(pqNotifyList); + return (e ? (PQNotifyList *) DLE_VAL(e) : NULL); } void -PQremoveNotify(PQNotifyList *nPtr) +PQremoveNotify(PQNotifyList * nPtr) { - nPtr->valid = 0; /* remove later */ + nPtr->valid = 0; /* remove later */ } void PQappendNotify(char *relname, int pid) { - PQNotifyList *p; - - if (pqNotifyList == NULL) - pqNotifyList = DLNewList(); - - p = (PQNotifyList*)pbuf_alloc(sizeof(PQNotifyList)); - strNcpy(p->relname, relname, NAMEDATALEN-1); - p->be_pid = pid; - p->valid = 1; - DLAddTail(pqNotifyList, DLNewElem(p)); + PQNotifyList *p; + + if (pqNotifyList == NULL) + pqNotifyList = DLNewList(); + + p = (PQNotifyList *) pbuf_alloc(sizeof(PQNotifyList)); + strNcpy(p->relname, relname, NAMEDATALEN - 1); + p->be_pid = pid; + p->valid = 1; + DLAddTail(pqNotifyList, DLNewElem(p)); } diff --git a/src/backend/libpq/portalbuf.c b/src/backend/libpq/portalbuf.c index b7a527dcfaf..ed2d5bbe615 100644 --- a/src/backend/libpq/portalbuf.c +++ b/src/backend/libpq/portalbuf.c @@ -1,50 +1,50 @@ /*------------------------------------------------------------------------- * * portalbuf.c-- - * portal buffer support routines for src/libpq/portal.c + * portal buffer support routines for src/libpq/portal.c * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/portalbuf.c,v 1.4 1997/08/12 20:15:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/portalbuf.c,v 1.5 1997/09/07 04:42:24 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * pbuf_alloc - allocate memory for libpq routines - * pbuf_free - free memory for libpq routines - * pbuf_addPortal - Allocate a new portal buffer - * pbuf_addGroup - Add a new tuple group to the portal - * pbuf_addTypes - Allocate n type blocks - * pbuf_addTuples - Allocate a tuple block - * pbuf_addTuple - Allocate a tuple of n fields (attributes) - * pbuf_addValues - Allocate n bytes for a value - * pbuf_addEntry - Allocate a portal entry - * pbuf_freeEntry - Free a portal entry in the portal table - * pbuf_freeTypes - Free up the space used by a portal - * pbuf_freeTuples - free space used by tuple block - * pbuf_freeGroup - free space used by group, types and tuples - * pbuf_freePortal - free space used by portal and portal's group - * pbuf_getIndex - Return the index of the portal entry - * pbuf_setup - Set up a portal for dumping data - * pbuf_close - Close a portal, remove it from the portal table - * pbuf_findGroup - Return group given the group_index - * pbuf_findFnumber - Return field index of a given field within a group - * pbuf_findFname - Find the field name given the field index - * pbuf_checkFnumber - signal an error if field number is out of bounds + * pbuf_alloc - allocate memory for libpq routines + * pbuf_free - free memory for libpq routines + * pbuf_addPortal - Allocate a new portal buffer + * pbuf_addGroup - Add a new tuple group to the portal + * pbuf_addTypes - Allocate n type blocks + * pbuf_addTuples - Allocate a tuple block + * pbuf_addTuple - Allocate a tuple of n fields (attributes) + * pbuf_addValues - Allocate n bytes for a value + * pbuf_addEntry - Allocate a portal entry + * pbuf_freeEntry - Free a portal entry in the portal table + * pbuf_freeTypes - Free up the space used by a portal + * pbuf_freeTuples - free space used by tuple block + * pbuf_freeGroup - free space used by group, types and tuples + * pbuf_freePortal - free space used by portal and portal's group + * pbuf_getIndex - Return the index of the portal entry + * pbuf_setup - Set up a portal for dumping data + * pbuf_close - Close a portal, remove it from the portal table + * pbuf_findGroup - Return group given the group_index + * pbuf_findFnumber - Return field index of a given field within a group + * pbuf_findFname - Find the field name given the field index + * pbuf_checkFnumber - signal an error if field number is out of bounds * * NOTES - * These functions may be used by both frontend routines which - * communicate with a backend or by user-defined functions which - * are compiled or dynamically loaded into a backend. + * These functions may be used by both frontend routines which + * communicate with a backend or by user-defined functions which + * are compiled or dynamically loaded into a backend. * - * the portals[] array should be organized as a hash table for - * quick portal-by-name lookup. + * the portals[] array should be organized as a hash table for + * quick portal-by-name lookup. * - * Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal" - * see utils/mmgr/portalmem.c for why. -cim 2/22/91 + * Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal" + * see utils/mmgr/portalmem.c for why. -cim 2/22/91 * */ #include <string.h> @@ -55,407 +55,417 @@ #include <libpq/libpq.h> /* where the declarations go */ #include <utils/exc.h> -PortalEntry** portals = (PortalEntry**) NULL; -size_t portals_array_size = 0; +PortalEntry **portals = (PortalEntry **) NULL; +size_t portals_array_size = 0; /* portals array memory is malloc'd instead of using MemoryContexts */ /* since it will be used by both front and backend programs*/ -/* GlobalMemory portals_mmcxt = (GlobalMemory) NULL; */ +/* GlobalMemory portals_mmcxt = (GlobalMemory) NULL; */ -/* ------------------------------- +/* ------------------------------- * portals_realloc -- - * grow the size of the portals array by size + * grow the size of the portals array by size * - * also ensures that elements are initially NULL + * also ensures that elements are initially NULL */ static void portals_realloc(size_t size) { - size_t oldsize; - int i; - PortalEntry** newp; - - oldsize = portals_array_size; - - portals_array_size += size; - if (portals) - newp= (PortalEntry**)realloc(portals, - portals_array_size*sizeof(PortalEntry*)); - else - newp= (PortalEntry**)malloc(portals_array_size*sizeof(PortalEntry*)); - - if (newp) - portals = newp; - else - libpq_raise(&PortalError, - form("Cannot alloc more memory in portals_realloc")); - - for (i=oldsize;i<portals_array_size;i++) - portals[i]=(PortalEntry*)NULL; - + size_t oldsize; + int i; + PortalEntry **newp; + + oldsize = portals_array_size; + + portals_array_size += size; + if (portals) + newp = (PortalEntry **) realloc(portals, + portals_array_size * sizeof(PortalEntry *)); + else + newp = (PortalEntry **) malloc(portals_array_size * sizeof(PortalEntry *)); + + if (newp) + portals = newp; + else + libpq_raise(&PortalError, + form("Cannot alloc more memory in portals_realloc")); + + for (i = oldsize; i < portals_array_size; i++) + portals[i] = (PortalEntry *) NULL; + } /* -------------------------------- - * pbuf_alloc - allocate memory for portal buffers + * pbuf_alloc - allocate memory for portal buffers * - * remember: palloc() in the backend uses the postgres MemoryContext - * library and palloc() in the frontend (fe-pqstubs.c) calls malloc(). + * remember: palloc() in the backend uses the postgres MemoryContext + * library and palloc() in the frontend (fe-pqstubs.c) calls malloc(). * -------------------------------- */ caddr_t pbuf_alloc(size_t size) { - caddr_t addr; - - if (size <= 0) - libpq_raise(&MemoryError, form("Invalid argument to pbuf_alloc().")); - - addr = (caddr_t) palloc(size); - if (addr == (caddr_t) NULL) - libpq_raise(&MemoryError, form("Cannot Allocate space.")); - - return (addr); + caddr_t addr; + + if (size <= 0) + libpq_raise(&MemoryError, form("Invalid argument to pbuf_alloc().")); + + addr = (caddr_t) palloc(size); + if (addr == (caddr_t) NULL) + libpq_raise(&MemoryError, form("Cannot Allocate space.")); + + return (addr); } /* -------------------------------- - * pbuf_free - free memory for portal buffers + * pbuf_free - free memory for portal buffers * - * remember: pfree() in the backend uses the postgres MemoryContext - * library and pfree() in the frontend (fe-pqstubs.c) calls free(). + * remember: pfree() in the backend uses the postgres MemoryContext + * library and pfree() in the frontend (fe-pqstubs.c) calls free(). * -------------------------------- */ void pbuf_free(caddr_t pointer) { - if (pointer) - pfree(pointer); - else - libpq_raise(&MemoryError, form("Tried to free NULL memory pointer")); - + if (pointer) + pfree(pointer); + else + libpq_raise(&MemoryError, form("Tried to free NULL memory pointer")); + } /* -------------------------------- - * pbuf_addPortal - Allocate a new portal buffer + * pbuf_addPortal - Allocate a new portal buffer * -------------------------------- */ -PortalBuffer * +PortalBuffer * pbuf_addPortal() { - PortalBuffer *portal; - - portal = (PortalBuffer *) - pbuf_alloc(sizeof (PortalBuffer)); - - portal->rule_p = 0; - portal->no_tuples = 0; - portal->no_groups = 0; - portal->groups = NULL; - - return (portal); + PortalBuffer *portal; + + portal = (PortalBuffer *) + pbuf_alloc(sizeof(PortalBuffer)); + + portal->rule_p = 0; + portal->no_tuples = 0; + portal->no_groups = 0; + portal->groups = NULL; + + return (portal); } /* -------------------------------- - * pbuf_addGroup - Add a new tuple group to the portal + * pbuf_addGroup - Add a new tuple group to the portal * -------------------------------- */ -GroupBuffer * -pbuf_addGroup(PortalBuffer *portal) +GroupBuffer * +pbuf_addGroup(PortalBuffer * portal) { - GroupBuffer *group, *group1; - - group = (GroupBuffer *) - pbuf_alloc(sizeof (GroupBuffer)); - - /* Initialize the new group buffer. */ - group->no_tuples = 0; - group->no_fields = 0; - group->types = NULL; - group->tuples = NULL; - group->next = NULL; - - if ((group1 = portal->groups) == NULL) - portal->groups = group; - else { - while (group1->next != NULL) - group1 = group1->next; - group1->next = group; - } - - return (group); + GroupBuffer *group, + *group1; + + group = (GroupBuffer *) + pbuf_alloc(sizeof(GroupBuffer)); + + /* Initialize the new group buffer. */ + group->no_tuples = 0; + group->no_fields = 0; + group->types = NULL; + group->tuples = NULL; + group->next = NULL; + + if ((group1 = portal->groups) == NULL) + portal->groups = group; + else + { + while (group1->next != NULL) + group1 = group1->next; + group1->next = group; + } + + return (group); } /* -------------------------------- - * pbuf_addTypes - Allocate n type blocks + * pbuf_addTypes - Allocate n type blocks * -------------------------------- */ -TypeBlock * +TypeBlock * pbuf_addTypes(int n) { - TypeBlock *types; - - types = (TypeBlock *) - pbuf_alloc(n * sizeof (TypeBlock)); - - return (types); + TypeBlock *types; + + types = (TypeBlock *) + pbuf_alloc(n * sizeof(TypeBlock)); + + return (types); } /* -------------------------------- - * pbuf_addTuples - Allocate a tuple block + * pbuf_addTuples - Allocate a tuple block * -------------------------------- */ -TupleBlock * +TupleBlock * pbuf_addTuples() { - TupleBlock *tuples; - - tuples = (TupleBlock *) - pbuf_alloc(sizeof (TupleBlock)); - - tuples->next = NULL; - tuples->tuple_index = 0; - - return (tuples); + TupleBlock *tuples; + + tuples = (TupleBlock *) + pbuf_alloc(sizeof(TupleBlock)); + + tuples->next = NULL; + tuples->tuple_index = 0; + + return (tuples); } /* -------------------------------- - * pbuf_addTuple - Allocate a tuple of n fields (attributes) + * pbuf_addTuple - Allocate a tuple of n fields (attributes) * -------------------------------- */ -char ** +char ** pbuf_addTuple(int n) { - return (char **) - pbuf_alloc(n * sizeof (char *)); + return (char **) + pbuf_alloc(n * sizeof(char *)); } /* -------------------------------- - * pbuf_addTupleValueLengths - Allocate a tuple of n lengths (attributes) + * pbuf_addTupleValueLengths - Allocate a tuple of n lengths (attributes) * -------------------------------- */ -int * +int * pbuf_addTupleValueLengths(int n) { - return (int *) + return (int *) pbuf_alloc(n * sizeof(int)); } /* -------------------------------- - * pbuf_addValues - Allocate n bytes for a value + * pbuf_addValues - Allocate n bytes for a value * -------------------------------- */ -char * +char * pbuf_addValues(int n) { - return + return pbuf_alloc(n); } /* -------------------------------- - * pbuf_addEntry - Allocate a portal entry + * pbuf_addEntry - Allocate a portal entry * -------------------------------- */ -PortalEntry *pbuf_addEntry() +PortalEntry * +pbuf_addEntry() { - return (PortalEntry *) - pbuf_alloc (sizeof (PortalEntry)); + return (PortalEntry *) + pbuf_alloc(sizeof(PortalEntry)); } /* -------------------------------- - * pbuf_freeEntry - Free a portal entry in the portal table - * the portal is freed separately. + * pbuf_freeEntry - Free a portal entry in the portal table + * the portal is freed separately. * -------------------------------- */ void pbuf_freeEntry(int i) { - if (portals) + if (portals) { - pbuf_free ((caddr_t)portals[i]); - portals[i] = NULL; + pbuf_free((caddr_t) portals[i]); + portals[i] = NULL; } } /* -------------------------------- - * pbuf_freeTypes - Free up the space used by a portal + * pbuf_freeTypes - Free up the space used by a portal * -------------------------------- */ void -pbuf_freeTypes(TypeBlock *types) +pbuf_freeTypes(TypeBlock * types) { - pbuf_free((caddr_t)types); + pbuf_free((caddr_t) types); } /* -------------------------------- - * pbuf_freeTuples - free space used by tuple block + * pbuf_freeTuples - free space used by tuple block * -------------------------------- */ void -pbuf_freeTuples(TupleBlock *tuples, - int no_tuples, - int no_fields) +pbuf_freeTuples(TupleBlock * tuples, + int no_tuples, + int no_fields) { - int i, j; - - if (no_tuples > TupleBlockSize) { - pbuf_freeTuples (tuples->next, no_tuples - TupleBlockSize, no_fields); - no_tuples = TupleBlockSize; - } - - /* For each tuple, free all its attribute values. */ - for (i = 0; i < no_tuples; i++) { - for (j = 0; j < no_fields; j++) - if (tuples->values[i][j] != NULL) - pbuf_free((caddr_t)tuples->values[i][j]); - if (tuples->lengths[i]) - pbuf_free((caddr_t)tuples->lengths[i]); - if (tuples->values[i]) - pbuf_free((caddr_t)tuples->values[i]); - } - - pbuf_free((caddr_t)tuples); + int i, + j; + + if (no_tuples > TupleBlockSize) + { + pbuf_freeTuples(tuples->next, no_tuples - TupleBlockSize, no_fields); + no_tuples = TupleBlockSize; + } + + /* For each tuple, free all its attribute values. */ + for (i = 0; i < no_tuples; i++) + { + for (j = 0; j < no_fields; j++) + if (tuples->values[i][j] != NULL) + pbuf_free((caddr_t) tuples->values[i][j]); + if (tuples->lengths[i]) + pbuf_free((caddr_t) tuples->lengths[i]); + if (tuples->values[i]) + pbuf_free((caddr_t) tuples->values[i]); + } + + pbuf_free((caddr_t) tuples); } /* -------------------------------- - * pbuf_freeGroup - free space used by group, types and tuples + * pbuf_freeGroup - free space used by group, types and tuples * -------------------------------- */ void -pbuf_freeGroup(GroupBuffer *group) +pbuf_freeGroup(GroupBuffer * group) { - if (group->next != NULL) - pbuf_freeGroup(group->next); - - if (group->types != NULL) - pbuf_freeTypes(group->types); - - if (group->tuples != NULL) - pbuf_freeTuples(group->tuples, group->no_tuples,group->no_fields); - - pbuf_free((caddr_t)group); + if (group->next != NULL) + pbuf_freeGroup(group->next); + + if (group->types != NULL) + pbuf_freeTypes(group->types); + + if (group->tuples != NULL) + pbuf_freeTuples(group->tuples, group->no_tuples, group->no_fields); + + pbuf_free((caddr_t) group); } /* -------------------------------- - * pbuf_freePortal - free space used by portal and portal's group + * pbuf_freePortal - free space used by portal and portal's group * -------------------------------- */ void -pbuf_freePortal(PortalBuffer *portal) +pbuf_freePortal(PortalBuffer * portal) { - if (portal->groups != NULL) - pbuf_freeGroup(portal->groups); - - pbuf_free((caddr_t)portal); + if (portal->groups != NULL) + pbuf_freeGroup(portal->groups); + + pbuf_free((caddr_t) portal); } /* -------------------------------- - * pbuf_getIndex - Return the index of the portal entry - * note: portals[] maps portal names to portal buffers. + * pbuf_getIndex - Return the index of the portal entry + * note: portals[] maps portal names to portal buffers. * -------------------------------- */ int pbuf_getIndex(char *pname) { - int i; - - if (portals) { - for (i = 0; i < portals_array_size; i++) - if (portals[i] != NULL && - strncmp(portals[i]->name, pname, PortalNameLength) == 0) - return i; - } - - return (-1); + int i; + + if (portals) + { + for (i = 0; i < portals_array_size; i++) + if (portals[i] != NULL && + strncmp(portals[i]->name, pname, PortalNameLength) == 0) + return i; + } + + return (-1); } /* -------------------------------- - * pbuf_setportalname - assign a user given name to a portal + * pbuf_setportalname - assign a user given name to a portal * -------------------------------- */ void -pbuf_setportalinfo(PortalEntry *entry, char *pname) +pbuf_setportalinfo(PortalEntry * entry, char *pname) { - if (entry) - strNcpy(entry->name, pname, PortalNameLength-1); + if (entry) + strNcpy(entry->name, pname, PortalNameLength - 1); } /* -------------------------------- - * pbuf_setup - Set up a portal for dumping data + * pbuf_setup - Set up a portal for dumping data * -------------------------------- */ -PortalEntry * +PortalEntry * pbuf_setup(char *pname) { - int i; - - if (!portals) /* the portals array has not been allocated yet */ + int i; + + if (!portals) /* the portals array has not been + * allocated yet */ { - /* allocate portals[] array here */ - portals_realloc(PORTALS_INITIAL_SIZE); + /* allocate portals[] array here */ + portals_realloc(PORTALS_INITIAL_SIZE); } - - /* If a portal with the same name already exists, close it. */ - /* else look for an empty entry in the portal table. */ - if ((i = pbuf_getIndex(pname)) != -1) - pbuf_freePortal(portals[i]->portal); - else { - for (i = 0; i < portals_array_size; i++) - if (portals[i] == NULL) - break; - - /* If the portal table is full, enlarge it */ - if (i >= portals_array_size) - portals_realloc(PORTALS_GROW_BY); - - portals[i] = pbuf_addEntry(); - strncpy(portals[i]->name, pname, PortalNameLength); - } - portals[i]->portal = pbuf_addPortal(); - portals[i]->portalcxt = NULL; - portals[i]->result = NULL; - - return portals[i]; + + /* If a portal with the same name already exists, close it. */ + /* else look for an empty entry in the portal table. */ + if ((i = pbuf_getIndex(pname)) != -1) + pbuf_freePortal(portals[i]->portal); + else + { + for (i = 0; i < portals_array_size; i++) + if (portals[i] == NULL) + break; + + /* If the portal table is full, enlarge it */ + if (i >= portals_array_size) + portals_realloc(PORTALS_GROW_BY); + + portals[i] = pbuf_addEntry(); + strncpy(portals[i]->name, pname, PortalNameLength); + } + portals[i]->portal = pbuf_addPortal(); + portals[i]->portalcxt = NULL; + portals[i]->result = NULL; + + return portals[i]; } /* -------------------------------- - * pbuf_close - Close a portal, remove it from the portal table - * and free up the space + * pbuf_close - Close a portal, remove it from the portal table + * and free up the space * -------------------------------- */ void pbuf_close(char *pname) { - int i; - - if ((i = pbuf_getIndex(pname)) == -1) - libpq_raise(&PortalError, form("Portal %s does not exist.", pname)); - - pbuf_freePortal(portals[i]->portal); - pbuf_freeEntry(i); + int i; + + if ((i = pbuf_getIndex(pname)) == -1) + libpq_raise(&PortalError, form("Portal %s does not exist.", pname)); + + pbuf_freePortal(portals[i]->portal); + pbuf_freeEntry(i); } /* -------------------------------- - * pbuf_findGroup - Return the group given the group_index + * pbuf_findGroup - Return the group given the group_index * -------------------------------- */ -GroupBuffer * -pbuf_findGroup(PortalBuffer *portal, - int group_index) +GroupBuffer * +pbuf_findGroup(PortalBuffer * portal, + int group_index) { - GroupBuffer *group; - - group = portal->groups; - while (group_index > 0 && group != NULL) { - group = group->next; - group_index--; - } - - if (group == NULL) - libpq_raise(&PortalError, - form("Group index %d out of bound.", group_index)); - - return (group); + GroupBuffer *group; + + group = portal->groups; + while (group_index > 0 && group != NULL) + { + group = group->next; + group_index--; + } + + if (group == NULL) + libpq_raise(&PortalError, + form("Group index %d out of bound.", group_index)); + + return (group); } /* -------------------------------- @@ -463,49 +473,48 @@ pbuf_findGroup(PortalBuffer *portal, * -------------------------------- */ int -pbuf_findFnumber(GroupBuffer *group, - char *field_name) -{ - TypeBlock *types; - int i; - - types = group->types; - - for (i = 0; i < group->no_fields; i++) - if (strncmp(types[i].name, field_name, NAMEDATALEN) == 0) - return (i); - - libpq_raise(&PortalError, - form("Field-name %s does not exist.", field_name)); - - /* not reached, here to make compiler happy */ - return 0; +pbuf_findFnumber(GroupBuffer * group, + char *field_name) +{ + TypeBlock *types; + int i; + + types = group->types; + + for (i = 0; i < group->no_fields; i++) + if (strncmp(types[i].name, field_name, NAMEDATALEN) == 0) + return (i); + + libpq_raise(&PortalError, + form("Field-name %s does not exist.", field_name)); + + /* not reached, here to make compiler happy */ + return 0; } /* -------------------------------- - * pbuf_checkFnumber - signal an error if field number is out of bounds + * pbuf_checkFnumber - signal an error if field number is out of bounds * -------------------------------- */ void -pbuf_checkFnumber(GroupBuffer *group, - int field_number) +pbuf_checkFnumber(GroupBuffer * group, + int field_number) { - if (field_number < 0 || field_number >= group->no_fields) - libpq_raise(&PortalError, - form("Field number %d out of bound.", field_number)); + if (field_number < 0 || field_number >= group->no_fields) + libpq_raise(&PortalError, + form("Field number %d out of bound.", field_number)); } /* -------------------------------- - * pbuf_findFname - Find the field name given the field index + * pbuf_findFname - Find the field name given the field index * -------------------------------- */ -char * -pbuf_findFname(GroupBuffer *group, - int field_number) +char * +pbuf_findFname(GroupBuffer * group, + int field_number) { - pbuf_checkFnumber(group, field_number); - return - (group->types[field_number]).name; + pbuf_checkFnumber(group, field_number); + return + (group->types[field_number]).name; } - diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index cfa0aebc521..6a7df5771ac 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -1,37 +1,37 @@ /*------------------------------------------------------------------------- * * pqcomm.c-- - * Communication functions between the Frontend and the Backend + * Communication functions between the Frontend and the Backend * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.19 1997/08/12 22:52:58 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.20 1997/09/07 04:42:25 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * pq_gettty - return the name of the tty in the given buffer - * pq_getport - return the PGPORT setting - * pq_close - close input / output connections - * pq_flush - flush pending output - * pq_getstr - get a null terminated string from connection - * pq_getnchar - get n characters from connection - * pq_getint - get an integer from connection - * pq_putstr - send a null terminated string to connection - * pq_putnchar - send n characters to connection - * pq_putint - send an integer to connection - * pq_getinaddr - initialize address from host and port number - * pq_getinserv - initialize address from host and service name - * pq_connect - create remote input / output connection - * pq_accept - accept remote input / output connection - * pq_async_notify - receive notification from backend. + * pq_gettty - return the name of the tty in the given buffer + * pq_getport - return the PGPORT setting + * pq_close - close input / output connections + * pq_flush - flush pending output + * pq_getstr - get a null terminated string from connection + * pq_getnchar - get n characters from connection + * pq_getint - get an integer from connection + * pq_putstr - send a null terminated string to connection + * pq_putnchar - send n characters to connection + * pq_putint - send an integer to connection + * pq_getinaddr - initialize address from host and port number + * pq_getinserv - initialize address from host and service name + * pq_connect - create remote input / output connection + * pq_accept - accept remote input / output connection + * pq_async_notify - receive notification from backend. * * NOTES - * These functions are used by both frontend applications and - * the postgres backend. + * These functions are used by both frontend applications and + * the postgres backend. * */ #include <stdio.h> @@ -39,7 +39,7 @@ #include <signal.h> #include <errno.h> #include <fcntl.h> -#include <unistd.h> /* for ttyname() */ +#include <unistd.h> /* for ttyname() */ #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> @@ -49,208 +49,217 @@ #if defined(linux) #ifndef SOMAXCONN -#define SOMAXCONN 5 /* from Linux listen(2) man page */ -#endif /* SOMAXCONN */ -#endif /* linux */ +#define SOMAXCONN 5 /* from Linux listen(2) man page */ +#endif /* SOMAXCONN */ +#endif /* linux */ #include <postgres.h> #include <libpq/pqsignal.h> #include <libpq/auth.h> -#include <libpq/libpq.h> /* where the declarations go */ +#include <libpq/libpq.h> /* where the declarations go */ /* ---------------- - * declarations + * declarations * ---------------- */ -FILE *Pfout, *Pfin; -FILE *Pfdebug; /* debugging libpq */ -int PQAsyncNotifyWaiting; /* for async. notification */ +FILE *Pfout, + *Pfin; +FILE *Pfdebug; /* debugging libpq */ +int PQAsyncNotifyWaiting; /* for async. notification */ /* -------------------------------- - * pq_init - open portal file descriptors + * pq_init - open portal file descriptors * -------------------------------- */ void pq_init(int fd) { - Pfin = fdopen(fd, "r"); - Pfout = fdopen(dup(fd), "w"); - if (!Pfin || !Pfout) - elog(FATAL, "pq_init: Couldn't initialize socket connection"); - PQnotifies_init(); - if (getenv("LIBPQ_DEBUG")) { - Pfdebug = stderr; - }else { - Pfdebug = NULL; - } + Pfin = fdopen(fd, "r"); + Pfout = fdopen(dup(fd), "w"); + if (!Pfin || !Pfout) + elog(FATAL, "pq_init: Couldn't initialize socket connection"); + PQnotifies_init(); + if (getenv("LIBPQ_DEBUG")) + { + Pfdebug = stderr; + } + else + { + Pfdebug = NULL; + } } /* ------------------------- - * pq_getc(File* fin) - * - * get a character from the input file, + * pq_getc(File* fin) + * + * get a character from the input file, * - * if Pfdebug is set, also echo the character fetched into Pfdebug + * if Pfdebug is set, also echo the character fetched into Pfdebug * - * used for debugging libpq + * used for debugging libpq */ static int -pq_getc(FILE* fin) +pq_getc(FILE * fin) { - int c; + int c; - c = getc(fin); - if (Pfdebug && c != EOF) - putc(c,Pfdebug); - return c; + c = getc(fin); + if (Pfdebug && c != EOF) + putc(c, Pfdebug); + return c; } /* -------------------------------- - * pq_gettty - return the name of the tty in the given buffer + * pq_gettty - return the name of the tty in the given buffer * -------------------------------- */ void pq_gettty(char *tp) -{ - strncpy(tp, ttyname(0), 19); +{ + strncpy(tp, ttyname(0), 19); } /* -------------------------------- - * pq_getport - return the PGPORT setting + * pq_getport - return the PGPORT setting * -------------------------------- */ int pq_getport() { - char *envport = getenv("PGPORT"); - - if (envport) - return(atoi(envport)); - return(atoi(DEF_PGPORT)); + char *envport = getenv("PGPORT"); + + if (envport) + return (atoi(envport)); + return (atoi(DEF_PGPORT)); } /* -------------------------------- - * pq_close - close input / output connections + * pq_close - close input / output connections * -------------------------------- */ void pq_close() { - if (Pfin) { - fclose(Pfin); - Pfin = NULL; - } - if (Pfout) { - fclose(Pfout); - Pfout = NULL; - } - PQAsyncNotifyWaiting = 0; - PQnotifies_init(); - pq_unregoob(); + if (Pfin) + { + fclose(Pfin); + Pfin = NULL; + } + if (Pfout) + { + fclose(Pfout); + Pfout = NULL; + } + PQAsyncNotifyWaiting = 0; + PQnotifies_init(); + pq_unregoob(); } /* -------------------------------- - * pq_flush - flush pending output + * pq_flush - flush pending output * -------------------------------- */ void pq_flush() { - if (Pfout) - fflush(Pfout); + if (Pfout) + fflush(Pfout); } /* -------------------------------- - * pq_getstr - get a null terminated string from connection + * pq_getstr - get a null terminated string from connection * -------------------------------- */ int pq_getstr(char *s, int maxlen) { - int c = '\0'; - - if (Pfin == (FILE *) NULL) { -/* elog(DEBUG, "Input descriptor is null"); */ - return(EOF); - } - - while (maxlen-- && (c = pq_getc(Pfin)) != EOF && c) - *s++ = c; - *s = '\0'; - - /* ----------------- - * If EOF reached let caller know. - * (This will only happen if we hit EOF before the string - * delimiter is reached.) - * ----------------- - */ - if (c == EOF) - return(EOF); - return(!EOF); + int c = '\0'; + + if (Pfin == (FILE *) NULL) + { +/* elog(DEBUG, "Input descriptor is null"); */ + return (EOF); + } + + while (maxlen-- && (c = pq_getc(Pfin)) != EOF && c) + *s++ = c; + *s = '\0'; + + /* ----------------- + * If EOF reached let caller know. + * (This will only happen if we hit EOF before the string + * delimiter is reached.) + * ----------------- + */ + if (c == EOF) + return (EOF); + return (!EOF); } /* * USER FUNCTION - gets a newline-terminated string from the backend. - * + * * Chiefly here so that applications can use "COPY <rel> to stdout" - * and read the output string. Returns a null-terminated string in s. + * and read the output string. Returns a null-terminated string in s. * * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips * the terminating \n (like gets(3)). * * RETURNS: - * EOF if it is detected or invalid arguments are given - * 0 if EOL is reached (i.e., \n has been read) - * (this is required for backward-compatibility -- this - * routine used to always return EOF or 0, assuming that - * the line ended within maxlen bytes.) - * 1 in other cases + * EOF if it is detected or invalid arguments are given + * 0 if EOL is reached (i.e., \n has been read) + * (this is required for backward-compatibility -- this + * routine used to always return EOF or 0, assuming that + * the line ended within maxlen bytes.) + * 1 in other cases */ -int PQgetline(char *s, int maxlen) +int +PQgetline(char *s, int maxlen) +{ + if (!Pfin || !s || maxlen <= 1) + return (EOF); + + if (fgets(s, maxlen - 1, Pfin) == NULL) { - if (!Pfin || !s || maxlen <= 1) - return(EOF); - - if(fgets(s, maxlen - 1, Pfin) == NULL) - { - return feof(Pfin) ? EOF : 1; - } - else - { - for( ; *s; s++) - { - if(*s == '\n') - { - *s = '\0'; - break; - } - } - } - - return 0; - } + return feof(Pfin) ? EOF : 1; + } + else + { + for (; *s; s++) + { + if (*s == '\n') + { + *s = '\0'; + break; + } + } + } + + return 0; +} /* * USER FUNCTION - sends a string to the backend. - * + * * Chiefly here so that applications can use "COPY <rel> from stdin". * * RETURNS: - * 0 in all cases. + * 0 in all cases. */ int PQputline(char *s) { - if (Pfout) { - fputs(s, Pfout); - fflush(Pfout); - } - return(0); + if (Pfout) + { + fputs(s, Pfout); + fflush(Pfout); + } + return (0); } /* -------------------------------- - * pq_getnchar - get n characters from connection + * pq_getnchar - get n characters from connection * -------------------------------- */ int @@ -259,228 +268,239 @@ pq_getnchar(char *s, int off, int maxlen) return pqGetNBytes(s + off, maxlen, Pfin); #if 0 - int c = '\0'; - - if (Pfin == (FILE *) NULL) { -/* elog(DEBUG, "Input descriptor is null"); */ - return(EOF); - } - - s += off; - while (maxlen-- && (c = pq_getc(Pfin)) != EOF) - *s++ = c; - - /* ----------------- - * If EOF reached let caller know - * ----------------- - */ - if (c == EOF) - return(EOF); - return(!EOF); + int c = '\0'; + + if (Pfin == (FILE *) NULL) + { +/* elog(DEBUG, "Input descriptor is null"); */ + return (EOF); + } + + s += off; + while (maxlen-- && (c = pq_getc(Pfin)) != EOF) + *s++ = c; + + /* ----------------- + * If EOF reached let caller know + * ----------------- + */ + if (c == EOF) + return (EOF); + return (!EOF); #endif } /* -------------------------------- - * pq_getint - get an integer from connection - * we receive an integer a byte at a type and reconstruct it so that - * machines with different ENDIAN representations can talk to each - * other + * pq_getint - get an integer from connection + * we receive an integer a byte at a type and reconstruct it so that + * machines with different ENDIAN representations can talk to each + * other * -------------------------------- */ int pq_getint(int b) { - int n, status = 1; - - if(!Pfin) - return EOF; - /* mjl: Seems inconsisten w/ return value of pq_putint (void). Also, - EOF is a valid return value for an int! XXX */ - - switch(b) - { - case 1: - status = ((n = fgetc(Pfin)) == EOF); - break; - case 2: - status = pqGetShort(&n, Pfin); - break; - case 4: - status = pqGetLong(&n, Pfin); - break; - default: - fprintf(stderr, "** Unsupported size %d\n", b); - } - - if(status) - { - sprintf(PQerrormsg, - "FATAL: pq_getint failed: errno=%d\n", errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - n = 0; - } - - return n; + int n, + status = 1; + + if (!Pfin) + return EOF; + + /* + * mjl: Seems inconsisten w/ return value of pq_putint (void). Also, + * EOF is a valid return value for an int! XXX + */ + + switch (b) + { + case 1: + status = ((n = fgetc(Pfin)) == EOF); + break; + case 2: + status = pqGetShort(&n, Pfin); + break; + case 4: + status = pqGetLong(&n, Pfin); + break; + default: + fprintf(stderr, "** Unsupported size %d\n", b); + } + + if (status) + { + sprintf(PQerrormsg, + "FATAL: pq_getint failed: errno=%d\n", errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + n = 0; + } + + return n; } /* -------------------------------- - * pq_putstr - send a null terminated string to connection + * pq_putstr - send a null terminated string to connection * -------------------------------- */ void pq_putstr(char *s) { - if(pqPutString(s, Pfout)) - { - sprintf(PQerrormsg, - "FATAL: pq_putstr: fputs() failed: errno=%d\n", errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); + if (pqPutString(s, Pfout)) + { + sprintf(PQerrormsg, + "FATAL: pq_putstr: fputs() failed: errno=%d\n", errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); } } /* -------------------------------- - * pq_putnchar - send n characters to connection + * pq_putnchar - send n characters to connection * -------------------------------- */ void pq_putnchar(char *s, int n) +{ + if (pqPutNBytes(s, n, Pfout)) { - if(pqPutNBytes(s, n, Pfout)) - { sprintf(PQerrormsg, - "FATAL: pq_putnchar: fputc() failed: errno=%d\n", - errno); + "FATAL: pq_putnchar: fputc() failed: errno=%d\n", + errno); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); - } } +} /* -------------------------------- - * pq_putint - send an integer to connection - * we chop an integer into bytes and send individual bytes - * machines with different ENDIAN representations can still talk to each - * other + * pq_putint - send an integer to connection + * we chop an integer into bytes and send individual bytes + * machines with different ENDIAN representations can still talk to each + * other * -------------------------------- */ void pq_putint(int i, int b) { - int status; - - if(!Pfout) return; - - status = 1; - switch(b) - { - case 1: - status = (fputc(i, Pfout) == EOF); - break; - case 2: - status = pqPutShort(i, Pfout); - break; - case 4: - status = pqPutLong(i, Pfout); - break; - default: - fprintf(stderr, "** Unsupported size %d\n", b); - } - - if(status) - { + int status; + + if (!Pfout) + return; + + status = 1; + switch (b) + { + case 1: + status = (fputc(i, Pfout) == EOF); + break; + case 2: + status = pqPutShort(i, Pfout); + break; + case 4: + status = pqPutLong(i, Pfout); + break; + default: + fprintf(stderr, "** Unsupported size %d\n", b); + } + + if (status) + { sprintf(PQerrormsg, - "FATAL: pq_putint failed: errno=%d\n", errno); + "FATAL: pq_putint failed: errno=%d\n", errno); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); - } + } } /* --- - * pq_sendoob - send a string over the out-of-band channel - * pq_recvoob - receive a string over the oob channel - * NB: Fortunately, the out-of-band channel doesn't conflict with - * buffered I/O because it is separate from regular com. channel. + * pq_sendoob - send a string over the out-of-band channel + * pq_recvoob - receive a string over the oob channel + * NB: Fortunately, the out-of-band channel doesn't conflict with + * buffered I/O because it is separate from regular com. channel. * --- */ int pq_sendoob(char *msg, int len) { - int fd = fileno(Pfout); - - return(send(fd,msg,len,MSG_OOB)); + int fd = fileno(Pfout); + + return (send(fd, msg, len, MSG_OOB)); } int pq_recvoob(char *msgPtr, int *lenPtr) { - int fd = fileno(Pfout); - int len = 0; - - len = recv(fd,msgPtr+len,*lenPtr,MSG_OOB); - *lenPtr = len; - return(len); + int fd = fileno(Pfout); + int len = 0; + + len = recv(fd, msgPtr + len, *lenPtr, MSG_OOB); + *lenPtr = len; + return (len); } /* -------------------------------- - * pq_getinaddr - initialize address from host and port number + * pq_getinaddr - initialize address from host and port number * -------------------------------- */ int -pq_getinaddr(struct sockaddr_in *sin, - char *host, - int port) +pq_getinaddr(struct sockaddr_in * sin, + char *host, + int port) { - struct hostent *hs; - - memset((char *) sin, 0, sizeof(*sin)); - - if (host) { - if (*host >= '0' && *host <= '9') - sin->sin_addr.s_addr = inet_addr(host); - else { - if (!(hs = gethostbyname(host))) { - perror(host); - return(1); - } - if (hs->h_addrtype != AF_INET) { - sprintf(PQerrormsg, - "FATAL: pq_getinaddr: %s not on Internet\n", - host); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(1); - } - memmove((char *) &sin->sin_addr, - hs->h_addr, - hs->h_length); + struct hostent *hs; + + memset((char *) sin, 0, sizeof(*sin)); + + if (host) + { + if (*host >= '0' && *host <= '9') + sin->sin_addr.s_addr = inet_addr(host); + else + { + if (!(hs = gethostbyname(host))) + { + perror(host); + return (1); + } + if (hs->h_addrtype != AF_INET) + { + sprintf(PQerrormsg, + "FATAL: pq_getinaddr: %s not on Internet\n", + host); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (1); + } + memmove((char *) &sin->sin_addr, + hs->h_addr, + hs->h_length); + } } - } - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - return(0); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + return (0); } /* -------------------------------- - * pq_getinserv - initialize address from host and servive name + * pq_getinserv - initialize address from host and servive name * -------------------------------- */ int -pq_getinserv(struct sockaddr_in *sin, char *host, char *serv) +pq_getinserv(struct sockaddr_in * sin, char *host, char *serv) { - struct servent *ss; - - if (*serv >= '0' && *serv <= '9') - return(pq_getinaddr(sin, host, atoi(serv))); - if (!(ss = getservbyname(serv, NULL))) { - sprintf(PQerrormsg, - "FATAL: pq_getinserv: unknown service: %s\n", - serv); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(1); - } - return(pq_getinaddr(sin, host, ntohs(ss->s_port))); + struct servent *ss; + + if (*serv >= '0' && *serv <= '9') + return (pq_getinaddr(sin, host, atoi(serv))); + if (!(ss = getservbyname(serv, NULL))) + { + sprintf(PQerrormsg, + "FATAL: pq_getinserv: unknown service: %s\n", + serv); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (1); + } + return (pq_getinaddr(sin, host, ntohs(ss->s_port))); } /* @@ -488,279 +508,297 @@ pq_getinserv(struct sockaddr_in *sin, char *host, char *serv) * This is used for receiving async. notification from the backend. */ void -pq_regoob(void (*fptr)()) +pq_regoob(void (*fptr) ()) { - int fd = fileno(Pfout); + int fd = fileno(Pfout); + #if defined(hpux) - ioctl(fd, FIOSSAIOOWN, getpid()); + ioctl(fd, FIOSSAIOOWN, getpid()); #elif defined(sco) - ioctl(fd, SIOCSPGRP, getpid()); + ioctl(fd, SIOCSPGRP, getpid()); #else - fcntl(fd, F_SETOWN, getpid()); -#endif /* hpux */ - pqsignal(SIGURG,fptr); + fcntl(fd, F_SETOWN, getpid()); +#endif /* hpux */ + pqsignal(SIGURG, fptr); } void pq_unregoob() { - pqsignal(SIGURG,SIG_DFL); + pqsignal(SIGURG, SIG_DFL); } void pq_async_notify() { - char msg[20]; - /* int len = sizeof(msg);*/ - int len = 20; - - if (pq_recvoob(msg,&len) >= 0) { - /* debugging */ - printf("received notification: %s\n",msg); - PQAsyncNotifyWaiting = 1; - /* PQappendNotify(msg+1);*/ - } else { - extern int errno; - printf("SIGURG but no data: len = %d, err=%d\n",len,errno); - } + char msg[20]; + + /* int len = sizeof(msg); */ + int len = 20; + + if (pq_recvoob(msg, &len) >= 0) + { + /* debugging */ + printf("received notification: %s\n", msg); + PQAsyncNotifyWaiting = 1; + /* PQappendNotify(msg+1); */ + } + else + { + extern int errno; + + printf("SIGURG but no data: len = %d, err=%d\n", len, errno); + } } /* * Streams -- wrapper around Unix socket system calls * * - * Stream functions are used for vanilla TCP connection protocol. + * Stream functions are used for vanilla TCP connection protocol. */ /* * StreamServerPort -- open a sock stream "listening" port. * * This initializes the Postmaster's connection - * accepting port. + * accepting port. * * ASSUME: that this doesn't need to be non-blocking because - * the Postmaster uses select() to tell when the socket - * is ready. + * the Postmaster uses select() to tell when the socket + * is ready. * * RETURNS: STATUS_OK or STATUS_ERROR */ int StreamServerPort(char *hostName, short portName, int *fdP) { - struct sockaddr_in sin; - int fd; - int one = 1; - - - if (! hostName) - hostName = "localhost"; - - memset((char *)&sin, 0, sizeof sin); - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - sprintf(PQerrormsg, - "FATAL: StreamServerPort: socket() failed: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - - if((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, - sizeof(one))) == -1) { - sprintf(PQerrormsg, - "FATAL: StreamServerPort: setsockopt (SO_REUSEADDR) failed: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - - sin.sin_family = AF_INET; - sin.sin_port = htons(portName); - - if (bind(fd, (struct sockaddr *)&sin, sizeof sin) < 0) { - sprintf(PQerrormsg, - "FATAL: StreamServerPort: bind() failed: errno=%d\n", - errno); - pqdebug("%s", PQerrormsg); - strcat(PQerrormsg, "\tIs another postmaster already running on that port?\n"); - strcat(PQerrormsg, "\tIf not, wait a few seconds and retry.\n"); - fputs(PQerrormsg, stderr); - return(STATUS_ERROR); - } - - listen(fd, SOMAXCONN); - - /* MS: I took this code from Dillon's version. It makes the - * listening port non-blocking. That is not necessary (and - * may tickle kernel bugs). - - fcntl(fd, F_SETFD, 1); - fcntl(fd, F_SETFL, FNDELAY); - */ - - *fdP = fd; - return(STATUS_OK); + struct sockaddr_in sin; + int fd; + int one = 1; + + + if (!hostName) + hostName = "localhost"; + + memset((char *) &sin, 0, sizeof sin); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + sprintf(PQerrormsg, + "FATAL: StreamServerPort: socket() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + + if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, + sizeof(one))) == -1) + { + sprintf(PQerrormsg, + "FATAL: StreamServerPort: setsockopt (SO_REUSEADDR) failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(portName); + + if (bind(fd, (struct sockaddr *) & sin, sizeof sin) < 0) + { + sprintf(PQerrormsg, + "FATAL: StreamServerPort: bind() failed: errno=%d\n", + errno); + pqdebug("%s", PQerrormsg); + strcat(PQerrormsg, "\tIs another postmaster already running on that port?\n"); + strcat(PQerrormsg, "\tIf not, wait a few seconds and retry.\n"); + fputs(PQerrormsg, stderr); + return (STATUS_ERROR); + } + + listen(fd, SOMAXCONN); + + /* + * MS: I took this code from Dillon's version. It makes the listening + * port non-blocking. That is not necessary (and may tickle kernel + * bugs). + * + * fcntl(fd, F_SETFD, 1); fcntl(fd, F_SETFL, FNDELAY); + */ + + *fdP = fd; + return (STATUS_OK); } /* * StreamConnection -- create a new connection with client using - * server port. + * server port. * * This one should be non-blocking. - * + * * RETURNS: STATUS_OK or STATUS_ERROR */ int -StreamConnection(int server_fd, Port *port) +StreamConnection(int server_fd, Port * port) { - int addrlen; - - /* accept connection (and fill in the client (remote) address) */ - addrlen = sizeof(struct sockaddr_in); - if ((port->sock = accept(server_fd, - (struct sockaddr *) &port->raddr, - &addrlen)) < 0) { - elog(WARN, "postmaster: StreamConnection: accept: %m"); - return(STATUS_ERROR); - } - - /* fill in the server (local) address */ - addrlen = sizeof(struct sockaddr_in); - if (getsockname(port->sock, (struct sockaddr *) &port->laddr, - &addrlen) < 0) { - elog(WARN, "postmaster: StreamConnection: getsockname: %m"); - return(STATUS_ERROR); - } - { - struct protoent *pe; - int on=1; - - pe = getprotobyname ("TCP"); - if ( pe == NULL ) - { - elog(WARN, "postmaster: getprotobyname failed"); - return(STATUS_ERROR); + int addrlen; + + /* accept connection (and fill in the client (remote) address) */ + addrlen = sizeof(struct sockaddr_in); + if ((port->sock = accept(server_fd, + (struct sockaddr *) & port->raddr, + &addrlen)) < 0) + { + elog(WARN, "postmaster: StreamConnection: accept: %m"); + return (STATUS_ERROR); + } + + /* fill in the server (local) address */ + addrlen = sizeof(struct sockaddr_in); + if (getsockname(port->sock, (struct sockaddr *) & port->laddr, + &addrlen) < 0) + { + elog(WARN, "postmaster: StreamConnection: getsockname: %m"); + return (STATUS_ERROR); } - if ( setsockopt (port->sock, pe->p_proto, TCP_NODELAY, - &on, sizeof (on)) < 0 ) - { - elog(WARN, "postmaster: setsockopt failed"); - return(STATUS_ERROR); + { + struct protoent *pe; + int on = 1; + + pe = getprotobyname("TCP"); + if (pe == NULL) + { + elog(WARN, "postmaster: getprotobyname failed"); + return (STATUS_ERROR); + } + if (setsockopt(port->sock, pe->p_proto, TCP_NODELAY, + &on, sizeof(on)) < 0) + { + elog(WARN, "postmaster: setsockopt failed"); + return (STATUS_ERROR); + } } - } - - port->mask = 1 << port->sock; - - /* reset to non-blocking */ - fcntl(port->sock, F_SETFL, 1); - - return(STATUS_OK); + + port->mask = 1 << port->sock; + + /* reset to non-blocking */ + fcntl(port->sock, F_SETFL, 1); + + return (STATUS_OK); } -/* +/* * StreamClose -- close a client/backend connection */ void StreamClose(int sock) { - close(sock); + close(sock); } /* --------------------------- - * StreamOpen -- From client, initiate a connection with the - * server (Postmaster). + * StreamOpen -- From client, initiate a connection with the + * server (Postmaster). * * RETURNS: STATUS_OK or STATUS_ERROR * * NOTE: connection is NOT established just because this - * routine exits. Local state is ok, but we haven't - * spoken to the postmaster yet. + * routine exits. Local state is ok, but we haven't + * spoken to the postmaster yet. * --------------------------- */ int -StreamOpen(char *hostName, short portName, Port *port) +StreamOpen(char *hostName, short portName, Port * port) { - struct hostent *hp; - int laddrlen = sizeof(struct sockaddr_in); - extern int errno; - - if (!hostName) - hostName = "localhost"; - - /* set up the server (remote) address */ - if (!(hp = gethostbyname(hostName)) || hp->h_addrtype != AF_INET) { - sprintf(PQerrormsg, - "FATAL: StreamOpen: unknown hostname: %s\n", - hostName); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - memset((char *) &port->raddr, 0, sizeof(port->raddr)); - memmove((char *) &(port->raddr.sin_addr), - (char *) hp->h_addr, - hp->h_length); - port->raddr.sin_family = AF_INET; - port->raddr.sin_port = htons(portName); - - /* connect to the server */ - if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - sprintf(PQerrormsg, - "FATAL: StreamOpen: socket() failed: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - if (connect(port->sock, (struct sockaddr *)&port->raddr, - sizeof(port->raddr)) < 0) { - sprintf(PQerrormsg, - "FATAL: StreamOpen: connect() failed: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - - /* fill in the client address */ - if (getsockname(port->sock, (struct sockaddr *) &port->laddr, - &laddrlen) < 0) { - sprintf(PQerrormsg, - "FATAL: StreamOpen: getsockname() failed: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return(STATUS_ERROR); - } - - return(STATUS_OK); + struct hostent *hp; + int laddrlen = sizeof(struct sockaddr_in); + extern int errno; + + if (!hostName) + hostName = "localhost"; + + /* set up the server (remote) address */ + if (!(hp = gethostbyname(hostName)) || hp->h_addrtype != AF_INET) + { + sprintf(PQerrormsg, + "FATAL: StreamOpen: unknown hostname: %s\n", + hostName); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + memset((char *) &port->raddr, 0, sizeof(port->raddr)); + memmove((char *) &(port->raddr.sin_addr), + (char *) hp->h_addr, + hp->h_length); + port->raddr.sin_family = AF_INET; + port->raddr.sin_port = htons(portName); + + /* connect to the server */ + if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + sprintf(PQerrormsg, + "FATAL: StreamOpen: socket() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + if (connect(port->sock, (struct sockaddr *) & port->raddr, + sizeof(port->raddr)) < 0) + { + sprintf(PQerrormsg, + "FATAL: StreamOpen: connect() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + + /* fill in the client address */ + if (getsockname(port->sock, (struct sockaddr *) & port->laddr, + &laddrlen) < 0) + { + sprintf(PQerrormsg, + "FATAL: StreamOpen: getsockname() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return (STATUS_ERROR); + } + + return (STATUS_OK); } -static char *authentication_type_name[] = { - 0, 0, 0, 0, 0, 0, 0, - "the default authentication type", - 0, 0, - "Kerberos v4", - "Kerberos v5", - "host-based authentication", - "unauthenication", - "plaintext password authentication" +static char *authentication_type_name[] = { + 0, 0, 0, 0, 0, 0, 0, + "the default authentication type", + 0, 0, + "Kerberos v4", + "Kerberos v5", + "host-based authentication", + "unauthenication", + "plaintext password authentication" }; -char *name_of_authentication_type(int type) +char * +name_of_authentication_type(int type) { - char *result = 0; + char *result = 0; - if(type >= 1 && type <= LAST_AUTHENTICATION_TYPE) { - result = authentication_type_name[type]; - } + if (type >= 1 && type <= LAST_AUTHENTICATION_TYPE) + { + result = authentication_type_name[type]; + } - if(result == 0) { - result = "<unknown authentication type>"; - } + if (result == 0) + { + result = "<unknown authentication type>"; + } - return result; + return result; } diff --git a/src/backend/libpq/pqcomprim.c b/src/backend/libpq/pqcomprim.c index 3501b63c7b6..8675205d784 100644 --- a/src/backend/libpq/pqcomprim.c +++ b/src/backend/libpq/pqcomprim.c @@ -4,171 +4,185 @@ #include "postgres.h" #include "libpq/pqcomm.h" -#ifdef HAVE_ENDIAN_H -# include <endian.h> +#ifdef HAVE_ENDIAN_H +#include <endian.h> #endif /* --------------------------------------------------------------------- */ /* These definitions for ntoh/hton are the other way around from the - * default system definitions, so we roll our own here. + * default system definitions, so we roll our own here. */ -#ifndef BYTE_ORDER +#ifndef BYTE_ORDER #error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN #endif #if BYTE_ORDER == LITTLE_ENDIAN -# define ntoh_s(n) n -# define ntoh_l(n) n -# define hton_s(n) n -# define hton_l(n) n -#else /* BYTE_ORDER != LITTLE_ENDIAN */ -# if BYTE_ORDER == BIG_ENDIAN -# define ntoh_s(n) (u_short)(((u_char *)&n)[1] << 8 \ - | ((u_char *)&n)[0]) -# define ntoh_l(n) (u_long) (((u_char *)&n)[3] << 24 \ - | ((u_char *)&n)[2] << 16 \ - | ((u_char *)&n)[1] << 8 \ - | ((u_char *)&n)[0]) -# define hton_s(n) (ntoh_s(n)) -# define hton_l(n) (ntoh_l(n)) -# else /* BYTE_ORDER != BIG_ENDIAN */ -# if BYTE_ORDER == PDP_ENDIAN -# error PDP_ENDIAN macros not written yet -# else /* BYTE_ORDER != anything known */ -# error BYTE_ORDER not defined as anything understood -# endif /* BYTE_ORDER == PDP_ENDIAN */ -# endif /* BYTE_ORDER == BIG_ENDIAN */ -#endif /* BYTE_ORDER == LITTLE_ENDIAN */ +#define ntoh_s(n) n +#define ntoh_l(n) n +#define hton_s(n) n +#define hton_l(n) n +#else /* BYTE_ORDER != LITTLE_ENDIAN */ +#if BYTE_ORDER == BIG_ENDIAN +#define ntoh_s(n) (u_short)(((u_char *)&n)[1] << 8 \ + | ((u_char *)&n)[0]) +#define ntoh_l(n) (u_long) (((u_char *)&n)[3] << 24 \ + | ((u_char *)&n)[2] << 16 \ + | ((u_char *)&n)[1] << 8 \ + | ((u_char *)&n)[0]) +#define hton_s(n) (ntoh_s(n)) +#define hton_l(n) (ntoh_l(n)) +#else +/* BYTE_ORDER != BIG_ENDIAN */ +#if BYTE_ORDER == PDP_ENDIAN +#error PDP_ENDIAN macros not written yet +#else +/* BYTE_ORDER != anything known */ +#error BYTE_ORDER not defined as anything understood +#endif /* BYTE_ORDER == PDP_ENDIAN */ +#endif /* BYTE_ORDER == BIG_ENDIAN */ +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ /* --------------------------------------------------------------------- */ -int pqPutShort(int integer, FILE *f) - { - int retval = 0; - u_short n,s; - - s = integer; - n = hton_s(s); - if(fwrite(&n, sizeof(u_short), 1, f) != 1) - retval = EOF; - - return retval; - } +int +pqPutShort(int integer, FILE * f) +{ + int retval = 0; + u_short n, + s; + + s = integer; + n = hton_s(s); + if (fwrite(&n, sizeof(u_short), 1, f) != 1) + retval = EOF; + + return retval; +} /* --------------------------------------------------------------------- */ -int pqPutLong(int integer, FILE *f) - { - int retval = 0; - u_long n; - - n = hton_l(integer); - if(fwrite(&n, sizeof(u_long), 1, f) != 1) - retval = EOF; - - return retval; - } - +int +pqPutLong(int integer, FILE * f) +{ + int retval = 0; + u_long n; + + n = hton_l(integer); + if (fwrite(&n, sizeof(u_long), 1, f) != 1) + retval = EOF; + + return retval; +} + /* --------------------------------------------------------------------- */ -int pqGetShort(int *result, FILE *f) - { - int retval = 0; - u_short n; - - if(fread(&n, sizeof(u_short), 1, f) != 1) - retval = EOF; - - *result = ntoh_s(n); - return retval; - } +int +pqGetShort(int *result, FILE * f) +{ + int retval = 0; + u_short n; + + if (fread(&n, sizeof(u_short), 1, f) != 1) + retval = EOF; + + *result = ntoh_s(n); + return retval; +} /* --------------------------------------------------------------------- */ -int pqGetLong(int *result, FILE *f) - { - int retval = 0; - u_long n; - - if(fread(&n, sizeof(u_long), 1, f) != 1) - retval = EOF; - - *result = ntoh_l(n); - return retval; - } +int +pqGetLong(int *result, FILE * f) +{ + int retval = 0; + u_long n; + + if (fread(&n, sizeof(u_long), 1, f) != 1) + retval = EOF; + + *result = ntoh_l(n); + return retval; +} /* --------------------------------------------------------------------- */ /* pqGetNBytes: Read a chunk of exactly len bytes in buffer s. - Return 0 if ok. + Return 0 if ok. */ -int pqGetNBytes(char *s, size_t len, FILE *f) - { - int cnt; +int +pqGetNBytes(char *s, size_t len, FILE * f) +{ + int cnt; if (f == NULL) return EOF; - + cnt = fread(s, 1, len, f); s[cnt] = '\0'; - /* mjl: actually needs up to len+1 bytes, is this okay? XXX */ + /* mjl: actually needs up to len+1 bytes, is this okay? XXX */ return (cnt == len) ? 0 : EOF; - } +} /* --------------------------------------------------------------------- */ -int pqPutNBytes(const char *s, size_t len, FILE *f) - { +int +pqPutNBytes(const char *s, size_t len, FILE * f) +{ if (f == NULL) return 0; - if(fwrite(s, 1, len, f) != len) - return EOF; + if (fwrite(s, 1, len, f) != len) + return EOF; return 0; - } - +} + /* --------------------------------------------------------------------- */ -int pqGetString(char *s, size_t len, FILE *f) - { - int c; +int +pqGetString(char *s, size_t len, FILE * f) +{ + int c; if (f == NULL) - return EOF; - + return EOF; + while (len-- && (c = getc(f)) != EOF && c) *s++ = c; *s = '\0'; - /* mjl: actually needs up to len+1 bytes, is this okay? XXX */ + /* mjl: actually needs up to len+1 bytes, is this okay? XXX */ return 0; - } +} /* --------------------------------------------------------------------- */ -int pqPutString(const char *s, FILE *f) - { +int +pqPutString(const char *s, FILE * f) +{ if (f == NULL) return 0; - + if (fputs(s, f) == EOF) return EOF; - fputc('\0', f); /* important to send an ending \0 since backend expects it */ + fputc('\0', f); /* important to send an ending \0 since + * backend expects it */ fflush(f); return 0; - } +} /* --------------------------------------------------------------------- */ -int pqGetByte(FILE *f) - { +int +pqGetByte(FILE * f) +{ return getc(f); - } - +} + /* --------------------------------------------------------------------- */ -int pqPutByte(int c, FILE *f) - { - if(!f) return 0; - +int +pqPutByte(int c, FILE * f) +{ + if (!f) + return 0; + return (putc(c, f) == c) ? 0 : EOF; - } - -/* --------------------------------------------------------------------- */ +} +/* --------------------------------------------------------------------- */ diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c index 6f67ddc5f5b..9f56556537f 100644 --- a/src/backend/libpq/pqpacket.c +++ b/src/backend/libpq/pqpacket.c @@ -1,39 +1,39 @@ /*------------------------------------------------------------------------- * * pqpacket.c-- - * routines for reading and writing data packets sent/received by - * POSTGRES clients and servers + * routines for reading and writing data packets sent/received by + * POSTGRES clients and servers * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.5 1997/08/12 22:53:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.6 1997/09/07 04:42:28 momjian Exp $ * *------------------------------------------------------------------------- */ /* NOTES - * This is the module that understands the lowest-level part - * of the communication protocol. All of the trickiness in - * this module is for making sure that non-blocking I/O in - * the Postmaster works correctly. Check the notes in PacketRecv - * on non-blocking I/O. + * This is the module that understands the lowest-level part + * of the communication protocol. All of the trickiness in + * this module is for making sure that non-blocking I/O in + * the Postmaster works correctly. Check the notes in PacketRecv + * on non-blocking I/O. * * Data Structures: - * Port has two important functions. (1) It records the - * sock/addr used in communication. (2) It holds partially - * read in messages. This is especially important when - * we haven't seen enough to construct a complete packet - * header. + * Port has two important functions. (1) It records the + * sock/addr used in communication. (2) It holds partially + * read in messages. This is especially important when + * we haven't seen enough to construct a complete packet + * header. * * PacketBuf -- None of the clients of this module should know - * what goes into a packet hdr (although they know how big - * it is). This routine is in charge of host to net order - * conversion for headers. Data conversion is someone elses - * responsibility. + * what goes into a packet hdr (although they know how big + * it is). This routine is in charge of host to net order + * conversion for headers. Data conversion is someone elses + * responsibility. * * IMPORTANT: these routines are called by backends, clients, and - * the Postmaster. + * the Postmaster. * */ #include <stdio.h> @@ -57,134 +57,156 @@ * */ int -PacketReceive(Port *port, /* receive port */ - PacketBuf *buf, /* MAX_PACKET_SIZE-worth of buffer space */ - bool nonBlocking) /* NON_BLOCKING or BLOCKING i/o */ +PacketReceive(Port * port, /* receive port */ + PacketBuf * buf, /* MAX_PACKET_SIZE-worth of buffer space */ + bool nonBlocking) /* NON_BLOCKING or BLOCKING i/o */ { - PacketLen max_size = sizeof(PacketBuf); - PacketLen cc; /* character count -- bytes recvd */ - PacketLen packetLen; /* remaining packet chars to read */ - Addr tmp; /* curr recv buf pointer */ - int addrLen = sizeof(struct sockaddr_in); - int hdrLen; - int flag; - int decr; - - hdrLen = sizeof(buf->len); - - if (nonBlocking == NON_BLOCKING) { - flag = MSG_PEEK; - decr = 0; - } else { - flag = 0; - decr = hdrLen; - } - /* - * Assume port->nBytes is zero unless we were interrupted during - * non-blocking I/O. This first recvfrom() is to get the hdr - * information so we know how many bytes to read. Life would - * be very complicated if we read too much data (buffering). - */ - tmp = ((Addr)buf) + port->nBytes; - - if (port->nBytes >= hdrLen) { - packetLen = ntohl(buf->len) - port->nBytes; - } - else { - /* peeking into the incoming message */ - cc = recvfrom(port->sock, (char *)&(buf->len), hdrLen, flag, - (struct sockaddr*) &(port->raddr), &addrLen); - if (cc < hdrLen) { - /* if cc is negative, the system call failed */ - if (cc < 0) { - return(STATUS_ERROR); - } - /* - * cc == 0 means the connection was broken at the - * other end. - */ - else if (! cc) { - return(STATUS_INVALID); - - } else { - /* - * Worst case. We didn't even read in enough data to - * get the header length. - * since we are using a data stream, - * this happens only if the client is mallicious. - * - * Don't save the number of bytes we've read so far. - * Since we only peeked at the incoming message, the - * kernel is going to keep it for us. - */ - return(STATUS_NOT_DONE); - } - } else { - /* - * This is an attempt to shield the Postmaster - * from mallicious attacks by placing tighter - * restrictions on the reported packet length. - * - * Check for negative packet length - */ - if ((buf->len) <= 0) { - return(STATUS_INVALID); - } - /* - * Check for oversize packet - */ - if ((ntohl(buf->len)) > max_size) { - return(STATUS_INVALID); - } - /* - * great. got the header. now get the true length (including - * header size). - */ - packetLen = ntohl(buf->len); - /* - * if someone is sending us junk, close the connection - */ - if (packetLen > max_size) { - port->nBytes = packetLen; - return(STATUS_BAD_PACKET); - } - packetLen -= decr; - tmp += decr - port->nBytes; + PacketLen max_size = sizeof(PacketBuf); + PacketLen cc; /* character count -- bytes recvd */ + PacketLen packetLen; /* remaining packet chars to read */ + Addr tmp; /* curr recv buf pointer */ + int addrLen = sizeof(struct sockaddr_in); + int hdrLen; + int flag; + int decr; + + hdrLen = sizeof(buf->len); + + if (nonBlocking == NON_BLOCKING) + { + flag = MSG_PEEK; + decr = 0; + } + else + { + flag = 0; + decr = hdrLen; + } + + /* + * Assume port->nBytes is zero unless we were interrupted during + * non-blocking I/O. This first recvfrom() is to get the hdr + * information so we know how many bytes to read. Life would be very + * complicated if we read too much data (buffering). + */ + tmp = ((Addr) buf) + port->nBytes; + + if (port->nBytes >= hdrLen) + { + packetLen = ntohl(buf->len) - port->nBytes; + } + else + { + /* peeking into the incoming message */ + cc = recvfrom(port->sock, (char *) &(buf->len), hdrLen, flag, + (struct sockaddr *) & (port->raddr), &addrLen); + if (cc < hdrLen) + { + /* if cc is negative, the system call failed */ + if (cc < 0) + { + return (STATUS_ERROR); + } + + /* + * cc == 0 means the connection was broken at the other end. + */ + else if (!cc) + { + return (STATUS_INVALID); + + } + else + { + + /* + * Worst case. We didn't even read in enough data to get + * the header length. since we are using a data stream, + * this happens only if the client is mallicious. + * + * Don't save the number of bytes we've read so far. Since we + * only peeked at the incoming message, the kernel is + * going to keep it for us. + */ + return (STATUS_NOT_DONE); + } + } + else + { + + /* + * This is an attempt to shield the Postmaster from mallicious + * attacks by placing tighter restrictions on the reported + * packet length. + * + * Check for negative packet length + */ + if ((buf->len) <= 0) + { + return (STATUS_INVALID); + } + + /* + * Check for oversize packet + */ + if ((ntohl(buf->len)) > max_size) + { + return (STATUS_INVALID); + } + + /* + * great. got the header. now get the true length (including + * header size). + */ + packetLen = ntohl(buf->len); + + /* + * if someone is sending us junk, close the connection + */ + if (packetLen > max_size) + { + port->nBytes = packetLen; + return (STATUS_BAD_PACKET); + } + packetLen -= decr; + tmp += decr - port->nBytes; + } } - } - - /* - * Now that we know how big it is, read the packet. We read - * the entire packet, since the last call was just a peek. - */ - while (packetLen) { - cc = recvfrom(port->sock, tmp, packetLen, 0, - (struct sockaddr*) &(port->raddr), &addrLen); - if (cc < 0) - return(STATUS_ERROR); - /* - * cc == 0 means the connection was broken at the - * other end. + + /* + * Now that we know how big it is, read the packet. We read the + * entire packet, since the last call was just a peek. */ - else if (! cc) - return(STATUS_INVALID); - + while (packetLen) + { + cc = recvfrom(port->sock, tmp, packetLen, 0, + (struct sockaddr *) & (port->raddr), &addrLen); + if (cc < 0) + return (STATUS_ERROR); + + /* + * cc == 0 means the connection was broken at the other end. + */ + else if (!cc) + return (STATUS_INVALID); + /* fprintf(stderr,"expected packet of %d bytes, got %d bytes\n", - packetLen, cc); + packetLen, cc); */ - tmp += cc; - packetLen -= cc; - - /* if non-blocking, we're done. */ - if (nonBlocking && packetLen) { - port->nBytes += cc; - return(STATUS_NOT_DONE); + tmp += cc; + packetLen -= cc; + + /* if non-blocking, we're done. */ + if (nonBlocking && packetLen) + { + port->nBytes += cc; + return (STATUS_NOT_DONE); + } } - } - - port->nBytes = 0; - return(STATUS_OK); + + port->nBytes = 0; + return (STATUS_OK); } /* @@ -192,46 +214,47 @@ PacketReceive(Port *port, /* receive port */ * * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise. * SIDE_EFFECTS: may block. - * NOTES: Non-blocking writes would significantly complicate - * buffer management. For now, we're not going to do it. + * NOTES: Non-blocking writes would significantly complicate + * buffer management. For now, we're not going to do it. * */ int -PacketSend(Port *port, - PacketBuf *buf, - PacketLen len, - bool nonBlocking) +PacketSend(Port * port, + PacketBuf * buf, + PacketLen len, + bool nonBlocking) { - PacketLen totalLen; - int addrLen = sizeof(struct sockaddr_in); - - Assert(!nonBlocking); - Assert(buf); - - totalLen = len; - - len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0, - (struct sockaddr *)&(port->raddr), addrLen); - - if (len < totalLen) { - sprintf(PQerrormsg, - "FATAL: PacketSend: couldn't send complete packet: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - return(STATUS_ERROR); - } - - return(STATUS_OK); + PacketLen totalLen; + int addrLen = sizeof(struct sockaddr_in); + + Assert(!nonBlocking); + Assert(buf); + + totalLen = len; + + len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0, + (struct sockaddr *) & (port->raddr), addrLen); + + if (len < totalLen) + { + sprintf(PQerrormsg, + "FATAL: PacketSend: couldn't send complete packet: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + return (STATUS_ERROR); + } + + return (STATUS_OK); } /* * StartupInfo2PacketBuf - - * convert the fields of the StartupInfo to a PacketBuf + * convert the fields of the StartupInfo to a PacketBuf * */ /* moved to src/libpq/fe-connect.c */ /* -PacketBuf* +PacketBuf* StartupInfo2PacketBuf(StartupInfo* s) { PacketBuf* res; @@ -259,10 +282,10 @@ StartupInfo2PacketBuf(StartupInfo* s) /* * PacketBuf2StartupInfo - - * convert the fields of the StartupInfo to a PacketBuf + * convert the fields of the StartupInfo to a PacketBuf * */ -/* moved to postmaster.c +/* moved to postmaster.c StartupInfo* PacketBuf2StartupInfo(PacketBuf* p) { diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c index 0c91f50df03..727a2a7207d 100644 --- a/src/backend/libpq/pqsignal.c +++ b/src/backend/libpq/pqsignal.c @@ -1,41 +1,41 @@ /*------------------------------------------------------------------------- * * pqsignal.c-- - * reliable BSD-style signal(2) routine stolen from RWW who stole it - * from Stevens... + * reliable BSD-style signal(2) routine stolen from RWW who stole it + * from Stevens... * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/pqsignal.c,v 1.5 1996/12/26 22:07:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/pqsignal.c,v 1.6 1997/09/07 04:42:29 momjian Exp $ * * NOTES - * This shouldn't be in libpq, but the monitor and some other - * things need it... + * This shouldn't be in libpq, but the monitor and some other + * things need it... * - * A NOTE ABOUT SIGNAL HANDLING ACROSS THE VARIOUS PLATFORMS. + * A NOTE ABOUT SIGNAL HANDLING ACROSS THE VARIOUS PLATFORMS. * - * config.h defines the macro USE_POSIX_SIGNALS for some platforms and - * not for others. This file and pqsignal.h use that macro to decide - * how to handle signalling. + * config.h defines the macro USE_POSIX_SIGNALS for some platforms and + * not for others. This file and pqsignal.h use that macro to decide + * how to handle signalling. * - * signal(2) handling - this is here because it affects some of - * the frontend commands as well as the backend server. - * - * Ultrix and SunOS provide BSD signal(2) semantics by default. - * - * SVID2 and POSIX signal(2) semantics differ from BSD signal(2) - * semantics. We can use the POSIX sigaction(2) on systems that - * allow us to request restartable signals (SA_RESTART). - * - * Some systems don't allow restartable signals at all unless we - * link to a special BSD library. - * - * We devoutly hope that there aren't any systems that provide - * neither POSIX signals nor BSD signals. The alternative - * is to do signal-handler reinstallation, which doesn't work well - * at all. + * signal(2) handling - this is here because it affects some of + * the frontend commands as well as the backend server. + * + * Ultrix and SunOS provide BSD signal(2) semantics by default. + * + * SVID2 and POSIX signal(2) semantics differ from BSD signal(2) + * semantics. We can use the POSIX sigaction(2) on systems that + * allow us to request restartable signals (SA_RESTART). + * + * Some systems don't allow restartable signals at all unless we + * link to a special BSD library. + * + * We devoutly hope that there aren't any systems that provide + * neither POSIX signals nor BSD signals. The alternative + * is to do signal-handler reinstallation, which doesn't work well + * at all. * ------------------------------------------------------------------------*/ #include <postgres.h> @@ -47,18 +47,20 @@ pqsigfunc pqsignal(int signo, pqsigfunc func) { #if !defined(USE_POSIX_SIGNALS) - return signal(signo, func); + return signal(signo, func); #else - struct sigaction act, oact; - - act.sa_handler = func; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - if (signo != SIGALRM) { - act.sa_flags |= SA_RESTART; - } - if (sigaction(signo, &act, &oact) < 0) - return(SIG_ERR); - return(oact.sa_handler); -#endif /* !USE_POSIX_SIGNALS */ + struct sigaction act, + oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (signo != SIGALRM) + { + act.sa_flags |= SA_RESTART; + } + if (sigaction(signo, &act, &oact) < 0) + return (SIG_ERR); + return (oact.sa_handler); +#endif /* !USE_POSIX_SIGNALS */ } diff --git a/src/backend/libpq/util.c b/src/backend/libpq/util.c index e8ca2e4ccc5..f4efec13f3a 100644 --- a/src/backend/libpq/util.c +++ b/src/backend/libpq/util.c @@ -1,100 +1,100 @@ /*------------------------------------------------------------------------- * * util.c-- - * general routines for libpq backend + * general routines for libpq backend * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/util.c,v 1.3 1996/11/06 08:48:33 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/util.c,v 1.4 1997/09/07 04:42:31 momjian Exp $ * *------------------------------------------------------------------------- */ /* - * UTILITY ROUTINES - * pqdebug - send a string to the debugging output port - * pqdebug2 - send two strings to stdout - * PQtrace - turn on pqdebug() tracing - * PQuntrace - turn off pqdebug() tracing + * UTILITY ROUTINES + * pqdebug - send a string to the debugging output port + * pqdebug2 - send two strings to stdout + * PQtrace - turn on pqdebug() tracing + * PQuntrace - turn off pqdebug() tracing */ -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include <string.h> #include <postgres.h> #include <lib/dllist.h> -#include <libpq/libpq.h> /* where the declarations go */ +#include <libpq/libpq.h> /* where the declarations go */ #include <utils/exc.h> /* ---------------- - * exceptions + * exceptions * ---------------- */ -Exception MemoryError = {"Memory Allocation Error"}; -Exception PortalError = {"Invalid arguments to portal functions"}; -Exception PostquelError = {"Sql Error"}; -Exception ProtocolError = {"Protocol Error"}; -char PQerrormsg[ERROR_MSG_LENGTH]; +Exception MemoryError = {"Memory Allocation Error"}; +Exception PortalError = {"Invalid arguments to portal functions"}; +Exception PostquelError = {"Sql Error"}; +Exception ProtocolError = {"Protocol Error"}; +char PQerrormsg[ERROR_MSG_LENGTH]; -int PQtracep = 0; /* 1 to print out debugging messages */ -FILE *debug_port = (FILE *) NULL; +int PQtracep = 0; /* 1 to print out debugging messages */ +FILE *debug_port = (FILE *) NULL; /* ---------------------------------------------------------------- - * PQ utility routines + * PQ utility routines * ---------------------------------------------------------------- */ void pqdebug(char *target, char *msg) { - if (!target) - return; - - if (PQtracep) { - /* - * if nothing else was suggested default to stdout - */ - if (!debug_port) - debug_port = stdout; - fprintf(debug_port, target, msg); - fprintf(debug_port, "\n"); - } + if (!target) + return; + + if (PQtracep) + { + + /* + * if nothing else was suggested default to stdout + */ + if (!debug_port) + debug_port = stdout; + fprintf(debug_port, target, msg); + fprintf(debug_port, "\n"); + } } void pqdebug2(char *target, char *msg1, char *msg2) { - if (!target) - return; - - if (PQtracep) { - /* - * if nothing else was suggested default to stdout - */ - if (!debug_port) - debug_port = stdout; - fprintf(debug_port, target, msg1, msg2); - fprintf(debug_port, "\n"); - } + if (!target) + return; + + if (PQtracep) + { + + /* + * if nothing else was suggested default to stdout + */ + if (!debug_port) + debug_port = stdout; + fprintf(debug_port, target, msg1, msg2); + fprintf(debug_port, "\n"); + } } /* -------------------------------- - * PQtrace() / PQuntrace() + * PQtrace() / PQuntrace() * -------------------------------- */ void PQtrace() { - PQtracep = 1; + PQtracep = 1; } void PQuntrace() { - PQtracep = 0; + PQtracep = 0; } - - - - diff --git a/src/backend/main/main.c b/src/backend/main/main.c index c3bb0793394..b70be2a08c4 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * main.c-- - * Stub main() routine for the postgres backend. + * Stub main() routine for the postgres backend. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.7 1997/04/24 20:30:09 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.8 1997/09/07 04:42:35 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -16,11 +16,11 @@ #include <unistd.h> #include "postgres.h" -#ifdef USE_LOCALE -# include <locale.h> +#ifdef USE_LOCALE +#include <locale.h> #endif #include "miscadmin.h" -#include "bootstrap/bootstrap.h" /* for BootstrapMain() */ +#include "bootstrap/bootstrap.h"/* for BootstrapMain() */ #include "tcop/tcopprot.h" /* for PostgresMain() */ #include "port-protos.h" /* for init_address_fixup() */ @@ -34,36 +34,45 @@ echo \"postmaster -B 256 >/var/log/pglog 2>&1 &\" | su - postgres\n\n" int main(int argc, char *argv[]) { - int len; + int len; + #ifdef USE_LOCALE - setlocale(LC_CTYPE,""); /* take locale information from an environment */ - setlocale(LC_COLLATE,""); - setlocale(LC_MONETARY,""); + setlocale(LC_CTYPE, ""); /* take locale information from an + * environment */ + setlocale(LC_COLLATE, ""); + setlocale(LC_MONETARY, ""); #endif #if defined(NOFIXADE) || defined(NOPRINTADE) - /* - * Must be first so that the bootstrap code calls it, too. - * (Only needed on some RISC architectures.) - */ - init_address_fixup(); -#endif /* NOFIXADE || NOPRINTADE */ - - /* use one executable for both postgres and postmaster, - invoke one or the other depending on the name of the executable */ - len = strlen(argv[0]); - if (!geteuid()) { - fprintf(stderr, "%s", NOROOTEXEC); - exit(1); - } + /* + * Must be first so that the bootstrap code calls it, too. (Only + * needed on some RISC architectures.) + */ + init_address_fixup(); +#endif /* NOFIXADE || NOPRINTADE */ + + /* + * use one executable for both postgres and postmaster, invoke one or + * the other depending on the name of the executable + */ + len = strlen(argv[0]); + + if (!geteuid()) + { + fprintf(stderr, "%s", NOROOTEXEC); + exit(1); + } - if(len >= 10 && ! strcmp(argv[0] + len - 10, "postmaster")) - exit(PostmasterMain(argc, argv)); + if (len >= 10 && !strcmp(argv[0] + len - 10, "postmaster")) + exit(PostmasterMain(argc, argv)); - /* if the first argument is "-boot", then invoke the backend in - bootstrap mode */ - if (argc > 1 && strcmp(argv[1], "-boot") == 0) - exit(BootstrapMain(argc-1, argv+1)); /* remove the -boot arg from the command line */ - else - exit(PostgresMain(argc, argv)); + /* + * if the first argument is "-boot", then invoke the backend in + * bootstrap mode + */ + if (argc > 1 && strcmp(argv[1], "-boot") == 0) + exit(BootstrapMain(argc - 1, argv + 1)); /* remove the -boot arg + * from the command line */ + else + exit(PostgresMain(argc, argv)); } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e763b9cd7ce..caf9e176ef3 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * copyfuncs.c-- - * Copy functions for Postgres tree nodes. + * Copy functions for Postgres tree nodes. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.6 1997/09/04 13:24:01 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.7 1997/09/07 04:42:39 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,7 @@ #include "parser/parse_query.h" #include "utils/syscache.h" -#include "utils/builtins.h" /* for namecpy */ +#include "utils/builtins.h" /* for namecpy */ #include "utils/elog.h" #include "utils/palloc.h" #include "catalog/pg_type.h" @@ -33,1716 +33,1774 @@ /* * listCopy-- - * this copy function only copies the "lcons-cells" of the list but not - * its contents. (good for list of pointers as well as list of integers). + * this copy function only copies the "lcons-cells" of the list but not + * its contents. (good for list of pointers as well as list of integers). */ -List * -listCopy(List *list) +List * +listCopy(List * list) { - List *newlist=NIL; - List *l, *nl=NIL; - - foreach(l, list) { - if (newlist==NIL) { - newlist = nl = lcons(lfirst(l),NIL); - }else { - lnext(nl) = lcons(lfirst(l),NIL); - nl = lnext(nl); + List *newlist = NIL; + List *l, + *nl = NIL; + + foreach(l, list) + { + if (newlist == NIL) + { + newlist = nl = lcons(lfirst(l), NIL); + } + else + { + lnext(nl) = lcons(lfirst(l), NIL); + nl = lnext(nl); + } } - } - return newlist; + return newlist; } - + /* * Node_Copy-- - * a macro to simplify calling of copyObject on the specified field + * a macro to simplify calling of copyObject on the specified field */ #define Node_Copy(from, newnode, field) \ - newnode->field = copyObject(from->field) + newnode->field = copyObject(from->field) /* **************************************************************** - * plannodes.h copy functions + * plannodes.h copy functions * **************************************************************** */ /* ---------------- - * CopyPlanFields + * CopyPlanFields * - * This function copies the fields of the Plan node. It is used by - * all the copy functions for classes which inherit from Plan. + * This function copies the fields of the Plan node. It is used by + * all the copy functions for classes which inherit from Plan. * ---------------- */ static void -CopyPlanFields(Plan *from, Plan *newnode) +CopyPlanFields(Plan * from, Plan * newnode) { - newnode->cost = from->cost; - newnode->plan_size = from->plan_size; - newnode->plan_width = from->plan_width; - newnode->state = from->state; - newnode->targetlist = copyObject(from->targetlist); - newnode->qual = copyObject(from->qual); - newnode->lefttree = copyObject(from->lefttree); - newnode->righttree = copyObject(from->righttree); + newnode->cost = from->cost; + newnode->plan_size = from->plan_size; + newnode->plan_width = from->plan_width; + newnode->state = from->state; + newnode->targetlist = copyObject(from->targetlist); + newnode->qual = copyObject(from->qual); + newnode->lefttree = copyObject(from->lefttree); + newnode->righttree = copyObject(from->righttree); } /* ---------------- - * _copyPlan + * _copyPlan * ---------------- */ -static Plan * -_copyPlan(Plan *from) +static Plan * +_copyPlan(Plan * from) { - Plan *newnode = makeNode(Plan); - - /* ---------------- - * copy the node superclass fields - * ---------------- - */ - CopyPlanFields(from, newnode); - - return newnode; + Plan *newnode = makeNode(Plan); + + /* ---------------- + * copy the node superclass fields + * ---------------- + */ + CopyPlanFields(from, newnode); + + return newnode; } /* ---------------- - * _copyExistential + * _copyExistential * ---------------- */ static Existential * -_copyExistential(Existential *from) +_copyExistential(Existential * from) { - Existential *newnode = makeNode(Existential); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields(from, newnode); - - return newnode; + Existential *newnode = makeNode(Existential); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields(from, newnode); + + return newnode; } /* ---------------- - * _copyResult + * _copyResult * ---------------- */ -static Result * -_copyResult(Result *from) +static Result * +_copyResult(Result * from) { - Result *newnode = makeNode(Result); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, resconstantqual); - Node_Copy(from, newnode, resstate); - - return newnode; + Result *newnode = makeNode(Result); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, resconstantqual); + Node_Copy(from, newnode, resstate); + + return newnode; } /* ---------------- - * _copyAppend + * _copyAppend * ---------------- */ -static Append * -_copyAppend(Append *from) +static Append * +_copyAppend(Append * from) { - Append *newnode = makeNode(Append); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, unionplans); - newnode->unionrelid = from->unionrelid; - Node_Copy(from, newnode, unionrtentries); - Node_Copy(from, newnode, unionstate); - - return newnode; + Append *newnode = makeNode(Append); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, unionplans); + newnode->unionrelid = from->unionrelid; + Node_Copy(from, newnode, unionrtentries); + Node_Copy(from, newnode, unionstate); + + return newnode; } /* ---------------- - * CopyScanFields + * CopyScanFields * - * This function copies the fields of the Scan node. It is used by - * all the copy functions for classes which inherit from Scan. + * This function copies the fields of the Scan node. It is used by + * all the copy functions for classes which inherit from Scan. * ---------------- */ static void -CopyScanFields(Scan *from, Scan *newnode) +CopyScanFields(Scan * from, Scan * newnode) { - newnode->scanrelid = from->scanrelid; - Node_Copy(from, newnode, scanstate); - return; + newnode->scanrelid = from->scanrelid; + Node_Copy(from, newnode, scanstate); + return; } /* ---------------- - * _copyScan + * _copyScan * ---------------- */ -static Scan * -_copyScan(Scan *from) +static Scan * +_copyScan(Scan * from) { - Scan *newnode = makeNode(Scan); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyScanFields(from, newnode); - - return newnode; + Scan *newnode = makeNode(Scan); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyScanFields(from, newnode); + + return newnode; } /* ---------------- - * _copySeqScan + * _copySeqScan * ---------------- */ static SeqScan * -_copySeqScan(SeqScan *from) +_copySeqScan(SeqScan * from) { - SeqScan *newnode = makeNode(SeqScan); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyScanFields((Scan*)from, (Scan*)newnode); - - return newnode; + SeqScan *newnode = makeNode(SeqScan); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyScanFields((Scan *) from, (Scan *) newnode); + + return newnode; } /* ---------------- - * _copyIndexScan + * _copyIndexScan * ---------------- */ static IndexScan * -_copyIndexScan(IndexScan *from) +_copyIndexScan(IndexScan * from) { - IndexScan *newnode = makeNode(IndexScan); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyScanFields((Scan*)from, (Scan*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->indxid = listCopy(from->indxid); - Node_Copy(from, newnode, indxqual); - Node_Copy(from, newnode, indxstate); - - return newnode; + IndexScan *newnode = makeNode(IndexScan); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyScanFields((Scan *) from, (Scan *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->indxid = listCopy(from->indxid); + Node_Copy(from, newnode, indxqual); + Node_Copy(from, newnode, indxstate); + + return newnode; } /* ---------------- - * CopyJoinFields + * CopyJoinFields * - * This function copies the fields of the Join node. It is used by - * all the copy functions for classes which inherit from Join. + * This function copies the fields of the Join node. It is used by + * all the copy functions for classes which inherit from Join. * ---------------- */ static void -CopyJoinFields(Join *from, Join *newnode) +CopyJoinFields(Join * from, Join * newnode) { - /* nothing extra */ - return; + /* nothing extra */ + return; } /* ---------------- - * _copyJoin + * _copyJoin * ---------------- */ -static Join * -_copyJoin(Join *from) +static Join * +_copyJoin(Join * from) { - Join *newnode = makeNode(Join); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyJoinFields(from, newnode); - - return newnode; + Join *newnode = makeNode(Join); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyJoinFields(from, newnode); + + return newnode; } /* ---------------- - * _copyNestLoop + * _copyNestLoop * ---------------- */ static NestLoop * -_copyNestLoop(NestLoop *from) +_copyNestLoop(NestLoop * from) { - NestLoop *newnode = makeNode(NestLoop); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyJoinFields((Join*)from, (Join*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, nlstate); - - return newnode; + NestLoop *newnode = makeNode(NestLoop); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyJoinFields((Join *) from, (Join *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, nlstate); + + return newnode; } /* ---------------- - * _copyMergeJoin + * _copyMergeJoin * ---------------- */ static MergeJoin * -_copyMergeJoin(MergeJoin *from) +_copyMergeJoin(MergeJoin * from) { - MergeJoin *newnode = makeNode(MergeJoin); - List *newlist; - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyJoinFields((Join*)from, (Join*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, mergeclauses); - - newnode->mergesortop = from->mergesortop; - newlist = NIL; - - newnode->mergerightorder = (Oid *)palloc(sizeof(Oid)*2); - newnode->mergerightorder[0] = from->mergerightorder[0]; - newnode->mergerightorder[1] = 0; - - newnode->mergeleftorder = (Oid *)palloc(sizeof(Oid)*2); - newnode->mergeleftorder[0] = from->mergeleftorder[0]; - newnode->mergeleftorder[1] = 0; - - Node_Copy(from, newnode, mergestate); - - return newnode; + MergeJoin *newnode = makeNode(MergeJoin); + List *newlist; + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyJoinFields((Join *) from, (Join *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, mergeclauses); + + newnode->mergesortop = from->mergesortop; + newlist = NIL; + + newnode->mergerightorder = (Oid *) palloc(sizeof(Oid) * 2); + newnode->mergerightorder[0] = from->mergerightorder[0]; + newnode->mergerightorder[1] = 0; + + newnode->mergeleftorder = (Oid *) palloc(sizeof(Oid) * 2); + newnode->mergeleftorder[0] = from->mergeleftorder[0]; + newnode->mergeleftorder[1] = 0; + + Node_Copy(from, newnode, mergestate); + + return newnode; } /* ---------------- - * _copyHashJoin + * _copyHashJoin * ---------------- */ static HashJoin * -_copyHashJoin(HashJoin *from) +_copyHashJoin(HashJoin * from) { - HashJoin *newnode = makeNode(HashJoin); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyJoinFields((Join*)from, (Join*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, hashclauses); - - newnode->hashjoinop = from->hashjoinop; - - Node_Copy(from, newnode, hashjoinstate); - - newnode->hashjointable = from->hashjointable; - newnode->hashjointablekey = from->hashjointablekey; - newnode->hashjointablesize = from->hashjointablesize; - newnode->hashdone = from->hashdone; - - return newnode; + HashJoin *newnode = makeNode(HashJoin); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyJoinFields((Join *) from, (Join *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, hashclauses); + + newnode->hashjoinop = from->hashjoinop; + + Node_Copy(from, newnode, hashjoinstate); + + newnode->hashjointable = from->hashjointable; + newnode->hashjointablekey = from->hashjointablekey; + newnode->hashjointablesize = from->hashjointablesize; + newnode->hashdone = from->hashdone; + + return newnode; } /* ---------------- - * CopyTempFields + * CopyTempFields * - * This function copies the fields of the Temp node. It is used by - * all the copy functions for classes which inherit from Temp. + * This function copies the fields of the Temp node. It is used by + * all the copy functions for classes which inherit from Temp. * ---------------- */ static void -CopyTempFields(Temp *from, Temp *newnode) +CopyTempFields(Temp * from, Temp * newnode) { - newnode->tempid = from->tempid; - newnode->keycount = from->keycount; - return; + newnode->tempid = from->tempid; + newnode->keycount = from->keycount; + return; } /* ---------------- - * _copyTemp + * _copyTemp * ---------------- */ -static Temp * -_copyTemp(Temp *from) +static Temp * +_copyTemp(Temp * from) { - Temp *newnode = makeNode(Temp); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyTempFields(from, newnode); - - return newnode; + Temp *newnode = makeNode(Temp); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyTempFields(from, newnode); + + return newnode; } /* ---------------- - * _copyMaterial + * _copyMaterial * ---------------- */ static Material * -_copyMaterial(Material *from) +_copyMaterial(Material * from) { - Material *newnode = makeNode(Material); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyTempFields((Temp*)from, (Temp*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, matstate); - - return newnode; + Material *newnode = makeNode(Material); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyTempFields((Temp *) from, (Temp *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, matstate); + + return newnode; } /* ---------------- - * _copySort + * _copySort * ---------------- */ -static Sort * -_copySort(Sort *from) +static Sort * +_copySort(Sort * from) { - Sort *newnode = makeNode(Sort); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyTempFields((Temp*)from, (Temp*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, sortstate); - - return newnode; + Sort *newnode = makeNode(Sort); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyTempFields((Temp *) from, (Temp *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, sortstate); + + return newnode; } /* --------------- - * _copyAgg + * _copyAgg * -------------- */ -static Agg * -_copyAgg(Agg *from) +static Agg * +_copyAgg(Agg * from) { - Agg *newnode = makeNode(Agg); - int i; - - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyTempFields((Temp*)from, (Temp*)newnode); - - newnode->numAgg = from->numAgg; - newnode->aggs = malloc(sizeof(Aggreg *)); - for(i=0; i < from->numAgg; i++) { - newnode->aggs[i] = copyObject(from->aggs[i]); - } - - Node_Copy(from, newnode, aggstate); - - return newnode; + Agg *newnode = makeNode(Agg); + int i; + + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyTempFields((Temp *) from, (Temp *) newnode); + + newnode->numAgg = from->numAgg; + newnode->aggs = malloc(sizeof(Aggreg *)); + for (i = 0; i < from->numAgg; i++) + { + newnode->aggs[i] = copyObject(from->aggs[i]); + } + + Node_Copy(from, newnode, aggstate); + + return newnode; } /* ---------------- - * _copyUnique + * _copyUnique * ---------------- */ -static Unique * -_copyUnique(Unique *from) +static Unique * +_copyUnique(Unique * from) { - Unique *newnode = makeNode(Unique); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - CopyTempFields((Temp*)from, (Temp*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, uniquestate); - - return newnode; + Unique *newnode = makeNode(Unique); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyTempFields((Temp *) from, (Temp *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, uniquestate); + + return newnode; } /* ---------------- - * _copyHash + * _copyHash * ---------------- */ -static Hash * -_copyHash(Hash *from) +static Hash * +_copyHash(Hash * from) { - Hash *newnode = makeNode(Hash); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyPlanFields((Plan*)from, (Plan*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, hashkey); - Node_Copy(from, newnode, hashstate); - - newnode->hashtable = from->hashtable; - newnode->hashtablekey = from->hashtablekey; - newnode->hashtablesize = from->hashtablesize; - - return newnode; + Hash *newnode = makeNode(Hash); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, hashkey); + Node_Copy(from, newnode, hashstate); + + newnode->hashtable = from->hashtable; + newnode->hashtablekey = from->hashtablekey; + newnode->hashtablesize = from->hashtablesize; + + return newnode; } /* **************************************************************** - * primnodes.h copy functions + * primnodes.h copy functions * **************************************************************** */ /* ---------------- - * _copyResdom + * _copyResdom * ---------------- */ -static Resdom * -_copyResdom(Resdom *from) +static Resdom * +_copyResdom(Resdom * from) { - Resdom *newnode = makeNode(Resdom); - - newnode->resno = from->resno; - newnode->restype = from->restype; - newnode->reslen = from->reslen; - - if (from->resname != NULL) { - newnode->resname = palloc(strlen(from->resname)+1); - strcpy(newnode->resname, from->resname); - } else - newnode->resname = (char*) NULL; - - newnode->reskey = from->reskey; - newnode->reskeyop = from->reskeyop; - newnode->resjunk = from->resjunk; - - return newnode; + Resdom *newnode = makeNode(Resdom); + + newnode->resno = from->resno; + newnode->restype = from->restype; + newnode->reslen = from->reslen; + + if (from->resname != NULL) + { + newnode->resname = palloc(strlen(from->resname) + 1); + strcpy(newnode->resname, from->resname); + } + else + newnode->resname = (char *) NULL; + + newnode->reskey = from->reskey; + newnode->reskeyop = from->reskeyop; + newnode->resjunk = from->resjunk; + + return newnode; } -static Fjoin * -_copyFjoin(Fjoin *from) +static Fjoin * +_copyFjoin(Fjoin * from) { - Fjoin *newnode = makeNode(Fjoin); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - - newnode->fj_initialized = from->fj_initialized; - newnode->fj_nNodes = from->fj_nNodes; - - Node_Copy(from, newnode, fj_innerNode); - - newnode->fj_results = (DatumPtr) - palloc((from->fj_nNodes)*sizeof(Datum)); - - newnode->fj_alwaysDone = (BoolPtr) - palloc((from->fj_nNodes)*sizeof(bool)); - - memmove(from->fj_results, - newnode->fj_results, - (from->fj_nNodes)*sizeof(Datum)); - - memmove(from->fj_alwaysDone, - newnode->fj_alwaysDone, - (from->fj_nNodes)*sizeof(bool)); - - - return newnode; + Fjoin *newnode = makeNode(Fjoin); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + + newnode->fj_initialized = from->fj_initialized; + newnode->fj_nNodes = from->fj_nNodes; + + Node_Copy(from, newnode, fj_innerNode); + + newnode->fj_results = (DatumPtr) + palloc((from->fj_nNodes) * sizeof(Datum)); + + newnode->fj_alwaysDone = (BoolPtr) + palloc((from->fj_nNodes) * sizeof(bool)); + + memmove(from->fj_results, + newnode->fj_results, + (from->fj_nNodes) * sizeof(Datum)); + + memmove(from->fj_alwaysDone, + newnode->fj_alwaysDone, + (from->fj_nNodes) * sizeof(bool)); + + + return newnode; } /* ---------------- - * _copyExpr + * _copyExpr * ---------------- */ -static Expr * -_copyExpr(Expr *from) +static Expr * +_copyExpr(Expr * from) { - Expr *newnode = makeNode(Expr); - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - newnode->typeOid = from->typeOid; - newnode->opType = from->opType; - - Node_Copy(from, newnode, oper); - Node_Copy(from, newnode, args); - - return newnode; + Expr *newnode = makeNode(Expr); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + newnode->typeOid = from->typeOid; + newnode->opType = from->opType; + + Node_Copy(from, newnode, oper); + Node_Copy(from, newnode, args); + + return newnode; } /* ---------------- - * _copyVar + * _copyVar * ---------------- */ -static Var * -_copyVar(Var *from) +static Var * +_copyVar(Var * from) { - Var *newnode = makeNode(Var); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->varno = from->varno; - newnode->varattno = from->varattno; - newnode->vartype = from->vartype; - - newnode->varnoold = from->varnoold; - newnode->varoattno = from->varoattno; - - return newnode; + Var *newnode = makeNode(Var); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->varno = from->varno; + newnode->varattno = from->varattno; + newnode->vartype = from->vartype; + + newnode->varnoold = from->varnoold; + newnode->varoattno = from->varoattno; + + return newnode; } /* ---------------- - * _copyOper + * _copyOper * ---------------- */ -static Oper * -_copyOper(Oper *from) +static Oper * +_copyOper(Oper * from) { - Oper *newnode = makeNode(Oper); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->opno = from->opno; - newnode->opid = from->opid; - newnode->opresulttype = from->opresulttype; - newnode->opsize = from->opsize; - - /* - * NOTE: shall we copy the cache structure or just the pointer ? - * Alternatively we can set 'op_fcache' to NULL, in which - * case the executor will initialize it when it needs it... - */ - newnode->op_fcache = from->op_fcache; - - return newnode; + Oper *newnode = makeNode(Oper); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->opno = from->opno; + newnode->opid = from->opid; + newnode->opresulttype = from->opresulttype; + newnode->opsize = from->opsize; + + /* + * NOTE: shall we copy the cache structure or just the pointer ? + * Alternatively we can set 'op_fcache' to NULL, in which case the + * executor will initialize it when it needs it... + */ + newnode->op_fcache = from->op_fcache; + + return newnode; } /* ---------------- - * _copyConst + * _copyConst * ---------------- */ -static Const * -_copyConst(Const *from) +static Const * +_copyConst(Const * from) { - static Oid cached_type; - static bool cached_typbyval; - - Const *newnode = makeNode(Const); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->consttype = from->consttype; - newnode->constlen = from->constlen; - - /* ---------------- - * XXX super cheesy hack until parser/planner - * puts in the right values here. - * ---------------- - */ - if (cached_type != from->consttype) { - HeapTuple typeTuple; - TypeTupleForm typeStruct; - - /* ---------------- - * get the type tuple corresponding to the paramList->type, - * If this fails, returnValue has been pre-initialized - * to "null" so we just return it. - * ---------------- - */ - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(from->consttype), - 0,0,0); - + static Oid cached_type; + static bool cached_typbyval; + + Const *newnode = makeNode(Const); + /* ---------------- - * get the type length and by-value from the type tuple and - * save the information in our one element cache. + * copy remainder of node * ---------------- */ - Assert(PointerIsValid(typeTuple)); - - typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple); - cached_typbyval = (typeStruct)->typbyval ? true : false ; - cached_type = from->consttype; - } - - from->constbyval = cached_typbyval; - - if (!from->constisnull) { + newnode->consttype = from->consttype; + newnode->constlen = from->constlen; + /* ---------------- - * copying the Datum in a const node is a bit trickier - * because it might be a pointer and it might also be of - * variable length... + * XXX super cheesy hack until parser/planner + * puts in the right values here. * ---------------- */ - if (from->constbyval == true) { - /* ---------------- - * passed by value so just copy the datum. - * ---------------- - */ - newnode->constvalue = from->constvalue; - } else { - /* ---------------- - * not passed by value. datum contains a pointer. - * ---------------- - */ - if (from->constlen != -1) { + if (cached_type != from->consttype) + { + HeapTuple typeTuple; + TypeTupleForm typeStruct; + + /* ---------------- + * get the type tuple corresponding to the paramList->type, + * If this fails, returnValue has been pre-initialized + * to "null" so we just return it. + * ---------------- + */ + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(from->consttype), + 0, 0, 0); + /* ---------------- - * fixed length structure + * get the type length and by-value from the type tuple and + * save the information in our one element cache. * ---------------- */ - newnode->constvalue = PointerGetDatum(palloc(from->constlen)); - memmove((char*)newnode->constvalue, - (char*)from->constvalue, from->constlen); - } else { + Assert(PointerIsValid(typeTuple)); + + typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple); + cached_typbyval = (typeStruct)->typbyval ? true : false; + cached_type = from->consttype; + } + + from->constbyval = cached_typbyval; + + if (!from->constisnull) + { /* ---------------- - * variable length structure. here the length is stored - * in the first int pointed to by the constval. + * copying the Datum in a const node is a bit trickier + * because it might be a pointer and it might also be of + * variable length... * ---------------- */ - int length; - length = *((int *) from->constvalue); - newnode->constvalue = PointerGetDatum(palloc(length)); - memmove((char*)newnode->constvalue, - (char*)from->constvalue, length); - } + if (from->constbyval == true) + { + /* ---------------- + * passed by value so just copy the datum. + * ---------------- + */ + newnode->constvalue = from->constvalue; + } + else + { + /* ---------------- + * not passed by value. datum contains a pointer. + * ---------------- + */ + if (from->constlen != -1) + { + /* ---------------- + * fixed length structure + * ---------------- + */ + newnode->constvalue = PointerGetDatum(palloc(from->constlen)); + memmove((char *) newnode->constvalue, + (char *) from->constvalue, from->constlen); + } + else + { + /* ---------------- + * variable length structure. here the length is stored + * in the first int pointed to by the constval. + * ---------------- + */ + int length; + + length = *((int *) from->constvalue); + newnode->constvalue = PointerGetDatum(palloc(length)); + memmove((char *) newnode->constvalue, + (char *) from->constvalue, length); + } + } + } + else + { + newnode->constvalue = from->constvalue; } - } - else { - newnode->constvalue = from->constvalue; - } - newnode->constisnull = from->constisnull; - newnode->constbyval = from->constbyval; - - return newnode; + newnode->constisnull = from->constisnull; + newnode->constbyval = from->constbyval; + + return newnode; } /* ---------------- - * _copyParam + * _copyParam * ---------------- */ -static Param * -_copyParam(Param *from) +static Param * +_copyParam(Param * from) { - Param *newnode = makeNode(Param); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->paramkind = from->paramkind; - newnode->paramid = from->paramid; - - if (from->paramname != NULL) { - newnode->paramname = pstrdup(from->paramname); - } else - newnode->paramname = (char*)NULL; - - newnode->paramtype = from->paramtype; - Node_Copy(from, newnode, param_tlist); - - return newnode; + Param *newnode = makeNode(Param); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->paramkind = from->paramkind; + newnode->paramid = from->paramid; + + if (from->paramname != NULL) + { + newnode->paramname = pstrdup(from->paramname); + } + else + newnode->paramname = (char *) NULL; + + newnode->paramtype = from->paramtype; + Node_Copy(from, newnode, param_tlist); + + return newnode; } /* ---------------- - * _copyFunc + * _copyFunc * ---------------- */ -static Func * -_copyFunc(Func *from) +static Func * +_copyFunc(Func * from) { - Func *newnode = makeNode(Func); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->funcid = from->funcid; - newnode->functype = from->functype; - newnode->funcisindex = from->funcisindex; - newnode->funcsize = from->funcsize; - newnode->func_fcache = from->func_fcache; - Node_Copy(from, newnode, func_tlist); - Node_Copy(from, newnode, func_planlist); - - return newnode; + Func *newnode = makeNode(Func); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->funcid = from->funcid; + newnode->functype = from->functype; + newnode->funcisindex = from->funcisindex; + newnode->funcsize = from->funcsize; + newnode->func_fcache = from->func_fcache; + Node_Copy(from, newnode, func_tlist); + Node_Copy(from, newnode, func_planlist); + + return newnode; } /* ---------------- - * _copyAggreg + * _copyAggreg * ---------------- */ -static Aggreg * -_copyAggreg(Aggreg *from) +static Aggreg * +_copyAggreg(Aggreg * from) { - Aggreg *newnode = makeNode(Aggreg); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->aggname = pstrdup(from->aggname); - newnode->basetype = from->basetype; - newnode->aggtype = from->aggtype; - - Node_Copy(from, newnode, target); - - newnode->aggno = from->aggno; - - return newnode; + Aggreg *newnode = makeNode(Aggreg); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->aggname = pstrdup(from->aggname); + newnode->basetype = from->basetype; + newnode->aggtype = from->aggtype; + + Node_Copy(from, newnode, target); + + newnode->aggno = from->aggno; + + return newnode; } -static Array * -_copyArray(Array *from) +static Array * +_copyArray(Array * from) { - Array *newnode = makeNode(Array); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->arrayelemtype = from->arrayelemtype; - newnode->arrayelemlength = from->arrayelemlength; - newnode->arrayelembyval = from->arrayelembyval; - newnode->arrayndim = from->arrayndim; - newnode->arraylow = from->arraylow; - newnode->arrayhigh = from->arrayhigh; - newnode->arraylen = from->arraylen; - - return newnode; + Array *newnode = makeNode(Array); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->arrayelemtype = from->arrayelemtype; + newnode->arrayelemlength = from->arrayelemlength; + newnode->arrayelembyval = from->arrayelembyval; + newnode->arrayndim = from->arrayndim; + newnode->arraylow = from->arraylow; + newnode->arrayhigh = from->arrayhigh; + newnode->arraylen = from->arraylen; + + return newnode; } static ArrayRef * -_copyArrayRef(ArrayRef *from) +_copyArrayRef(ArrayRef * from) { - ArrayRef *newnode = makeNode(ArrayRef); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->refelemtype = from->refelemtype; - newnode->refattrlength = from->refattrlength; - newnode->refelemlength = from->refelemlength; - newnode->refelembyval = from->refelembyval; - - Node_Copy(from,newnode,refupperindexpr); - Node_Copy(from,newnode,reflowerindexpr); - Node_Copy(from,newnode,refexpr); - Node_Copy(from,newnode,refassgnexpr); - - return newnode; + ArrayRef *newnode = makeNode(ArrayRef); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->refelemtype = from->refelemtype; + newnode->refattrlength = from->refattrlength; + newnode->refelemlength = from->refelemlength; + newnode->refelembyval = from->refelembyval; + + Node_Copy(from, newnode, refupperindexpr); + Node_Copy(from, newnode, reflowerindexpr); + Node_Copy(from, newnode, refexpr); + Node_Copy(from, newnode, refassgnexpr); + + return newnode; } /* **************************************************************** - * relation.h copy functions + * relation.h copy functions * **************************************************************** */ /* ---------------- - * _copyRel + * _copyRel * ---------------- */ /* - ** when you change this, also make sure to fix up xfunc_copyRel in + ** when you change this, also make sure to fix up xfunc_copyRel in ** planner/path/xfunc.c accordingly!!! - ** -- JMH, 8/2/93 + ** -- JMH, 8/2/93 */ -static Rel * -_copyRel(Rel *from) +static Rel * +_copyRel(Rel * from) { - Rel *newnode = makeNode(Rel); - int i, len; - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->relids = listCopy(from->relids); - - newnode->indexed = from->indexed; - newnode->pages = from->pages; - newnode->tuples = from->tuples; - newnode->size = from->size; - newnode->width = from->width; - newnode->indproc = from->indproc; - - Node_Copy(from, newnode, targetlist); - Node_Copy(from, newnode, pathlist); - Node_Copy(from, newnode, unorderedpath); - Node_Copy(from, newnode, cheapestpath); - newnode->pruneable = from->pruneable; - newnode->relam = from->relam; - - if (from->classlist) { - for(len=0; from->classlist[len]!=0; len++) - ; - newnode->classlist = (Oid *)palloc(sizeof(Oid) * (len+1)); - for(i=0; i < len; i++) { - newnode->classlist[i] = from->classlist[i]; + Rel *newnode = makeNode(Rel); + int i, + len; + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->relids = listCopy(from->relids); + + newnode->indexed = from->indexed; + newnode->pages = from->pages; + newnode->tuples = from->tuples; + newnode->size = from->size; + newnode->width = from->width; + newnode->indproc = from->indproc; + + Node_Copy(from, newnode, targetlist); + Node_Copy(from, newnode, pathlist); + Node_Copy(from, newnode, unorderedpath); + Node_Copy(from, newnode, cheapestpath); + newnode->pruneable = from->pruneable; + newnode->relam = from->relam; + + if (from->classlist) + { + for (len = 0; from->classlist[len] != 0; len++) + ; + newnode->classlist = (Oid *) palloc(sizeof(Oid) * (len + 1)); + for (i = 0; i < len; i++) + { + newnode->classlist[i] = from->classlist[i]; + } + newnode->classlist[len] = 0; } - newnode->classlist[len] = 0; - } - - if (from->indexkeys) { - for(len=0; from->indexkeys[len]!=0; len++) - ; - newnode->indexkeys = (int *)palloc(sizeof(int) * (len+1)); - for(i=0; i < len; i++) { - newnode->indexkeys[i] = from->indexkeys[i]; + + if (from->indexkeys) + { + for (len = 0; from->indexkeys[len] != 0; len++) + ; + newnode->indexkeys = (int *) palloc(sizeof(int) * (len + 1)); + for (i = 0; i < len; i++) + { + newnode->indexkeys[i] = from->indexkeys[i]; + } + newnode->indexkeys[len] = 0; } - newnode->indexkeys[len] = 0; - } - - if (from->ordering) { - for(len=0; from->ordering[len]!=0; len++) - ; - newnode->ordering = (Oid *)palloc(sizeof(Oid) * (len+1)); - for(i=0; i < len; i++) { - newnode->ordering[i] = from->ordering[i]; + + if (from->ordering) + { + for (len = 0; from->ordering[len] != 0; len++) + ; + newnode->ordering = (Oid *) palloc(sizeof(Oid) * (len + 1)); + for (i = 0; i < len; i++) + { + newnode->ordering[i] = from->ordering[i]; + } + newnode->ordering[len] = 0; } - newnode->ordering[len] = 0; - } - - Node_Copy(from, newnode, clauseinfo); - Node_Copy(from, newnode, joininfo); - Node_Copy(from, newnode, innerjoin); - Node_Copy(from, newnode, superrels); - - return newnode; + + Node_Copy(from, newnode, clauseinfo); + Node_Copy(from, newnode, joininfo); + Node_Copy(from, newnode, innerjoin); + Node_Copy(from, newnode, superrels); + + return newnode; } /* ---------------- - * CopyPathFields + * CopyPathFields * - * This function copies the fields of the Path node. It is used by - * all the copy functions for classes which inherit from Path. + * This function copies the fields of the Path node. It is used by + * all the copy functions for classes which inherit from Path. * ---------------- */ static void -CopyPathFields(Path *from, Path *newnode) +CopyPathFields(Path * from, Path * newnode) { - newnode->pathtype = from->pathtype; - /* Modify the next line, since it causes the copying to cycle - (i.e. the parent points right back here! - -- JMH, 7/7/92. - Old version: - Node_Copy(from, newnode, parent); - */ - newnode->parent = from->parent; - - newnode->path_cost = from->path_cost; - - newnode->p_ordering.ordtype = from->p_ordering.ordtype; - if (from->p_ordering.ordtype == SORTOP_ORDER) { - int len, i; - Oid *ordering = from->p_ordering.ord.sortop; - - if (ordering) { - for(len=0; ordering[len]!=0; len++) - ; - newnode->p_ordering.ord.sortop = - (Oid *)palloc(sizeof(Oid) * (len+1)); - for(i=0; i < len; i++) { - newnode->p_ordering.ord.sortop[i] = ordering[i]; - } - newnode->p_ordering.ord.sortop[len] = 0; - } else { - newnode->p_ordering.ord.sortop = NULL; + newnode->pathtype = from->pathtype; + + /* + * Modify the next line, since it causes the copying to cycle (i.e. + * the parent points right back here! -- JMH, 7/7/92. Old version: + * Node_Copy(from, newnode, parent); + */ + newnode->parent = from->parent; + + newnode->path_cost = from->path_cost; + + newnode->p_ordering.ordtype = from->p_ordering.ordtype; + if (from->p_ordering.ordtype == SORTOP_ORDER) + { + int len, + i; + Oid *ordering = from->p_ordering.ord.sortop; + + if (ordering) + { + for (len = 0; ordering[len] != 0; len++) + ; + newnode->p_ordering.ord.sortop = + (Oid *) palloc(sizeof(Oid) * (len + 1)); + for (i = 0; i < len; i++) + { + newnode->p_ordering.ord.sortop[i] = ordering[i]; + } + newnode->p_ordering.ord.sortop[len] = 0; + } + else + { + newnode->p_ordering.ord.sortop = NULL; + } + } + else + { + Node_Copy(from, newnode, p_ordering.ord.merge); } - } else { - Node_Copy(from, newnode, p_ordering.ord.merge); - } - - Node_Copy(from, newnode, keys); - - newnode->outerjoincost = from->outerjoincost; - - newnode->joinid = listCopy(from->joinid); - Node_Copy(from, newnode, locclauseinfo); + + Node_Copy(from, newnode, keys); + + newnode->outerjoincost = from->outerjoincost; + + newnode->joinid = listCopy(from->joinid); + Node_Copy(from, newnode, locclauseinfo); } /* ---------------- - * _copyPath + * _copyPath * ---------------- */ -static Path * -_copyPath(Path *from) +static Path * +_copyPath(Path * from) { - Path *newnode = makeNode(Path); - - CopyPathFields(from, newnode); - - return newnode; + Path *newnode = makeNode(Path); + + CopyPathFields(from, newnode); + + return newnode; } /* ---------------- - * _copyIndexPath + * _copyIndexPath * ---------------- */ static IndexPath * -_copyIndexPath(IndexPath *from) +_copyIndexPath(IndexPath * from) { - IndexPath *newnode = makeNode(IndexPath); - - /* ---------------- - * copy the node superclass fields - * ---------------- - */ - CopyPathFields((Path*)from, (Path*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->indexid = listCopy(from->indexid); - Node_Copy(from, newnode, indexqual); - - if (from->indexkeys) - { - int i, len; - - for(len=0; from->indexkeys[len]!=0; len++) - ; - newnode->indexkeys = (int *)palloc(sizeof(int) * (len+1)); - for(i=0; i < len; i++) { - newnode->indexkeys[i] = from->indexkeys[i]; + IndexPath *newnode = makeNode(IndexPath); + + /* ---------------- + * copy the node superclass fields + * ---------------- + */ + CopyPathFields((Path *) from, (Path *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->indexid = listCopy(from->indexid); + Node_Copy(from, newnode, indexqual); + + if (from->indexkeys) + { + int i, + len; + + for (len = 0; from->indexkeys[len] != 0; len++) + ; + newnode->indexkeys = (int *) palloc(sizeof(int) * (len + 1)); + for (i = 0; i < len; i++) + { + newnode->indexkeys[i] = from->indexkeys[i]; + } + newnode->indexkeys[len] = 0; } - newnode->indexkeys[len] = 0; - } - - return newnode; + + return newnode; } /* ---------------- - * CopyJoinPathFields + * CopyJoinPathFields * - * This function copies the fields of the JoinPath node. It is used by - * all the copy functions for classes which inherit from JoinPath. + * This function copies the fields of the JoinPath node. It is used by + * all the copy functions for classes which inherit from JoinPath. * ---------------- */ static void -CopyJoinPathFields(JoinPath *from, JoinPath *newnode) +CopyJoinPathFields(JoinPath * from, JoinPath * newnode) { - Node_Copy(from, newnode, pathclauseinfo); - Node_Copy(from, newnode, outerjoinpath); - Node_Copy(from, newnode, innerjoinpath); + Node_Copy(from, newnode, pathclauseinfo); + Node_Copy(from, newnode, outerjoinpath); + Node_Copy(from, newnode, innerjoinpath); } /* ---------------- - * _copyJoinPath + * _copyJoinPath * ---------------- */ static JoinPath * -_copyJoinPath(JoinPath *from) +_copyJoinPath(JoinPath * from) { - JoinPath *newnode = makeNode(JoinPath); - - /* ---------------- - * copy the node superclass fields - * ---------------- - */ - CopyPathFields((Path*)from, (Path*)newnode); - CopyJoinPathFields(from, newnode); - - return newnode; + JoinPath *newnode = makeNode(JoinPath); + + /* ---------------- + * copy the node superclass fields + * ---------------- + */ + CopyPathFields((Path *) from, (Path *) newnode); + CopyJoinPathFields(from, newnode); + + return newnode; } /* ---------------- - * _copyMergePath + * _copyMergePath * ---------------- */ static MergePath * -_copyMergePath(MergePath *from) +_copyMergePath(MergePath * from) { - MergePath *newnode = makeNode(MergePath); - - /* ---------------- - * copy the node superclass fields - * ---------------- - */ - CopyPathFields((Path*)from, (Path*)newnode); - CopyJoinPathFields((JoinPath*)from, (JoinPath*)newnode); - - /* ---------------- - * copy the remainder of the node - * ---------------- - */ - Node_Copy(from, newnode, path_mergeclauses); - Node_Copy(from, newnode, outersortkeys); - Node_Copy(from, newnode, innersortkeys); - - return newnode; + MergePath *newnode = makeNode(MergePath); + + /* ---------------- + * copy the node superclass fields + * ---------------- + */ + CopyPathFields((Path *) from, (Path *) newnode); + CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode); + + /* ---------------- + * copy the remainder of the node + * ---------------- + */ + Node_Copy(from, newnode, path_mergeclauses); + Node_Copy(from, newnode, outersortkeys); + Node_Copy(from, newnode, innersortkeys); + + return newnode; } /* ---------------- - * _copyHashPath + * _copyHashPath * ---------------- */ static HashPath * -_copyHashPath(HashPath *from) +_copyHashPath(HashPath * from) { - HashPath *newnode = makeNode(HashPath); - - /* ---------------- - * copy the node superclass fields - * ---------------- - */ - CopyPathFields((Path*)from, (Path*)newnode); - CopyJoinPathFields((JoinPath*)from, (JoinPath*)newnode); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, path_hashclauses); - Node_Copy(from, newnode, outerhashkeys); - Node_Copy(from, newnode, innerhashkeys); - - return newnode; + HashPath *newnode = makeNode(HashPath); + + /* ---------------- + * copy the node superclass fields + * ---------------- + */ + CopyPathFields((Path *) from, (Path *) newnode); + CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, path_hashclauses); + Node_Copy(from, newnode, outerhashkeys); + Node_Copy(from, newnode, innerhashkeys); + + return newnode; } /* ---------------- - * _copyOrderKey + * _copyOrderKey * ---------------- */ static OrderKey * -_copyOrderKey(OrderKey *from) +_copyOrderKey(OrderKey * from) { - OrderKey *newnode = makeNode(OrderKey); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->attribute_number = from->attribute_number; - newnode->array_index = from->array_index; - - return newnode; + OrderKey *newnode = makeNode(OrderKey); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->attribute_number = from->attribute_number; + newnode->array_index = from->array_index; + + return newnode; } /* ---------------- - * _copyJoinKey + * _copyJoinKey * ---------------- */ static JoinKey * -_copyJoinKey(JoinKey *from) +_copyJoinKey(JoinKey * from) { - JoinKey *newnode = makeNode(JoinKey); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, outer); - Node_Copy(from, newnode, inner); - - return newnode; + JoinKey *newnode = makeNode(JoinKey); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, outer); + Node_Copy(from, newnode, inner); + + return newnode; } /* ---------------- - * _copyMergeOrder + * _copyMergeOrder * ---------------- */ static MergeOrder * -_copyMergeOrder(MergeOrder *from) +_copyMergeOrder(MergeOrder * from) { - MergeOrder *newnode = makeNode(MergeOrder); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->join_operator = from->join_operator; - newnode->left_operator = from->left_operator; - newnode->right_operator = from->right_operator; - newnode->left_type = from->left_type; - newnode->right_type = from->right_type; - - return newnode; + MergeOrder *newnode = makeNode(MergeOrder); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->join_operator = from->join_operator; + newnode->left_operator = from->left_operator; + newnode->right_operator = from->right_operator; + newnode->left_type = from->left_type; + newnode->right_type = from->right_type; + + return newnode; } /* ---------------- - * _copyCInfo + * _copyCInfo * ---------------- */ -static CInfo * -_copyCInfo(CInfo *from) +static CInfo * +_copyCInfo(CInfo * from) { - CInfo *newnode = makeNode(CInfo); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, clause); - - newnode->selectivity = from->selectivity; - newnode->notclause = from->notclause; - - Node_Copy(from, newnode, indexids); - Node_Copy(from, newnode, mergesortorder); - newnode->hashjoinoperator = from->hashjoinoperator; - newnode->cinfojoinid = listCopy(from->cinfojoinid); - - return newnode; + CInfo *newnode = makeNode(CInfo); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, clause); + + newnode->selectivity = from->selectivity; + newnode->notclause = from->notclause; + + Node_Copy(from, newnode, indexids); + Node_Copy(from, newnode, mergesortorder); + newnode->hashjoinoperator = from->hashjoinoperator; + newnode->cinfojoinid = listCopy(from->cinfojoinid); + + return newnode; } /* ---------------- - * CopyJoinMethodFields + * CopyJoinMethodFields * - * This function copies the fields of the JoinMethod node. It is used by - * all the copy functions for classes which inherit from JoinMethod. + * This function copies the fields of the JoinMethod node. It is used by + * all the copy functions for classes which inherit from JoinMethod. * ---------------- */ static void -CopyJoinMethodFields(JoinMethod *from, JoinMethod *newnode) +CopyJoinMethodFields(JoinMethod * from, JoinMethod * newnode) { - Node_Copy(from, newnode, jmkeys); - Node_Copy(from, newnode, clauses); - return; + Node_Copy(from, newnode, jmkeys); + Node_Copy(from, newnode, clauses); + return; } /* ---------------- - * _copyJoinMethod + * _copyJoinMethod * ---------------- */ static JoinMethod * -_copyJoinMethod(JoinMethod *from) +_copyJoinMethod(JoinMethod * from) { - JoinMethod *newnode = makeNode(JoinMethod); - - CopyJoinMethodFields(from, newnode); - - return newnode; + JoinMethod *newnode = makeNode(JoinMethod); + + CopyJoinMethodFields(from, newnode); + + return newnode; } /* ---------------- - * _copyHInfo + * _copyHInfo * ---------------- */ -static HInfo * -_copyHInfo(HInfo *from) +static HInfo * +_copyHInfo(HInfo * from) { - HInfo *newnode = makeNode(HInfo); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->hashop = from->hashop; - - return newnode; + HInfo *newnode = makeNode(HInfo); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->hashop = from->hashop; + + return newnode; } /* ---------------- - * _copyMInfo + * _copyMInfo * ---------------- */ -static MInfo * -_copyMInfo(MInfo *from) +static MInfo * +_copyMInfo(MInfo * from) { - MInfo *newnode = makeNode(MInfo); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, m_ordering); - - return newnode; + MInfo *newnode = makeNode(MInfo); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, m_ordering); + + return newnode; } /* ---------------- - * _copyJInfo + * _copyJInfo * ---------------- */ -static JInfo * -_copyJInfo(JInfo *from) +static JInfo * +_copyJInfo(JInfo * from) { - JInfo *newnode = makeNode(JInfo); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - newnode->otherrels = listCopy(from->otherrels); - Node_Copy(from, newnode, jinfoclauseinfo); - - newnode->mergesortable = from->mergesortable; - newnode->hashjoinable = from->hashjoinable; - newnode->inactive = from->inactive; - - return newnode; + JInfo *newnode = makeNode(JInfo); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->otherrels = listCopy(from->otherrels); + Node_Copy(from, newnode, jinfoclauseinfo); + + newnode->mergesortable = from->mergesortable; + newnode->hashjoinable = from->hashjoinable; + newnode->inactive = from->inactive; + + return newnode; } -static Iter * -_copyIter(Iter *from) +static Iter * +_copyIter(Iter * from) { - Iter *newnode = makeNode(Iter); + Iter *newnode = makeNode(Iter); + + Node_Copy(from, newnode, iterexpr); + newnode->itertype = from->itertype; - Node_Copy(from, newnode, iterexpr); - newnode->itertype = from->itertype; - - return newnode; + return newnode; } -static Stream * -_copyStream(Stream *from) +static Stream * +_copyStream(Stream * from) { - Stream *newnode = makeNode(Stream); - - newnode->pathptr = from->pathptr; - newnode->cinfo = from->cinfo; - newnode->clausetype = from->clausetype; - newnode->groupup = from->groupup; - newnode->groupcost = from->groupcost; - newnode->groupsel = from->groupsel; - newnode->upstream = (StreamPtr)NULL; /* only copy nodes downwards! */ - Node_Copy(from, newnode, downstream); - if (newnode->downstream) - ((Stream*)newnode->downstream)->upstream = (Stream*)newnode; - - return newnode; + Stream *newnode = makeNode(Stream); + + newnode->pathptr = from->pathptr; + newnode->cinfo = from->cinfo; + newnode->clausetype = from->clausetype; + newnode->groupup = from->groupup; + newnode->groupcost = from->groupcost; + newnode->groupsel = from->groupsel; + newnode->upstream = (StreamPtr) NULL; /* only copy nodes + * downwards! */ + Node_Copy(from, newnode, downstream); + if (newnode->downstream) + ((Stream *) newnode->downstream)->upstream = (Stream *) newnode; + + return newnode; } /* **************** - * parsenodes.h routines have no copy functions + * parsenodes.h routines have no copy functions * **************** */ static TargetEntry * -_copyTargetEntry(TargetEntry *from) +_copyTargetEntry(TargetEntry * from) { - TargetEntry *newnode = makeNode(TargetEntry); + TargetEntry *newnode = makeNode(TargetEntry); - Node_Copy(from, newnode, resdom); - Node_Copy(from, newnode, fjoin); - Node_Copy(from, newnode, expr); - return newnode; + Node_Copy(from, newnode, resdom); + Node_Copy(from, newnode, fjoin); + Node_Copy(from, newnode, expr); + return newnode; } static RangeTblEntry * -_copyRangeTblEntry(RangeTblEntry *from) +_copyRangeTblEntry(RangeTblEntry * from) { - RangeTblEntry *newnode = makeNode(RangeTblEntry); - - memcpy (newnode, from, sizeof (RangeTblEntry)); - if ( from->relname ) - newnode->relname = pstrdup (from->relname); - if ( from->refname ) - newnode->refname = pstrdup (from->refname); - if ( from->timeRange ) - { - newnode->timeRange = makeNode (TimeRange); - if ( from->timeRange->startDate ) - newnode->timeRange->startDate = pstrdup (from->timeRange->startDate); - else - newnode->timeRange->startDate = NULL; - if ( from->timeRange->endDate ) - newnode->timeRange->endDate = pstrdup (from->timeRange->endDate); - else - newnode->timeRange->endDate = NULL; - newnode->timeQual = makeTimeRange (newnode->timeRange->startDate, - newnode->timeRange->endDate, - ((newnode->timeRange->endDate == NULL) ? 0 : 1)); - } - - return newnode; + RangeTblEntry *newnode = makeNode(RangeTblEntry); + + memcpy(newnode, from, sizeof(RangeTblEntry)); + if (from->relname) + newnode->relname = pstrdup(from->relname); + if (from->refname) + newnode->refname = pstrdup(from->refname); + if (from->timeRange) + { + newnode->timeRange = makeNode(TimeRange); + if (from->timeRange->startDate) + newnode->timeRange->startDate = pstrdup(from->timeRange->startDate); + else + newnode->timeRange->startDate = NULL; + if (from->timeRange->endDate) + newnode->timeRange->endDate = pstrdup(from->timeRange->endDate); + else + newnode->timeRange->endDate = NULL; + newnode->timeQual = makeTimeRange(newnode->timeRange->startDate, + newnode->timeRange->endDate, + ((newnode->timeRange->endDate == NULL) ? 0 : 1)); + } + + return newnode; } static SortClause * -_copySortClause(SortClause *from) +_copySortClause(SortClause * from) { - SortClause *newnode = makeNode(SortClause); + SortClause *newnode = makeNode(SortClause); + + Node_Copy(from, newnode, resdom); + newnode->opoid = from->opoid; - Node_Copy(from, newnode, resdom); - newnode->opoid = from->opoid; - - return newnode; + return newnode; } static A_Const * -_copyAConst(A_Const *from) +_copyAConst(A_Const * from) { - A_Const *newnode = makeNode(A_Const); + A_Const *newnode = makeNode(A_Const); - newnode->val = *((Value *)(copyObject(&(from->val)))); - Node_Copy(from, newnode, typename); + newnode->val = *((Value *) (copyObject(&(from->val)))); + Node_Copy(from, newnode, typename); - return newnode; + return newnode; } static TypeName * -_copyTypeName(TypeName *from) +_copyTypeName(TypeName * from) { - TypeName *newnode = makeNode(TypeName); - - if(from->name) { - newnode->name = pstrdup(from->name); - } else { - from->name = (char *)0; - } - newnode->setof = from->setof; - Node_Copy(from, newnode, arrayBounds); - newnode->typlen = from->typlen; - - return newnode; + TypeName *newnode = makeNode(TypeName); + + if (from->name) + { + newnode->name = pstrdup(from->name); + } + else + { + from->name = (char *) 0; + } + newnode->setof = from->setof; + Node_Copy(from, newnode, arrayBounds); + newnode->typlen = from->typlen; + + return newnode; } -static Query * -_copyQuery(Query *from) +static Query * +_copyQuery(Query * from) { - Query *newnode = makeNode(Query); - - newnode->commandType = from->commandType; - newnode->resultRelation = from->resultRelation; - /* probably should dup this string instead of just pointing */ - /* to the old one --djm */ - if(from->into) { - newnode->into = pstrdup(from->into); - } else { - newnode->into = (char *)0; - } - newnode->isPortal = from->isPortal; - Node_Copy(from, newnode, rtable); - if (from->utilityStmt && nodeTag(from->utilityStmt) == T_NotifyStmt) { - NotifyStmt *from_notify = (NotifyStmt*)from->utilityStmt; - NotifyStmt *n = makeNode(NotifyStmt); - int length = strlen(from_notify->relname); - - n->relname = palloc(length + 1); - strcpy(n->relname,from_notify->relname); - newnode->utilityStmt = (Node*)n; - } - if (from->uniqueFlag) { - newnode->uniqueFlag = (char*)palloc(strlen(from->uniqueFlag)+1); - strcpy(newnode->uniqueFlag, from->uniqueFlag); - } - else - newnode->uniqueFlag = NULL; - Node_Copy(from, newnode, sortClause); - Node_Copy(from, newnode, targetList); - Node_Copy(from, newnode, qual); - - return newnode; + Query *newnode = makeNode(Query); + + newnode->commandType = from->commandType; + newnode->resultRelation = from->resultRelation; + /* probably should dup this string instead of just pointing */ + /* to the old one --djm */ + if (from->into) + { + newnode->into = pstrdup(from->into); + } + else + { + newnode->into = (char *) 0; + } + newnode->isPortal = from->isPortal; + Node_Copy(from, newnode, rtable); + if (from->utilityStmt && nodeTag(from->utilityStmt) == T_NotifyStmt) + { + NotifyStmt *from_notify = (NotifyStmt *) from->utilityStmt; + NotifyStmt *n = makeNode(NotifyStmt); + int length = strlen(from_notify->relname); + + n->relname = palloc(length + 1); + strcpy(n->relname, from_notify->relname); + newnode->utilityStmt = (Node *) n; + } + if (from->uniqueFlag) + { + newnode->uniqueFlag = (char *) palloc(strlen(from->uniqueFlag) + 1); + strcpy(newnode->uniqueFlag, from->uniqueFlag); + } + else + newnode->uniqueFlag = NULL; + Node_Copy(from, newnode, sortClause); + Node_Copy(from, newnode, targetList); + Node_Copy(from, newnode, qual); + + return newnode; } /* **************** - * mnodes.h routines have no copy functions + * mnodes.h routines have no copy functions * **************** */ /* **************************************************************** - * pg_list.h copy functions + * pg_list.h copy functions * **************************************************************** */ -static Value * -_copyValue(Value *from) +static Value * +_copyValue(Value * from) { - Value *newnode = makeNode(Value); - - newnode->type = from->type; - switch(from->type) { - case T_String: - newnode->val.str = pstrdup(from->val.str); - break; - case T_Integer: - newnode->val.ival = from->val.ival; - break; - case T_Float: - newnode->val.dval = from->val.dval; - break; - default: - break; - } - return newnode; + Value *newnode = makeNode(Value); + + newnode->type = from->type; + switch (from->type) + { + case T_String: + newnode->val.str = pstrdup(from->val.str); + break; + case T_Integer: + newnode->val.ival = from->val.ival; + break; + case T_Float: + newnode->val.dval = from->val.dval; + break; + default: + break; + } + return newnode; } /* ---------------- - * copyObject returns a copy of the node or list. If it is a list, it - * recursively copies its items. + * copyObject returns a copy of the node or list. If it is a list, it + * recursively copies its items. * ---------------- */ -void * +void * copyObject(void *from) { - void *retval; - - if (from==NULL) - return NULL; - switch(nodeTag(from)) { - /* - * PLAN NODES - */ - case T_Plan: - retval = _copyPlan(from); - break; - case T_Existential: - retval = _copyExistential(from); - break; - case T_Result: - retval = _copyResult(from); - break; - case T_Append: - retval = _copyAppend(from); - break; - case T_Scan: - retval = _copyScan(from); - break; - case T_SeqScan: - retval = _copySeqScan(from); - break; - case T_IndexScan: - retval = _copyIndexScan(from); - break; - case T_Join: - retval = _copyJoin(from); - break; - case T_NestLoop: - retval = _copyNestLoop(from); - break; - case T_MergeJoin: - retval = _copyMergeJoin(from); - break; - case T_HashJoin: - retval = _copyHashJoin(from); - break; - case T_Temp: - retval = _copyTemp(from); - break; - case T_Material: - retval = _copyMaterial(from); - break; - case T_Sort: - retval = _copySort(from); - break; - case T_Agg: - retval = _copyAgg(from); - break; - case T_Unique: - retval = _copyUnique(from); - break; - case T_Hash: - retval = _copyHash(from); - break; - - /* - * PRIMITIVE NODES - */ - case T_Resdom: - retval = _copyResdom(from); - break; - case T_Fjoin: - retval = _copyFjoin(from); - break; - case T_Expr: - retval = _copyExpr(from); - break; - case T_Var: - retval = _copyVar(from); - break; - case T_Oper: - retval = _copyOper(from); - break; - case T_Const: - retval = _copyConst(from); - break; - case T_Param: - retval = _copyParam(from); - break; - case T_Func: - retval = _copyFunc(from); - break; - case T_Array: - retval = _copyArray(from); - break; - case T_ArrayRef: - retval = _copyArrayRef(from); - break; - case T_Aggreg: - retval = _copyAggreg(from); - break; - /* - * RELATION NODES - */ - case T_Rel: - retval = _copyRel(from); - break; - case T_Path: - retval = _copyPath(from); - break; - case T_IndexPath: - retval = _copyIndexPath(from); - break; - case T_JoinPath: - retval = _copyJoinPath(from); - break; - case T_MergePath: - retval = _copyMergePath(from); - break; - case T_HashPath: - retval = _copyHashPath(from); - break; - case T_OrderKey: - retval = _copyOrderKey(from); - break; - case T_JoinKey: - retval = _copyJoinKey(from); - break; - case T_MergeOrder: - retval = _copyMergeOrder(from); - break; - case T_CInfo: - retval = _copyCInfo(from); - break; - case T_JoinMethod: - retval = _copyJoinMethod(from); - break; - case T_HInfo: - retval = _copyHInfo(from); - break; - case T_MInfo: - retval = _copyMInfo(from); - break; - case T_JInfo: - retval = _copyJInfo(from); - break; - case T_Iter: - retval = _copyIter(from); - break; - case T_Stream: - retval = _copyStream(from); - break; + void *retval; - /* - * PARSE NODES - */ - case T_Query: - retval = _copyQuery(from); - break; - case T_TargetEntry: - retval = _copyTargetEntry(from); - break; - case T_RangeTblEntry: - retval = _copyRangeTblEntry(from); - break; - case T_SortClause: - retval = _copySortClause(from); - break; - case T_A_Const: - retval = _copyAConst(from); - break; - case T_TypeName: - retval = _copyTypeName(from); - break; - - /* - * VALUE NODES - */ - case T_Integer: case T_String: case T_Float: - retval = _copyValue(from); - break; - case T_List: + if (from == NULL) + return NULL; + switch (nodeTag(from)) { - List *list=from, *l; - List *newlist = NIL, *nl=NIL; - foreach(l, list) { - if (newlist==NIL) { - newlist = nl = lcons(copyObject(lfirst(l)),NIL); - }else { - lnext(nl) = lcons(copyObject(lfirst(l)),NIL); - nl = lnext(nl); + + /* + * PLAN NODES + */ + case T_Plan: + retval = _copyPlan(from); + break; + case T_Existential: + retval = _copyExistential(from); + break; + case T_Result: + retval = _copyResult(from); + break; + case T_Append: + retval = _copyAppend(from); + break; + case T_Scan: + retval = _copyScan(from); + break; + case T_SeqScan: + retval = _copySeqScan(from); + break; + case T_IndexScan: + retval = _copyIndexScan(from); + break; + case T_Join: + retval = _copyJoin(from); + break; + case T_NestLoop: + retval = _copyNestLoop(from); + break; + case T_MergeJoin: + retval = _copyMergeJoin(from); + break; + case T_HashJoin: + retval = _copyHashJoin(from); + break; + case T_Temp: + retval = _copyTemp(from); + break; + case T_Material: + retval = _copyMaterial(from); + break; + case T_Sort: + retval = _copySort(from); + break; + case T_Agg: + retval = _copyAgg(from); + break; + case T_Unique: + retval = _copyUnique(from); + break; + case T_Hash: + retval = _copyHash(from); + break; + + /* + * PRIMITIVE NODES + */ + case T_Resdom: + retval = _copyResdom(from); + break; + case T_Fjoin: + retval = _copyFjoin(from); + break; + case T_Expr: + retval = _copyExpr(from); + break; + case T_Var: + retval = _copyVar(from); + break; + case T_Oper: + retval = _copyOper(from); + break; + case T_Const: + retval = _copyConst(from); + break; + case T_Param: + retval = _copyParam(from); + break; + case T_Func: + retval = _copyFunc(from); + break; + case T_Array: + retval = _copyArray(from); + break; + case T_ArrayRef: + retval = _copyArrayRef(from); + break; + case T_Aggreg: + retval = _copyAggreg(from); + break; + + /* + * RELATION NODES + */ + case T_Rel: + retval = _copyRel(from); + break; + case T_Path: + retval = _copyPath(from); + break; + case T_IndexPath: + retval = _copyIndexPath(from); + break; + case T_JoinPath: + retval = _copyJoinPath(from); + break; + case T_MergePath: + retval = _copyMergePath(from); + break; + case T_HashPath: + retval = _copyHashPath(from); + break; + case T_OrderKey: + retval = _copyOrderKey(from); + break; + case T_JoinKey: + retval = _copyJoinKey(from); + break; + case T_MergeOrder: + retval = _copyMergeOrder(from); + break; + case T_CInfo: + retval = _copyCInfo(from); + break; + case T_JoinMethod: + retval = _copyJoinMethod(from); + break; + case T_HInfo: + retval = _copyHInfo(from); + break; + case T_MInfo: + retval = _copyMInfo(from); + break; + case T_JInfo: + retval = _copyJInfo(from); + break; + case T_Iter: + retval = _copyIter(from); + break; + case T_Stream: + retval = _copyStream(from); + break; + + /* + * PARSE NODES + */ + case T_Query: + retval = _copyQuery(from); + break; + case T_TargetEntry: + retval = _copyTargetEntry(from); + break; + case T_RangeTblEntry: + retval = _copyRangeTblEntry(from); + break; + case T_SortClause: + retval = _copySortClause(from); + break; + case T_A_Const: + retval = _copyAConst(from); + break; + case T_TypeName: + retval = _copyTypeName(from); + break; + + /* + * VALUE NODES + */ + case T_Integer: + case T_String: + case T_Float: + retval = _copyValue(from); + break; + case T_List: + { + List *list = from, + *l; + List *newlist = NIL, + *nl = NIL; + + foreach(l, list) + { + if (newlist == NIL) + { + newlist = nl = lcons(copyObject(lfirst(l)), NIL); + } + else + { + lnext(nl) = lcons(copyObject(lfirst(l)), NIL); + nl = lnext(nl); + } + } + retval = newlist; } - } - retval = newlist; + break; + default: + elog(NOTICE, "copyObject: don't know how to copy %d", nodeTag(from)); + retval = from; + break; } - break; - default: - elog(NOTICE, "copyObject: don't know how to copy %d", nodeTag(from)); - retval = from; - break; - } - return retval; + return retval; } - diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 4a8a072d33f..792c8783f99 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * equalfuncs.c-- - * equal functions to compare the nodes + * equal functions to compare the nodes * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.6 1997/08/19 21:31:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.7 1997/09/07 04:42:44 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,700 +26,717 @@ #include "utils/elog.h" #include "storage/itemptr.h" -static bool equali(List *a, List *b); +static bool equali(List * a, List * b); /* - * Stuff from primnodes.h + * Stuff from primnodes.h */ /* - * Resdom is a subclass of Node. + * Resdom is a subclass of Node. */ -static bool -_equalResdom(Resdom *a, Resdom *b) +static bool +_equalResdom(Resdom * a, Resdom * b) { - if (a->resno != b->resno) - return (false); - if (a->restype != b->restype) - return (false); - if (a->reslen != b->reslen) - return (false); - if (strcmp(a->resname, b->resname) != 0) - return (false); - if (a->reskey != b->reskey) - return (false); - if (a->reskeyop != b->reskeyop) - return (false); - - return (true); + if (a->resno != b->resno) + return (false); + if (a->restype != b->restype) + return (false); + if (a->reslen != b->reslen) + return (false); + if (strcmp(a->resname, b->resname) != 0) + return (false); + if (a->reskey != b->reskey) + return (false); + if (a->reskeyop != b->reskeyop) + return (false); + + return (true); } -static bool -_equalFjoin(Fjoin *a, Fjoin *b) +static bool +_equalFjoin(Fjoin * a, Fjoin * b) { - int nNodes; - - if (a->fj_initialized != b->fj_initialized) - return (false); - if (a->fj_nNodes != b->fj_nNodes) - return (false); - if (!equal(a->fj_innerNode, b->fj_innerNode)) - return (false); - - nNodes = a->fj_nNodes; - if (memcmp(a->fj_results, b->fj_results, nNodes*sizeof(Datum)) != 0) - return (false); - if (memcmp(a->fj_alwaysDone, b->fj_alwaysDone, nNodes*sizeof(bool)) != 0) - return (false); - - return(true); + int nNodes; + + if (a->fj_initialized != b->fj_initialized) + return (false); + if (a->fj_nNodes != b->fj_nNodes) + return (false); + if (!equal(a->fj_innerNode, b->fj_innerNode)) + return (false); + + nNodes = a->fj_nNodes; + if (memcmp(a->fj_results, b->fj_results, nNodes * sizeof(Datum)) != 0) + return (false); + if (memcmp(a->fj_alwaysDone, b->fj_alwaysDone, nNodes * sizeof(bool)) != 0) + return (false); + + return (true); } /* - * Expr is a subclass of Node. + * Expr is a subclass of Node. */ -static bool -_equalExpr(Expr *a, Expr *b) +static bool +_equalExpr(Expr * a, Expr * b) { - if (a->opType != b->opType) - return (false); - if (!equal(a->oper, b->oper)) - return (false); - if (!equal(a->args, b->args)) - return (false); - - return (true); + if (a->opType != b->opType) + return (false); + if (!equal(a->oper, b->oper)) + return (false); + if (!equal(a->args, b->args)) + return (false); + + return (true); } -static bool -_equalIter(Iter *a, Iter *b) +static bool +_equalIter(Iter * a, Iter * b) { - return (equal(a->iterexpr, b->iterexpr)); + return (equal(a->iterexpr, b->iterexpr)); } -static bool -_equalStream(Stream *a, Stream *b) +static bool +_equalStream(Stream * a, Stream * b) { - if (a->clausetype != b->clausetype) - return(false); - if (a->groupup != b->groupup) - return(false); - if (a->groupcost != b->groupcost) - return(false); - if (a->groupsel != b->groupsel) - return(false); - if (!equal(a->pathptr, b->pathptr)) - return(false); - if (!equal(a->cinfo, b->cinfo)) - return(false); - if (!equal(a->upstream, b->upstream)) - return(false); - return(equal(a->downstream, b->downstream)); + if (a->clausetype != b->clausetype) + return (false); + if (a->groupup != b->groupup) + return (false); + if (a->groupcost != b->groupcost) + return (false); + if (a->groupsel != b->groupsel) + return (false); + if (!equal(a->pathptr, b->pathptr)) + return (false); + if (!equal(a->cinfo, b->cinfo)) + return (false); + if (!equal(a->upstream, b->upstream)) + return (false); + return (equal(a->downstream, b->downstream)); } /* - * Var is a subclass of Expr. + * Var is a subclass of Expr. */ -static bool -_equalVar(Var *a, Var *b) +static bool +_equalVar(Var * a, Var * b) { - if (a->varno != b->varno) - return (false); - if (a->varattno != b->varattno) - return (false); - if (a->vartype != b->vartype) - return (false); - if (a->varnoold != b->varnoold) - return (false); - if (a->varoattno != b->varoattno) - return (false); - - return (true); + if (a->varno != b->varno) + return (false); + if (a->varattno != b->varattno) + return (false); + if (a->vartype != b->vartype) + return (false); + if (a->varnoold != b->varnoold) + return (false); + if (a->varoattno != b->varoattno) + return (false); + + return (true); } -static bool -_equalArray(Array *a, Array *b) +static bool +_equalArray(Array * a, Array * b) { - if (a->arrayelemtype != b->arrayelemtype) - return (false); - if (a->arrayndim != b->arrayndim) - return (false); - if (a->arraylow.indx[0] != b->arraylow.indx[0]) - return (false); - if (a->arrayhigh.indx[0] != b->arrayhigh.indx[0]) - return (false); - if (a->arraylen != b->arraylen) - return (false); - return(TRUE); + if (a->arrayelemtype != b->arrayelemtype) + return (false); + if (a->arrayndim != b->arrayndim) + return (false); + if (a->arraylow.indx[0] != b->arraylow.indx[0]) + return (false); + if (a->arrayhigh.indx[0] != b->arrayhigh.indx[0]) + return (false); + if (a->arraylen != b->arraylen) + return (false); + return (TRUE); } -static bool -_equalArrayRef(ArrayRef *a, ArrayRef *b) +static bool +_equalArrayRef(ArrayRef * a, ArrayRef * b) { - if (a->refelemtype != b->refelemtype) - return (false); - if (a->refattrlength != b->refattrlength) - return (false); - if (a->refelemlength != b->refelemlength) - return (false); - if (a->refelembyval != b->refelembyval) - return (false); - if (!equal(a->refupperindexpr, b->refupperindexpr)) - return (false); - if (!equal(a->reflowerindexpr, b->reflowerindexpr)) - return (false); - if (!equal(a->refexpr, b->refexpr)) - return (false); - return (equal(a->refassgnexpr, b->refassgnexpr)); + if (a->refelemtype != b->refelemtype) + return (false); + if (a->refattrlength != b->refattrlength) + return (false); + if (a->refelemlength != b->refelemlength) + return (false); + if (a->refelembyval != b->refelembyval) + return (false); + if (!equal(a->refupperindexpr, b->refupperindexpr)) + return (false); + if (!equal(a->reflowerindexpr, b->reflowerindexpr)) + return (false); + if (!equal(a->refexpr, b->refexpr)) + return (false); + return (equal(a->refassgnexpr, b->refassgnexpr)); } /* - * Oper is a subclass of Expr. + * Oper is a subclass of Expr. */ -static bool -_equalOper(Oper *a, Oper *b) +static bool +_equalOper(Oper * a, Oper * b) { - if (a->opno != b->opno) - return (false); - if (a->opresulttype != b->opresulttype) - return (false); - - return (true); + if (a->opno != b->opno) + return (false); + if (a->opresulttype != b->opresulttype) + return (false); + + return (true); } /* - * Const is a subclass of Expr. + * Const is a subclass of Expr. */ -static bool -_equalConst(Const *a, Const *b) +static bool +_equalConst(Const * a, Const * b) { - /* - ** this function used to do a pointer compare on a and b. That's - ** ridiculous. -- JMH, 7/11/92 - */ - if (a->consttype != b->consttype) - return(false); - if (a->constlen != b->constlen) - return(false); - if (a->constisnull != b->constisnull) - return(false); - if (a->constbyval != b->constbyval) - return(false); - return(datumIsEqual(a->constvalue, b->constvalue, - a->consttype, a->constbyval, a->constlen)); + + /* + * * this function used to do a pointer compare on a and b. That's * + * ridiculous. -- JMH, 7/11/92 + */ + if (a->consttype != b->consttype) + return (false); + if (a->constlen != b->constlen) + return (false); + if (a->constisnull != b->constisnull) + return (false); + if (a->constbyval != b->constbyval) + return (false); + return (datumIsEqual(a->constvalue, b->constvalue, + a->consttype, a->constbyval, a->constlen)); } /* - * Param is a subclass of Expr. + * Param is a subclass of Expr. */ -static bool -_equalParam(Param *a, Param *b) +static bool +_equalParam(Param * a, Param * b) { - if (a->paramkind != b->paramkind) - return (false); - if (a->paramtype != b->paramtype) - return (false); - if (!equal(a->param_tlist, b->param_tlist)) - return (false); - - switch (a->paramkind) { - case PARAM_NAMED: - case PARAM_NEW: - case PARAM_OLD: - if (strcmp(a->paramname, b->paramname) != 0) - return (false); - break; - case PARAM_NUM: - if (a->paramid != b->paramid) - return (false); - break; - case PARAM_INVALID: - /* - * XXX: Hmmm... What are we supposed to return - * in this case ?? - */ - return(true); - break; - default: - elog(WARN, "_equalParam: Invalid paramkind value: %d", - a->paramkind); - } - - return (true); + if (a->paramkind != b->paramkind) + return (false); + if (a->paramtype != b->paramtype) + return (false); + if (!equal(a->param_tlist, b->param_tlist)) + return (false); + + switch (a->paramkind) + { + case PARAM_NAMED: + case PARAM_NEW: + case PARAM_OLD: + if (strcmp(a->paramname, b->paramname) != 0) + return (false); + break; + case PARAM_NUM: + if (a->paramid != b->paramid) + return (false); + break; + case PARAM_INVALID: + + /* + * XXX: Hmmm... What are we supposed to return in this case ?? + */ + return (true); + break; + default: + elog(WARN, "_equalParam: Invalid paramkind value: %d", + a->paramkind); + } + + return (true); } /* - * Func is a subclass of Expr. + * Func is a subclass of Expr. */ -static bool -_equalFunc(Func *a, Func *b) +static bool +_equalFunc(Func * a, Func * b) { - if (a->funcid != b->funcid) - return (false); - if (a->functype != b->functype) - return (false); - if (a->funcisindex != b->funcisindex) - return (false); - if (a->funcsize != b->funcsize) - return (false); - if (!equal(a->func_tlist, b->func_tlist)) - return (false); - if (!equal(a->func_planlist, b->func_planlist)) - return (false); - - return (true); + if (a->funcid != b->funcid) + return (false); + if (a->functype != b->functype) + return (false); + if (a->funcisindex != b->funcisindex) + return (false); + if (a->funcsize != b->funcsize) + return (false); + if (!equal(a->func_tlist, b->func_tlist)) + return (false); + if (!equal(a->func_planlist, b->func_planlist)) + return (false); + + return (true); } /* * CInfo is a subclass of Node. */ -static bool -_equalCInfo(CInfo *a, CInfo *b) +static bool +_equalCInfo(CInfo * a, CInfo * b) { - Assert(IsA(a,CInfo)); - Assert(IsA(b,CInfo)); - - if (!equal(a->clause, b->clause)) - return(false); - if (a->selectivity != b->selectivity) - return(false); - if (a->notclause != b->notclause) - return(false); + Assert(IsA(a, CInfo)); + Assert(IsA(b, CInfo)); + + if (!equal(a->clause, b->clause)) + return (false); + if (a->selectivity != b->selectivity) + return (false); + if (a->notclause != b->notclause) + return (false); #ifdef EqualMergeOrderExists - if (!EqualMergeOrder(a->mergesortorder,b->mergesortorder)) - return(false); + if (!EqualMergeOrder(a->mergesortorder, b->mergesortorder)) + return (false); #endif - if(a->hashjoinoperator != b->hashjoinoperator) - return(false); - return(equal((a->indexids), - (b->indexids))); + if (a->hashjoinoperator != b->hashjoinoperator) + return (false); + return (equal((a->indexids), + (b->indexids))); } -static bool -_equalJoinMethod(JoinMethod *a, JoinMethod *b) +static bool +_equalJoinMethod(JoinMethod * a, JoinMethod * b) { - Assert(IsA(a,JoinMethod)); - Assert(IsA(b,JoinMethod)); - - if (!equal((a->jmkeys), - (b->jmkeys))) - return(false); - if (!equal((a->clauses), - (b->clauses))) - return(false); - return(true); + Assert(IsA(a, JoinMethod)); + Assert(IsA(b, JoinMethod)); + + if (!equal((a->jmkeys), + (b->jmkeys))) + return (false); + if (!equal((a->clauses), + (b->clauses))) + return (false); + return (true); } -static bool -_equalPath(Path *a, Path *b) +static bool +_equalPath(Path * a, Path * b) { - if (a->pathtype != b->pathtype) - return(false); - if (a->parent != b->parent) - return(false); - /* - if (a->path_cost != b->path_cost) - return(false); - */ - if (a->p_ordering.ordtype == SORTOP_ORDER) { - int i = 0; - if (a->p_ordering.ord.sortop==NULL || - b->p_ordering.ord.sortop==NULL) { - - if (a->p_ordering.ord.sortop != b->p_ordering.ord.sortop) - return false; - } else { - while(a->p_ordering.ord.sortop[i]!=0 && - b->p_ordering.ord.sortop[i]!=0) { - if (a->p_ordering.ord.sortop[i] != b->p_ordering.ord.sortop[i]) - return false; - i++; - } - if (a->p_ordering.ord.sortop[i]!=0 || - b->p_ordering.ord.sortop[i]!=0) - return false; + if (a->pathtype != b->pathtype) + return (false); + if (a->parent != b->parent) + return (false); + + /* + * if (a->path_cost != b->path_cost) return(false); + */ + if (a->p_ordering.ordtype == SORTOP_ORDER) + { + int i = 0; + + if (a->p_ordering.ord.sortop == NULL || + b->p_ordering.ord.sortop == NULL) + { + + if (a->p_ordering.ord.sortop != b->p_ordering.ord.sortop) + return false; + } + else + { + while (a->p_ordering.ord.sortop[i] != 0 && + b->p_ordering.ord.sortop[i] != 0) + { + if (a->p_ordering.ord.sortop[i] != b->p_ordering.ord.sortop[i]) + return false; + i++; + } + if (a->p_ordering.ord.sortop[i] != 0 || + b->p_ordering.ord.sortop[i] != 0) + return false; + } + } + else + { + if (!equal((a->p_ordering.ord.merge), + (b->p_ordering.ord.merge))) + return (false); } - } else { - if (!equal((a->p_ordering.ord.merge), - (b->p_ordering.ord.merge))) - return(false); - } - if (!equal((a->keys), - (b->keys))) - return(false); - /* - if (a->outerjoincost != b->outerjoincost) - return(false); - */ - if (!equali((a->joinid), - (b->joinid))) - return(false); - return(true); + if (!equal((a->keys), + (b->keys))) + return (false); + + /* + * if (a->outerjoincost != b->outerjoincost) return(false); + */ + if (!equali((a->joinid), + (b->joinid))) + return (false); + return (true); } -static bool -_equalIndexPath(IndexPath *a, IndexPath *b) +static bool +_equalIndexPath(IndexPath * a, IndexPath * b) { - if (!_equalPath((Path*)a,(Path*)b)) - return(false); - if (!equali((a->indexid), (b->indexid))) - return(false); - if (!equal((a->indexqual), (b->indexqual))) - return(false); - return(true); + if (!_equalPath((Path *) a, (Path *) b)) + return (false); + if (!equali((a->indexid), (b->indexid))) + return (false); + if (!equal((a->indexqual), (b->indexqual))) + return (false); + return (true); } -static bool -_equalJoinPath(JoinPath *a,JoinPath *b) +static bool +_equalJoinPath(JoinPath * a, JoinPath * b) { - Assert(IsA_JoinPath(a)); - Assert(IsA_JoinPath(b)); - - if (!_equalPath((Path*)a,(Path*)b)) - return(false); - if (!equal((a->pathclauseinfo), (b->pathclauseinfo))) - return(false); - if (!equal((a->outerjoinpath), (b->outerjoinpath))) - return(false); - if (!equal((a->innerjoinpath), (b->innerjoinpath))) - return(false); - return(true); + Assert(IsA_JoinPath(a)); + Assert(IsA_JoinPath(b)); + + if (!_equalPath((Path *) a, (Path *) b)) + return (false); + if (!equal((a->pathclauseinfo), (b->pathclauseinfo))) + return (false); + if (!equal((a->outerjoinpath), (b->outerjoinpath))) + return (false); + if (!equal((a->innerjoinpath), (b->innerjoinpath))) + return (false); + return (true); } -static bool -_equalMergePath(MergePath *a, MergePath *b) +static bool +_equalMergePath(MergePath * a, MergePath * b) { - Assert(IsA(a,MergePath)); - Assert(IsA(b,MergePath)); - - if (!_equalJoinPath((JoinPath*)a,(JoinPath*)b)) - return(false); - if (!equal((a->path_mergeclauses), (b->path_mergeclauses))) - return(false); - if (!equal((a->outersortkeys), (b->outersortkeys))) - return(false); - if (!equal((a->innersortkeys), (b->innersortkeys))) - return(false); - return(true); + Assert(IsA(a, MergePath)); + Assert(IsA(b, MergePath)); + + if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b)) + return (false); + if (!equal((a->path_mergeclauses), (b->path_mergeclauses))) + return (false); + if (!equal((a->outersortkeys), (b->outersortkeys))) + return (false); + if (!equal((a->innersortkeys), (b->innersortkeys))) + return (false); + return (true); } -static bool -_equalHashPath(HashPath *a, HashPath *b) +static bool +_equalHashPath(HashPath * a, HashPath * b) { - Assert(IsA(a,HashPath)); - Assert(IsA(b,HashPath)); - - if (!_equalJoinPath((JoinPath*)a,(JoinPath*)b)) - return(false); - if (!equal((a->path_hashclauses), (b->path_hashclauses))) - return(false); - if (!equal((a->outerhashkeys), (b->outerhashkeys))) - return(false); - if (!equal((a->innerhashkeys), (b->innerhashkeys))) - return(false); - return(true); + Assert(IsA(a, HashPath)); + Assert(IsA(b, HashPath)); + + if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b)) + return (false); + if (!equal((a->path_hashclauses), (b->path_hashclauses))) + return (false); + if (!equal((a->outerhashkeys), (b->outerhashkeys))) + return (false); + if (!equal((a->innerhashkeys), (b->innerhashkeys))) + return (false); + return (true); } -static bool -_equalJoinKey(JoinKey *a, JoinKey *b) +static bool +_equalJoinKey(JoinKey * a, JoinKey * b) { - Assert(IsA(a,JoinKey)); - Assert(IsA(b,JoinKey)); - - if (!equal((a->outer),(b->outer))) - return(false); - if (!equal((a->inner),(b->inner))) - return(false); - return(true); + Assert(IsA(a, JoinKey)); + Assert(IsA(b, JoinKey)); + + if (!equal((a->outer), (b->outer))) + return (false); + if (!equal((a->inner), (b->inner))) + return (false); + return (true); } -static bool -_equalMergeOrder(MergeOrder *a, MergeOrder *b) +static bool +_equalMergeOrder(MergeOrder * a, MergeOrder * b) { - if (a == (MergeOrder*)NULL && b == (MergeOrder*)NULL) - return(true); - Assert(IsA(a,MergeOrder)); - Assert(IsA(b,MergeOrder)); - - if (a->join_operator != b->join_operator) - return(false); - if (a->left_operator != b->left_operator) - return(false); - if (a->right_operator != b->right_operator) - return(false); - if (a->left_type != b->left_type) - return(false); - if (a->right_type != b->right_type) - return(false); - return(true); + if (a == (MergeOrder *) NULL && b == (MergeOrder *) NULL) + return (true); + Assert(IsA(a, MergeOrder)); + Assert(IsA(b, MergeOrder)); + + if (a->join_operator != b->join_operator) + return (false); + if (a->left_operator != b->left_operator) + return (false); + if (a->right_operator != b->right_operator) + return (false); + if (a->left_type != b->left_type) + return (false); + if (a->right_type != b->right_type) + return (false); + return (true); } -static bool -_equalHInfo(HInfo *a, HInfo *b) +static bool +_equalHInfo(HInfo * a, HInfo * b) { - Assert(IsA(a,HInfo)); - Assert(IsA(b,HInfo)); - - if (a->hashop != b->hashop) - return(false); - return(true); + Assert(IsA(a, HInfo)); + Assert(IsA(b, HInfo)); + + if (a->hashop != b->hashop) + return (false); + return (true); } -/* XXX This equality function is a quick hack, should be - * fixed to compare all fields. +/* XXX This equality function is a quick hack, should be + * fixed to compare all fields. */ -static bool -_equalIndexScan(IndexScan *a, IndexScan *b) +static bool +_equalIndexScan(IndexScan * a, IndexScan * b) { - Assert(IsA(a,IndexScan)); - Assert(IsA(b,IndexScan)); - - /* - if(a->scan.plan.cost != b->scan.plan.cost) - return(false); - */ - - if (!equal((a->indxqual),(b->indxqual))) - return(false); - - if (a->scan.scanrelid != b->scan.scanrelid) - return(false); - - if (!equali((a->indxid),(b->indxid))) - return(false); - return(true); + Assert(IsA(a, IndexScan)); + Assert(IsA(b, IndexScan)); + + /* + * if(a->scan.plan.cost != b->scan.plan.cost) return(false); + */ + + if (!equal((a->indxqual), (b->indxqual))) + return (false); + + if (a->scan.scanrelid != b->scan.scanrelid) + return (false); + + if (!equali((a->indxid), (b->indxid))) + return (false); + return (true); } -static bool -_equalJInfo(JInfo *a, JInfo *b) +static bool +_equalJInfo(JInfo * a, JInfo * b) { - Assert(IsA(a,JInfo)); - Assert(IsA(b,JInfo)); - if (!equal((a->otherrels),(b->otherrels))) - return(false); - if (!equal((a->jinfoclauseinfo),(b->jinfoclauseinfo))) - return(false); - if (a->mergesortable != b->mergesortable) - return(false); - if (a->hashjoinable != b->hashjoinable) - return(false); - return(true); + Assert(IsA(a, JInfo)); + Assert(IsA(b, JInfo)); + if (!equal((a->otherrels), (b->otherrels))) + return (false); + if (!equal((a->jinfoclauseinfo), (b->jinfoclauseinfo))) + return (false); + if (a->mergesortable != b->mergesortable) + return (false); + if (a->hashjoinable != b->hashjoinable) + return (false); + return (true); } /* - * Stuff from execnodes.h + * Stuff from execnodes.h */ /* - * EState is a subclass of Node. + * EState is a subclass of Node. */ -static bool -_equalEState(EState *a, EState *b) +static bool +_equalEState(EState * a, EState * b) { - if (a->es_direction != b->es_direction) - return (false); - - if (!equal(a->es_range_table, b->es_range_table)) - return (false); - - if (a->es_result_relation_info != b->es_result_relation_info) - return (false); - - return (true); + if (a->es_direction != b->es_direction) + return (false); + + if (!equal(a->es_range_table, b->es_range_table)) + return (false); + + if (a->es_result_relation_info != b->es_result_relation_info) + return (false); + + return (true); } -static bool -_equalTargetEntry(TargetEntry *a, TargetEntry *b) +static bool +_equalTargetEntry(TargetEntry * a, TargetEntry * b) { - if (!equal(a->resdom,b->resdom)) - return(false); - if (!equal(a->fjoin,b->fjoin)) - return(false); - if (!equal(a->expr,b->expr)) - return(false); - - return(true); + if (!equal(a->resdom, b->resdom)) + return (false); + if (!equal(a->fjoin, b->fjoin)) + return (false); + if (!equal(a->expr, b->expr)) + return (false); + + return (true); } /* - * equal -- are two lists equal? + * equal -- are two lists equal? * - * This is a comparison by value. It would be simpler to write it - * to be recursive, but it should run faster if we iterate. + * This is a comparison by value. It would be simpler to write it + * to be recursive, but it should run faster if we iterate. */ -static bool -_equalValue(Value *a, Value *b) +static bool +_equalValue(Value * a, Value * b) { - if (a->type != b->type) - return (false); - - switch(a->type) { - case T_String: - return strcmp(a->val.str, b->val.str); - case T_Integer: - return (a->val.ival==b->val.ival); - case T_Float: - return (a->val.dval==b->val.dval); - default: - break; - } - - return (true); + if (a->type != b->type) + return (false); + + switch (a->type) + { + case T_String: + return strcmp(a->val.str, b->val.str); + case T_Integer: + return (a->val.ival == b->val.ival); + case T_Float: + return (a->val.dval == b->val.dval); + default: + break; + } + + return (true); } /* * equal-- - * returns whether two nodes are equal + * returns whether two nodes are equal */ bool equal(void *a, void *b) { - bool retval=false; - - if (a == b) - return(true); - /* - * note that a!=b, so only one of them can be NULL - */ - if (a==NULL || b==NULL) - return (false); - /* - * are they the same type of nodes? - */ - if (nodeTag(a)!=nodeTag(b)) - return (false); - - switch(nodeTag(a)) { - case T_Resdom: - retval = _equalResdom(a, b); - break; - case T_Fjoin: - retval = _equalFjoin(a, b); - break; - case T_Expr: - retval = _equalExpr(a, b); - break; - case T_TargetEntry: - retval = _equalTargetEntry(a,b); - break; - case T_Iter: - retval = _equalIter(a, b); - break; - case T_Stream: - retval = _equalStream(a, b); - break; - case T_Var: - retval = _equalVar(a, b); - break; - case T_Array: - retval = _equalArray(a, b); - break; - case T_ArrayRef: - retval = _equalArrayRef(a, b); - break; - case T_Oper: - retval = _equalOper(a, b); - break; - case T_Const: - retval = _equalConst(a, b); - break; - case T_Param: - retval = _equalParam(a, b); - break; - case T_Func: - retval = _equalFunc(a, b); - break; - case T_CInfo: - retval = _equalCInfo(a, b); - break; - case T_JoinMethod: - retval = _equalJoinMethod(a, b); - break; - case T_Path: - retval = _equalPath(a, b); - break; - case T_IndexPath: - retval = _equalIndexPath(a, b); - break; - case T_JoinPath: - retval = _equalJoinPath(a, b); - break; - case T_MergePath: - retval = _equalMergePath(a, b); - break; - case T_HashPath: - retval = _equalHashPath(a, b); - break; - case T_JoinKey: - retval = _equalJoinKey(a, b); - break; - case T_MergeOrder: - retval = _equalMergeOrder(a, b); - break; - case T_HInfo: - retval = _equalHInfo(a, b); - break; - case T_IndexScan: - retval = _equalIndexScan(a, b); - break; - case T_JInfo: - retval = _equalJInfo(a, b); - break; - case T_EState: - retval = _equalEState(a, b); - break; - case T_Integer: case T_String: case T_Float: - retval = _equalValue(a, b); - break; - case T_List: - { - List *la = (List*)a; - List *lb = (List*)b; - List *l; + bool retval = false; - if (a==NULL && b==NULL) + if (a == b) return (true); - if (length(a)!=length(b)) + + /* + * note that a!=b, so only one of them can be NULL + */ + if (a == NULL || b == NULL) return (false); - foreach(l, la) { - if (!equal(lfirst(l), lfirst(lb))) - return (false); - lb = lnext(lb); - } - retval = true; + + /* + * are they the same type of nodes? + */ + if (nodeTag(a) != nodeTag(b)) + return (false); + + switch (nodeTag(a)) + { + case T_Resdom: + retval = _equalResdom(a, b); + break; + case T_Fjoin: + retval = _equalFjoin(a, b); + break; + case T_Expr: + retval = _equalExpr(a, b); + break; + case T_TargetEntry: + retval = _equalTargetEntry(a, b); + break; + case T_Iter: + retval = _equalIter(a, b); + break; + case T_Stream: + retval = _equalStream(a, b); + break; + case T_Var: + retval = _equalVar(a, b); + break; + case T_Array: + retval = _equalArray(a, b); + break; + case T_ArrayRef: + retval = _equalArrayRef(a, b); + break; + case T_Oper: + retval = _equalOper(a, b); + break; + case T_Const: + retval = _equalConst(a, b); + break; + case T_Param: + retval = _equalParam(a, b); + break; + case T_Func: + retval = _equalFunc(a, b); + break; + case T_CInfo: + retval = _equalCInfo(a, b); + break; + case T_JoinMethod: + retval = _equalJoinMethod(a, b); + break; + case T_Path: + retval = _equalPath(a, b); + break; + case T_IndexPath: + retval = _equalIndexPath(a, b); + break; + case T_JoinPath: + retval = _equalJoinPath(a, b); + break; + case T_MergePath: + retval = _equalMergePath(a, b); + break; + case T_HashPath: + retval = _equalHashPath(a, b); + break; + case T_JoinKey: + retval = _equalJoinKey(a, b); + break; + case T_MergeOrder: + retval = _equalMergeOrder(a, b); + break; + case T_HInfo: + retval = _equalHInfo(a, b); + break; + case T_IndexScan: + retval = _equalIndexScan(a, b); + break; + case T_JInfo: + retval = _equalJInfo(a, b); + break; + case T_EState: + retval = _equalEState(a, b); + break; + case T_Integer: + case T_String: + case T_Float: + retval = _equalValue(a, b); + break; + case T_List: + { + List *la = (List *) a; + List *lb = (List *) b; + List *l; + + if (a == NULL && b == NULL) + return (true); + if (length(a) != length(b)) + return (false); + foreach(l, la) + { + if (!equal(lfirst(l), lfirst(lb))) + return (false); + lb = lnext(lb); + } + retval = true; + } + break; + default: + elog(NOTICE, "equal: don't know whether nodes of type %d are equal", + nodeTag(a)); + break; } - break; - default: - elog(NOTICE, "equal: don't know whether nodes of type %d are equal", - nodeTag(a)); - break; - } - - return retval; + + return retval; } /* * equali-- - * compares two lists of integers + * compares two lists of integers * * XXX temp hack. needs something like T_IntList */ -static bool -equali(List *a, List *b) -{ - List *la = (List*)a; - List *lb = (List*)b; - List *l; - - if (a==NULL && b==NULL) - return (true); - if (length(a)!=length(b)) - return (false); - foreach(l, la) { - if (lfirsti(l) != lfirsti(lb)) - return (false); - lb = lnext(lb); - } - return true; +static bool +equali(List * a, List * b) +{ + List *la = (List *) a; + List *lb = (List *) b; + List *l; + + if (a == NULL && b == NULL) + return (true); + if (length(a) != length(b)) + return (false); + foreach(l, la) + { + if (lfirsti(l) != lfirsti(lb)) + return (false); + lb = lnext(lb); + } + return true; } diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c index 6dc010eff65..334030822f7 100644 --- a/src/backend/nodes/list.c +++ b/src/backend/nodes/list.c @@ -1,23 +1,23 @@ /*------------------------------------------------------------------------- * * list.c-- - * various list handling routines + * various list handling routines * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.4 1997/08/19 21:31:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.5 1997/09/07 04:42:46 momjian Exp $ * * NOTES - * XXX a few of the following functions are duplicated to handle - * List of pointers and List of integers separately. Some day, - * someone should unify them. - ay 11/2/94 - * This file needs cleanup. + * XXX a few of the following functions are duplicated to handle + * List of pointers and List of integers separately. Some day, + * someone should unify them. - ay 11/2/94 + * This file needs cleanup. * * HISTORY - * AUTHOR DATE MAJOR EVENT - * Andrew Yu Oct, 1994 file creation + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Oct, 1994 file creation * *------------------------------------------------------------------------- */ @@ -25,442 +25,483 @@ #include "postgres.h" #include "nodes/pg_list.h" #include "nodes/parsenodes.h" -#include "utils/builtins.h" /* for namecpy */ +#include "utils/builtins.h" /* for namecpy */ #include "utils/elog.h" #include "utils/palloc.h" -List * -makeList(void *elem, ...) +List * +makeList(void *elem,...) { - va_list args; - List *retval = NIL; - List *temp = NIL; - List *tempcons = NIL; - - va_start(args, elem); - - temp = elem; - while (temp != (void *) -1) { - temp = lcons(temp, NIL); - if (tempcons == NIL) - retval = temp; - else - lnext(tempcons) = temp; - tempcons = temp; - - temp = va_arg(args, void *); - } + va_list args; + List *retval = NIL; + List *temp = NIL; + List *tempcons = NIL; + + va_start(args, elem); + + temp = elem; + while (temp != (void *) -1) + { + temp = lcons(temp, NIL); + if (tempcons == NIL) + retval = temp; + else + lnext(tempcons) = temp; + tempcons = temp; + + temp = va_arg(args, void *); + } - va_end(args); + va_end(args); - return (retval); + return (retval); } -List * -lcons(void *obj, List *list) +List * +lcons(void *obj, List * list) { - List *l = makeNode(List); - lfirst(l) = obj; - lnext(l) = list; - return l; + List *l = makeNode(List); + + lfirst(l) = obj; + lnext(l) = list; + return l; } -List * -lconsi(int datum, List *list) +List * +lconsi(int datum, List * list) { - List *l = makeNode(List); - lfirsti(l) = datum; - lnext(l) = list; - return l; + List *l = makeNode(List); + + lfirsti(l) = datum; + lnext(l) = list; + return l; } -List * -lappend(List *list, void *obj) +List * +lappend(List * list, void *obj) { - return nconc(list, lcons(obj, NIL)); + return nconc(list, lcons(obj, NIL)); } -List * -lappendi(List *list, int datum) +List * +lappendi(List * list, int datum) { - return nconc(list, lconsi(datum, NIL)); + return nconc(list, lconsi(datum, NIL)); } -Value * +Value * makeInteger(long i) { - Value *v = makeNode(Value); - v->type = T_Integer; - v->val.ival = i; - return v; + Value *v = makeNode(Value); + + v->type = T_Integer; + v->val.ival = i; + return v; } -Value * +Value * makeFloat(double d) { - Value *v = makeNode(Value); - v->type = T_Float; - v->val.dval = d; - return v; + Value *v = makeNode(Value); + + v->type = T_Float; + v->val.dval = d; + return v; } -Value * +Value * makeString(char *str) { - Value *v = makeNode(Value); - v->type = T_String; - v->val.str = str; - return v; + Value *v = makeNode(Value); + + v->type = T_String; + v->val.str = str; + return v; } /* n starts with 0 */ -void * -nth(int n, List *l) +void * +nth(int n, List * l) { - /* XXX assume list is long enough */ - while(n > 0) { - l = lnext(l); - n--; - } - return lfirst(l); + /* XXX assume list is long enough */ + while (n > 0) + { + l = lnext(l); + n--; + } + return lfirst(l); } int -nthi(int n, List *l) +nthi(int n, List * l) { - /* XXX assume list is long enough */ - while(n > 0) { - l = lnext(l); - n--; - } - return lfirsti(l); + /* XXX assume list is long enough */ + while (n > 0) + { + l = lnext(l); + n--; + } + return lfirsti(l); } /* this is here solely for rt_store. Get rid of me some day! */ void -set_nth(List *l, int n, void *elem) +set_nth(List * l, int n, void *elem) { - /* XXX assume list is long enough */ - while(n > 0) { - l = lnext(l); - n--; - } - lfirst(l) = elem; - return; + /* XXX assume list is long enough */ + while (n > 0) + { + l = lnext(l); + n--; + } + lfirst(l) = elem; + return; } int -length(List *l) +length(List * l) { - int i=0; - while(l!=NIL) { - l = lnext(l); - i++; - } - return i; + int i = 0; + + while (l != NIL) + { + l = lnext(l); + i++; + } + return i; } void -freeList(List *list) +freeList(List * list) { - while(list!=NIL) { - List *l = list; - list = lnext(list); - pfree(l); - } + while (list != NIL) + { + List *l = list; + + list = lnext(list); + pfree(l); + } } /* * below are for backwards compatibility */ -List * -append(List *l1, List *l2) +List * +append(List * l1, List * l2) { - List *newlist, *newlist2, *p; + List *newlist, + *newlist2, + *p; - if (l1==NIL) - return copyObject(l2); + if (l1 == NIL) + return copyObject(l2); - newlist = copyObject(l1); - newlist2 = copyObject(l2); + newlist = copyObject(l1); + newlist2 = copyObject(l2); - for (p=newlist; lnext(p)!=NIL; p=lnext(p)) - ; - lnext(p) = newlist2; - return newlist; + for (p = newlist; lnext(p) != NIL; p = lnext(p)) + ; + lnext(p) = newlist2; + return newlist; } /* * below are for backwards compatibility */ -List * -intAppend(List *l1, List *l2) +List * +intAppend(List * l1, List * l2) { - List *newlist, *newlist2, *p; + List *newlist, + *newlist2, + *p; - if (l1==NIL) - return listCopy(l2); + if (l1 == NIL) + return listCopy(l2); - newlist = listCopy(l1); - newlist2 = listCopy(l2); + newlist = listCopy(l1); + newlist2 = listCopy(l2); - for (p=newlist; lnext(p)!=NIL; p=lnext(p)) - ; - lnext(p) = newlist2; - return newlist; + for (p = newlist; lnext(p) != NIL; p = lnext(p)) + ; + lnext(p) = newlist2; + return newlist; } -List * -nconc(List *l1, List *l2) +List * +nconc(List * l1, List * l2) { - List *temp; - - if (l1 == NIL) - return l2; - if (l2 == NIL) - return l1; - if (l1 == l2) - elog(WARN, "tryout to nconc a list to itself"); - - for (temp = l1; lnext(temp)!=NULL; temp = lnext(temp)) - ; - - lnext(temp) = l2; - return(l1); /* list1 is now list1[]list2 */ + List *temp; + + if (l1 == NIL) + return l2; + if (l2 == NIL) + return l1; + if (l1 == l2) + elog(WARN, "tryout to nconc a list to itself"); + + for (temp = l1; lnext(temp) != NULL; temp = lnext(temp)) + ; + + lnext(temp) = l2; + return (l1); /* list1 is now list1[]list2 */ } -List * -nreverse(List *list) +List * +nreverse(List * list) { - List *rlist = NIL; - List *p = NIL; - - if(list==NULL) - return(NIL); - - if (length(list) == 1) - return(list); - - for (p = list; p!=NULL; p = lnext(p)) { - rlist = lcons(lfirst(p),rlist); - } - - lfirst(list) = lfirst(rlist); - lnext(list) = lnext(rlist); - return(list); + List *rlist = NIL; + List *p = NIL; + + if (list == NULL) + return (NIL); + + if (length(list) == 1) + return (list); + + for (p = list; p != NULL; p = lnext(p)) + { + rlist = lcons(lfirst(p), rlist); + } + + lfirst(list) = lfirst(rlist); + lnext(list) = lnext(rlist); + return (list); } -/* - * same - * - * Returns t if two lists contain the same elements. - * now defined in lispdep.c +/* + * same + * + * Returns t if two lists contain the same elements. + * now defined in lispdep.c * * XXX only good for IntList -ay */ bool -same(List *foo, List *bar) +same(List * foo, List * bar) { - List *temp = NIL; - - if (foo == NULL) - return (bar==NULL); - if (bar == NULL) - return (foo==NULL); - if (length(foo) == length(bar)) { - foreach (temp,foo) { - if (!intMember(lfirsti(temp),bar)) - return(false); + List *temp = NIL; + + if (foo == NULL) + return (bar == NULL); + if (bar == NULL) + return (foo == NULL); + if (length(foo) == length(bar)) + { + foreach(temp, foo) + { + if (!intMember(lfirsti(temp), bar)) + return (false); + } + return (true); } - return(true); - } - return(false); - -} - -List * -LispUnion(List *foo, List *bar) + return (false); + +} + +List * +LispUnion(List * foo, List * bar) { - List *retval = NIL; - List *i = NIL; - List *j = NIL; - - if (foo==NIL) - return(bar); /* XXX - should be copy of bar */ - - if (bar==NIL) - return(foo); /* XXX - should be copy of foo */ - - foreach (i,foo) { - foreach (j,bar) { - if (! equal(lfirst(i), lfirst(j))) { - retval = lappend(retval,lfirst(i)); - break; - } + List *retval = NIL; + List *i = NIL; + List *j = NIL; + + if (foo == NIL) + return (bar); /* XXX - should be copy of bar */ + + if (bar == NIL) + return (foo); /* XXX - should be copy of foo */ + + foreach(i, foo) + { + foreach(j, bar) + { + if (!equal(lfirst(i), lfirst(j))) + { + retval = lappend(retval, lfirst(i)); + break; + } + } } - } - foreach(i,bar) { - retval = lappend(retval,lfirst(i)); - } - - return(retval); + foreach(i, bar) + { + retval = lappend(retval, lfirst(i)); + } + + return (retval); } -List * -LispUnioni(List *foo, List *bar) +List * +LispUnioni(List * foo, List * bar) { - List *retval = NIL; - List *i = NIL; - List *j = NIL; - - if (foo==NIL) - return(bar); /* XXX - should be copy of bar */ - - if (bar==NIL) - return(foo); /* XXX - should be copy of foo */ - - foreach (i,foo) { - foreach (j,bar) { - if (lfirsti(i) != lfirsti(j)) { - retval = lappendi(retval,lfirsti(i)); - break; - } + List *retval = NIL; + List *i = NIL; + List *j = NIL; + + if (foo == NIL) + return (bar); /* XXX - should be copy of bar */ + + if (bar == NIL) + return (foo); /* XXX - should be copy of foo */ + + foreach(i, foo) + { + foreach(j, bar) + { + if (lfirsti(i) != lfirsti(j)) + { + retval = lappendi(retval, lfirsti(i)); + break; + } + } + } + foreach(i, bar) + { + retval = lappendi(retval, lfirsti(i)); } - } - foreach(i,bar) { - retval = lappendi(retval, lfirsti(i)); - } - - return(retval); + + return (retval); } /* * member() * - nondestructive, returns t iff foo is a member of the list - * bar + * bar */ bool -member(void *foo, List *bar) +member(void *foo, List * bar) { - List *i; - foreach (i,bar) - if (equal((Node*)(lfirst(i)),(Node*)foo)) - return(true); - return(false); + List *i; + + foreach(i, bar) + if (equal((Node *) (lfirst(i)), (Node *) foo)) + return (true); + return (false); } bool -intMember(int foo, List *bar) +intMember(int foo, List * bar) { - List *i; - foreach (i,bar) - if (foo == lfirsti(i)) - return(true); - return(false); + List *i; + + foreach(i, bar) + if (foo == lfirsti(i)) + return (true); + return (false); } /* * lremove - - * only does pointer comparisons. Removes 'elem' from the the linked list. + * only does pointer comparisons. Removes 'elem' from the the linked list. */ -List * -lremove(void *elem, List *list) +List * +lremove(void *elem, List * list) { - List *l; - List *prev = NIL; - List *result = list; - - foreach(l, list) { - if (elem == lfirst(l)) - break; - prev = l; - } - if (l!=NULL) { - if (prev == NIL) { - result = lnext(list); - } else { - lnext(prev) = lnext(l); + List *l; + List *prev = NIL; + List *result = list; + + foreach(l, list) + { + if (elem == lfirst(l)) + break; + prev = l; + } + if (l != NULL) + { + if (prev == NIL) + { + result = lnext(list); + } + else + { + lnext(prev) = lnext(l); + } } - } - return result; + return result; } - -List * -LispRemove(void *elem, List *list) + +List * +LispRemove(void *elem, List * list) { - List *temp = NIL; - List *prev = NIL; - - if (equal(elem, lfirst(list))) - return lnext(list); - - temp = lnext(list); - prev = list; - while(temp!=NIL) { - if (equal(elem, lfirst(temp))) { - lnext(prev) = lnext(temp); - break; + List *temp = NIL; + List *prev = NIL; + + if (equal(elem, lfirst(list))) + return lnext(list); + + temp = lnext(list); + prev = list; + while (temp != NIL) + { + if (equal(elem, lfirst(temp))) + { + lnext(prev) = lnext(temp); + break; + } + temp = lnext(temp); + prev = lnext(prev); } - temp = lnext(temp); - prev = lnext(prev); - } - return(list); + return (list); } #ifdef NOT_USED -List * -intLispRemove(int elem, List *list) +List * +intLispRemove(int elem, List * list) { - List *temp = NIL; - List *prev = NIL; - - if (elem == lfirsti(list)) - return lnext(list); - - temp = lnext(list); - prev = list; - while(temp!=NIL) { - if (elem == lfirsti(temp)) { - lnext(prev) = lnext(temp); - break; + List *temp = NIL; + List *prev = NIL; + + if (elem == lfirsti(list)) + return lnext(list); + + temp = lnext(list); + prev = list; + while (temp != NIL) + { + if (elem == lfirsti(temp)) + { + lnext(prev) = lnext(temp); + break; + } + temp = lnext(temp); + prev = lnext(prev); } - temp = lnext(temp); - prev = lnext(prev); - } - return(list); + return (list); } + #endif -List * -set_difference(List *list1, List *list2) +List * +set_difference(List * list1, List * list2) { - List *temp1 = NIL; - List *result = NIL; - - if (list2==NIL) - return(list1); - - foreach (temp1, list1) { - if (!member(lfirst(temp1), list2)) - result = lappend(result, lfirst(temp1)); - } - return(result); + List *temp1 = NIL; + List *result = NIL; + + if (list2 == NIL) + return (list1); + + foreach(temp1, list1) + { + if (!member(lfirst(temp1), list2)) + result = lappend(result, lfirst(temp1)); + } + return (result); } -List * -set_differencei(List *list1, List *list2) +List * +set_differencei(List * list1, List * list2) { - List *temp1 = NIL; - List *result = NIL; - - if (list2==NIL) - return(list1); - - foreach (temp1, list1) { - if (!intMember(lfirsti(temp1), list2)) - result = lappendi(result, lfirsti(temp1)); - } - return(result); -} + List *temp1 = NIL; + List *result = NIL; + + if (list2 == NIL) + return (list1); + foreach(temp1, list1) + { + if (!intMember(lfirsti(temp1), list2)) + result = lappendi(result, lfirsti(temp1)); + } + return (result); +} diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 18894e8a41c..7c5a9efc1fb 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -1,22 +1,22 @@ /* * makefuncs.c-- - * creator functions for primitive nodes. The functions here are for - * the most frequently created nodes. + * creator functions for primitive nodes. The functions here are for + * the most frequently created nodes. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.2 1997/01/22 01:42:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.3 1997/09/07 04:42:48 momjian Exp $ * * NOTES - * Creator functions in POSTGRES 4.2 are generated automatically. Most of - * them are rarely used. Now we don't generate them any more. If you want - * one, you have to write it yourself. + * Creator functions in POSTGRES 4.2 are generated automatically. Most of + * them are rarely used. Now we don't generate them any more. If you want + * one, you have to write it yourself. * * HISTORY - * AUTHOR DATE MAJOR EVENT - * Andrew Yu Oct 20, 1994 file creation + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Oct 20, 1994 file creation */ #include "postgres.h" #include "nodes/pg_list.h" @@ -25,95 +25,94 @@ /* * makeOper - - * creates an Oper node + * creates an Oper node */ -Oper * +Oper * makeOper(Oid opno, - Oid opid, - Oid opresulttype, - int opsize, - FunctionCachePtr op_fcache) + Oid opid, + Oid opresulttype, + int opsize, + FunctionCachePtr op_fcache) { - Oper *oper = makeNode(Oper); + Oper *oper = makeNode(Oper); - oper->opno = opno; - oper->opid = opid; - oper->opresulttype = opresulttype; - oper->opsize = opsize; - oper->op_fcache = op_fcache; - return oper; + oper->opno = opno; + oper->opid = opid; + oper->opresulttype = opresulttype; + oper->opsize = opsize; + oper->op_fcache = op_fcache; + return oper; } /* * makeVar - - * creates a Var node + * creates a Var node * */ -Var * -makeVar(Index varno, - AttrNumber varattno, - Oid vartype, - Index varnoold, - AttrNumber varoattno) +Var * +makeVar(Index varno, + AttrNumber varattno, + Oid vartype, + Index varnoold, + AttrNumber varoattno) { - Var *var = makeNode(Var); + Var *var = makeNode(Var); - var->varno = varno; - var->varattno = varattno; - var->vartype = vartype; - var->varnoold = varnoold; - var->varoattno = varoattno; + var->varno = varno; + var->varattno = varattno; + var->vartype = vartype; + var->varnoold = varnoold; + var->varoattno = varoattno; - return var; + return var; } /* * makeResdom - - * creates a Resdom (Result Domain) node + * creates a Resdom (Result Domain) node */ -Resdom * +Resdom * makeResdom(AttrNumber resno, - Oid restype, - int reslen, - char *resname, - Index reskey, - Oid reskeyop, - int resjunk) + Oid restype, + int reslen, + char *resname, + Index reskey, + Oid reskeyop, + int resjunk) { - Resdom *resdom = makeNode(Resdom); + Resdom *resdom = makeNode(Resdom); - resdom->resno = resno; - resdom->restype = restype; - resdom->reslen = reslen; - resdom->resname = resname; - resdom->reskey = reskey; - resdom->reskeyop = reskeyop; - resdom->resjunk = resjunk; - return resdom; + resdom->resno = resno; + resdom->restype = restype; + resdom->reslen = reslen; + resdom->resname = resname; + resdom->reskey = reskey; + resdom->reskeyop = reskeyop; + resdom->resjunk = resjunk; + return resdom; } /* * makeConst - - * creates a Const node + * creates a Const node */ -Const * +Const * makeConst(Oid consttype, - Size constlen, - Datum constvalue, - bool constisnull, - bool constbyval, - bool constisset, - bool constiscast) + Size constlen, + Datum constvalue, + bool constisnull, + bool constbyval, + bool constisset, + bool constiscast) { - Const *cnst = makeNode(Const); + Const *cnst = makeNode(Const); - cnst->consttype = consttype; - cnst->constlen = constlen; - cnst->constvalue = constvalue; - cnst->constisnull = constisnull; - cnst->constbyval = constbyval; - cnst->constisset = constisset; - cnst->constiscast = constiscast; - return cnst; + cnst->consttype = consttype; + cnst->constlen = constlen; + cnst->constvalue = constvalue; + cnst->constisnull = constisnull; + cnst->constbyval = constbyval; + cnst->constisset = constisset; + cnst->constiscast = constiscast; + return cnst; } - diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index e1e6bc6f140..081760eaca2 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -1,18 +1,18 @@ /*------------------------------------------------------------------------- * * nodeFuncs.c-- - * All node routines more complicated than simple access/modification + * All node routines more complicated than simple access/modification * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.3 1997/08/19 21:31:41 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.4 1997/09/07 04:42:49 momjian Exp $ * *------------------------------------------------------------------------- */ -#include <sys/types.h> +#include <sys/types.h> #include "postgres.h" @@ -23,99 +23,96 @@ #include "nodes/nodeFuncs.h" #include "utils/lsyscache.h" -static bool var_is_inner(Var *var); +static bool var_is_inner(Var * var); -/* +/* * single_node - - * Returns t if node corresponds to a single-noded expression + * Returns t if node corresponds to a single-noded expression */ bool -single_node(Node *node) +single_node(Node * node) { - if(IsA(node,Ident) || IsA(node,Const) || IsA(node,Var) || IsA(node,Param)) - return(true); - else - return(false); + if (IsA(node, Ident) || IsA(node, Const) || IsA(node, Var) || IsA(node, Param)) + return (true); + else + return (false); } /***************************************************************************** - * VAR nodes + * VAR nodes *****************************************************************************/ -/* - * var_is_outer - * var_is_inner - * var_is_mat - * var_is_rel - * - * Returns t iff the var node corresponds to (respectively): - * the outer relation in a join - * the inner relation of a join - * a materialized relation - * a base relation (i.e., not an attribute reference, a variable from - * some lower join level, or a sort result) - * var node is an array reference - * +/* + * var_is_outer + * var_is_inner + * var_is_mat + * var_is_rel + * + * Returns t iff the var node corresponds to (respectively): + * the outer relation in a join + * the inner relation of a join + * a materialized relation + * a base relation (i.e., not an attribute reference, a variable from + * some lower join level, or a sort result) + * var node is an array reference + * */ bool -var_is_outer (Var *var) +var_is_outer(Var * var) { - return((bool)(var->varno == OUTER)); + return ((bool) (var->varno == OUTER)); } -static bool -var_is_inner (Var *var) +static bool +var_is_inner(Var * var) { - return ( (bool) (var->varno == INNER)); + return ((bool) (var->varno == INNER)); } bool -var_is_rel (Var *var) +var_is_rel(Var * var) { - return (bool) - ! (var_is_inner (var) || var_is_outer (var)); + return (bool) + ! (var_is_inner(var) || var_is_outer(var)); } /***************************************************************************** - * OPER nodes + * OPER nodes *****************************************************************************/ -/* +/* * replace_opid - - * - * Given a oper node, resets the opfid field with the - * procedure OID (regproc id). - * - * Returns the modified oper node. - * + * + * Given a oper node, resets the opfid field with the + * procedure OID (regproc id). + * + * Returns the modified oper node. + * */ -Oper * -replace_opid (Oper *oper) +Oper * +replace_opid(Oper * oper) { - oper->opid = get_opcode(oper->opno); - oper->op_fcache = NULL; - return(oper); + oper->opid = get_opcode(oper->opno); + oper->op_fcache = NULL; + return (oper); } /***************************************************************************** - * constant (CONST, PARAM) nodes + * constant (CONST, PARAM) nodes *****************************************************************************/ -/* +/* * non_null - - * Returns t if the node is a non-null constant, e.g., if the node has a - * valid `constvalue' field. - * + * Returns t if the node is a non-null constant, e.g., if the node has a + * valid `constvalue' field. + * */ bool -non_null (Expr *c) +non_null(Expr * c) { - - if ( IsA(c,Const) && ! ((Const*)c)->constisnull ) - return(true); - else - return(false); -} - - + if (IsA(c, Const) && !((Const *) c)->constisnull) + return (true); + else + return (false); +} diff --git a/src/backend/nodes/nodes.c b/src/backend/nodes/nodes.c index 82845cca15c..2af057e5a4c 100644 --- a/src/backend/nodes/nodes.c +++ b/src/backend/nodes/nodes.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * nodes.c-- - * support code for nodes (now that we get rid of the home-brew - * inheritance system, our support code for nodes get much simpler) + * support code for nodes (now that we get rid of the home-brew + * inheritance system, our support code for nodes get much simpler) * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/nodes.c,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/nodes.c,v 1.2 1997/09/07 04:42:52 momjian Exp $ * * HISTORY - * Andrew Yu Oct 20, 1994 file creation + * Andrew Yu Oct 20, 1994 file creation * *------------------------------------------------------------------------- */ @@ -19,27 +19,27 @@ #include "postgres.h" #include "utils/palloc.h" #include "utils/elog.h" -#include "nodes/nodes.h" /* where func declarations of this file goes */ +#include "nodes/nodes.h" /* where func declarations of this file + * goes */ /* * newNode - - * create a new node of the specified size and tag the node with the - * specified tag. + * create a new node of the specified size and tag the node with the + * specified tag. * * !WARNING!: Avoid using newNode directly. You should be using the - * macro makeNode. eg. to create a Resdom node, use makeNode(Resdom) + * macro makeNode. eg. to create a Resdom node, use makeNode(Resdom) * */ -Node * +Node * newNode(Size size, NodeTag tag) { - Node *newNode; - - Assert(size >= 4); /* need the tag, at least */ - - newNode = (Node *)palloc(size); - memset((char *)newNode, 0, size); - newNode->type = tag; - return(newNode); -} + Node *newNode; + + Assert(size >= 4); /* need the tag, at least */ + newNode = (Node *) palloc(size); + memset((char *) newNode, 0, size); + newNode->type = tag; + return (newNode); +} diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index a41848c6188..a1574c8734f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1,24 +1,24 @@ /*------------------------------------------------------------------------- * * outfuncs.c-- - * routines to convert a node to ascii representation + * routines to convert a node to ascii representation * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.6 1997/08/18 20:52:43 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.7 1997/09/07 04:42:53 momjian Exp $ * * NOTES - * Every (plan) node in POSTGRES has an associated "out" routine which - * knows how to create its ascii representation. These functions are - * useful for debugging as well as for storing plans in the system - * catalogs (eg. indexes). This is also the plan string sent out in - * Mariposa. + * Every (plan) node in POSTGRES has an associated "out" routine which + * knows how to create its ascii representation. These functions are + * useful for debugging as well as for storing plans in the system + * catalogs (eg. indexes). This is also the plan string sent out in + * Mariposa. * - * These functions update the in/out argument of type StringInfo - * passed to them. This argument contains the string holding the ASCII - * representation plus some other information (string length, etc.) + * These functions update the in/out argument of type StringInfo + * passed to them. This argument contains the string holding the ASCII + * representation plus some other information (string length, etc.) * *------------------------------------------------------------------------- */ @@ -45,1305 +45,1322 @@ #include "catalog/pg_type.h" #include "lib/stringinfo.h" -static void _outDatum(StringInfo str, Datum value, Oid type); -static void _outNode(StringInfo str, void *obj); +static void _outDatum(StringInfo str, Datum value, Oid type); +static void _outNode(StringInfo str, void *obj); /* * _outIntList - - * converts a List of integers + * converts a List of integers */ static void -_outIntList(StringInfo str, List *list) +_outIntList(StringInfo str, List * list) { - List *l; - char buf[500]; + List *l; + char buf[500]; - appendStringInfo(str, "("); - foreach(l, list) { - sprintf(buf, "%d ", (int)lfirst(l)); - appendStringInfo(str, buf); - } - appendStringInfo(str, ")"); + appendStringInfo(str, "("); + foreach(l, list) + { + sprintf(buf, "%d ", (int) lfirst(l)); + appendStringInfo(str, buf); + } + appendStringInfo(str, ")"); } static void -_outQuery(StringInfo str, Query *node) +_outQuery(StringInfo str, Query * node) { - char buf[500]; - - sprintf(buf, "QUERY"); - appendStringInfo(str,buf); - - sprintf(buf, " :command %d", node->commandType); - appendStringInfo(str,buf); - if (node->utilityStmt && - nodeTag(node->utilityStmt) == T_NotifyStmt) - sprintf(buf," :utility %s", - ((NotifyStmt*)(node->utilityStmt))->relname); - else /* use "" to designate */ - sprintf(buf," :utility \"\""); - appendStringInfo(str,buf); - - sprintf(buf, " :resrel %d", node->resultRelation); - appendStringInfo(str,buf); - sprintf(buf, " :rtable "); - appendStringInfo(str,buf); - _outNode(str, node->rtable); - if (node->uniqueFlag) - sprintf(buf, " :unique %s", node->uniqueFlag); - else /* use "" to designate non-unique */ - sprintf(buf, " :unique \"\""); - appendStringInfo(str,buf); - sprintf(buf, " :targetlist "); - appendStringInfo(str,buf); - _outNode(str, node->targetList); - sprintf(buf, " :qual "); - appendStringInfo(str,buf); - _outNode(str, node->qual); - + char buf[500]; + + sprintf(buf, "QUERY"); + appendStringInfo(str, buf); + + sprintf(buf, " :command %d", node->commandType); + appendStringInfo(str, buf); + if (node->utilityStmt && + nodeTag(node->utilityStmt) == T_NotifyStmt) + sprintf(buf, " :utility %s", + ((NotifyStmt *) (node->utilityStmt))->relname); + else +/* use "" to designate */ + sprintf(buf, " :utility \"\""); + appendStringInfo(str, buf); + + sprintf(buf, " :resrel %d", node->resultRelation); + appendStringInfo(str, buf); + sprintf(buf, " :rtable "); + appendStringInfo(str, buf); + _outNode(str, node->rtable); + if (node->uniqueFlag) + sprintf(buf, " :unique %s", node->uniqueFlag); + else +/* use "" to designate non-unique */ + sprintf(buf, " :unique \"\""); + appendStringInfo(str, buf); + sprintf(buf, " :targetlist "); + appendStringInfo(str, buf); + _outNode(str, node->targetList); + sprintf(buf, " :qual "); + appendStringInfo(str, buf); + _outNode(str, node->qual); + } /* * print the basic stuff of all nodes that inherit from Plan */ static void -_outPlanInfo(StringInfo str, Plan *node) +_outPlanInfo(StringInfo str, Plan * node) { - char buf[500]; - - sprintf(buf, " :cost %g", node->cost ); - appendStringInfo(str,buf); - sprintf(buf, " :size %d", node->plan_size); - appendStringInfo(str,buf); - sprintf(buf, " :width %d", node->plan_width); - appendStringInfo(str,buf); - sprintf(buf, " :state %s", (node->state == (EState*) NULL ? - "nil" : "non-NIL")); - appendStringInfo(str,buf); - sprintf(buf, " :qptargetlist "); - appendStringInfo(str,buf); - _outNode(str, node->targetlist); - sprintf(buf, " :qpqual "); - appendStringInfo(str,buf); - _outNode(str, node->qual); - sprintf(buf, " :lefttree "); - appendStringInfo(str,buf); - _outNode(str, node->lefttree); - sprintf(buf, " :righttree "); - appendStringInfo(str,buf); - _outNode(str, node->righttree); - + char buf[500]; + + sprintf(buf, " :cost %g", node->cost); + appendStringInfo(str, buf); + sprintf(buf, " :size %d", node->plan_size); + appendStringInfo(str, buf); + sprintf(buf, " :width %d", node->plan_width); + appendStringInfo(str, buf); + sprintf(buf, " :state %s", (node->state == (EState *) NULL ? + "nil" : "non-NIL")); + appendStringInfo(str, buf); + sprintf(buf, " :qptargetlist "); + appendStringInfo(str, buf); + _outNode(str, node->targetlist); + sprintf(buf, " :qpqual "); + appendStringInfo(str, buf); + _outNode(str, node->qual); + sprintf(buf, " :lefttree "); + appendStringInfo(str, buf); + _outNode(str, node->lefttree); + sprintf(buf, " :righttree "); + appendStringInfo(str, buf); + _outNode(str, node->righttree); + } /* - * Stuff from plannodes.h + * Stuff from plannodes.h */ static void -_outPlan(StringInfo str, Plan *node) +_outPlan(StringInfo str, Plan * node) { - char buf[500]; - - sprintf(buf, "PLAN"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - + char buf[500]; + + sprintf(buf, "PLAN"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + } static void -_outResult(StringInfo str, Result *node) +_outResult(StringInfo str, Result * node) { - char buf[500]; - - sprintf(buf, "RESULT"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :resconstantqual "); - appendStringInfo(str,buf); - _outNode(str, node->resconstantqual); - + char buf[500]; + + sprintf(buf, "RESULT"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :resconstantqual "); + appendStringInfo(str, buf); + _outNode(str, node->resconstantqual); + } /* - * Existential is a subclass of Plan. + * Existential is a subclass of Plan. */ static void -_outExistential(StringInfo str, Existential *node) +_outExistential(StringInfo str, Existential * node) { - char buf[500]; - - sprintf(buf, "EXISTENTIAL"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - + char buf[500]; + + sprintf(buf, "EXISTENTIAL"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + } /* - * Append is a subclass of Plan. + * Append is a subclass of Plan. */ static void -_outAppend(StringInfo str, Append *node) +_outAppend(StringInfo str, Append * node) { - char buf[500]; - - sprintf(buf, "APPEND"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :unionplans "); - appendStringInfo(str,buf); - _outNode(str, node->unionplans); - - sprintf(buf, " :unionrelid %d", node->unionrelid); - appendStringInfo(str,buf); - - sprintf(buf, " :unionrtentries "); - appendStringInfo(str,buf); - _outNode(str, node->unionrtentries); - + char buf[500]; + + sprintf(buf, "APPEND"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :unionplans "); + appendStringInfo(str, buf); + _outNode(str, node->unionplans); + + sprintf(buf, " :unionrelid %d", node->unionrelid); + appendStringInfo(str, buf); + + sprintf(buf, " :unionrtentries "); + appendStringInfo(str, buf); + _outNode(str, node->unionrtentries); + } /* - * Join is a subclass of Plan + * Join is a subclass of Plan */ static void -_outJoin(StringInfo str, Join *node) +_outJoin(StringInfo str, Join * node) { - char buf[500]; - - sprintf(buf, "JOIN"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - + char buf[500]; + + sprintf(buf, "JOIN"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + } /* - * NestLoop is a subclass of Join + * NestLoop is a subclass of Join */ static void -_outNestLoop(StringInfo str, NestLoop *node) +_outNestLoop(StringInfo str, NestLoop * node) { - char buf[500]; - - sprintf(buf, "NESTLOOP"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); + char buf[500]; + + sprintf(buf, "NESTLOOP"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); } /* - * MergeJoin is a subclass of Join + * MergeJoin is a subclass of Join */ static void -_outMergeJoin(StringInfo str, MergeJoin *node) +_outMergeJoin(StringInfo str, MergeJoin * node) { - char buf[500]; - - sprintf(buf, "MERGEJOIN"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :mergeclauses "); - appendStringInfo(str,buf); - _outNode(str, node->mergeclauses); - - sprintf(buf, " :mergesortop %u", node->mergesortop); - appendStringInfo(str,buf); - - sprintf(buf, " :mergerightorder %u", node->mergerightorder[0]); - appendStringInfo(str, buf); - - sprintf(buf, " :mergeleftorder %u", node->mergeleftorder[0]); - appendStringInfo(str, buf); + char buf[500]; + + sprintf(buf, "MERGEJOIN"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :mergeclauses "); + appendStringInfo(str, buf); + _outNode(str, node->mergeclauses); + + sprintf(buf, " :mergesortop %u", node->mergesortop); + appendStringInfo(str, buf); + + sprintf(buf, " :mergerightorder %u", node->mergerightorder[0]); + appendStringInfo(str, buf); + + sprintf(buf, " :mergeleftorder %u", node->mergeleftorder[0]); + appendStringInfo(str, buf); } /* - * HashJoin is a subclass of Join. + * HashJoin is a subclass of Join. */ static void -_outHashJoin(StringInfo str, HashJoin *node) +_outHashJoin(StringInfo str, HashJoin * node) { - char buf[500]; - - sprintf(buf, "HASHJOIN"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :hashclauses "); - appendStringInfo(str,buf); - _outNode(str, node->hashclauses); - - sprintf(buf, " :hashjoinop %u",node->hashjoinop); - appendStringInfo(str,buf); - sprintf(buf, " :hashjointable 0x%x", (int) node->hashjointable); - appendStringInfo(str,buf); - sprintf(buf, " :hashjointablekey %d", node->hashjointablekey); - appendStringInfo(str,buf); - sprintf(buf, " :hashjointablesize %d", node->hashjointablesize); - appendStringInfo(str,buf); - sprintf(buf, " :hashdone %d", node->hashdone); - appendStringInfo(str,buf); + char buf[500]; + + sprintf(buf, "HASHJOIN"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :hashclauses "); + appendStringInfo(str, buf); + _outNode(str, node->hashclauses); + + sprintf(buf, " :hashjoinop %u", node->hashjoinop); + appendStringInfo(str, buf); + sprintf(buf, " :hashjointable 0x%x", (int) node->hashjointable); + appendStringInfo(str, buf); + sprintf(buf, " :hashjointablekey %d", node->hashjointablekey); + appendStringInfo(str, buf); + sprintf(buf, " :hashjointablesize %d", node->hashjointablesize); + appendStringInfo(str, buf); + sprintf(buf, " :hashdone %d", node->hashdone); + appendStringInfo(str, buf); } /* - * Scan is a subclass of Node + * Scan is a subclass of Node */ static void -_outScan(StringInfo str, Scan *node) +_outScan(StringInfo str, Scan * node) { - char buf[500]; - - sprintf(buf, "SCAN"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :scanrelid %d", node->scanrelid); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "SCAN"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :scanrelid %d", node->scanrelid); + appendStringInfo(str, buf); + } /* - * SeqScan is a subclass of Scan + * SeqScan is a subclass of Scan */ static void -_outSeqScan(StringInfo str, SeqScan *node) +_outSeqScan(StringInfo str, SeqScan * node) { - char buf[500]; - - sprintf(buf, "SEQSCAN"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :scanrelid %d", node->scanrelid); - appendStringInfo(str,buf); - - + char buf[500]; + + sprintf(buf, "SEQSCAN"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :scanrelid %d", node->scanrelid); + appendStringInfo(str, buf); + + } /* - * IndexScan is a subclass of Scan + * IndexScan is a subclass of Scan */ static void -_outIndexScan(StringInfo str, IndexScan *node) +_outIndexScan(StringInfo str, IndexScan * node) { - char buf[500]; - - sprintf(buf, "INDEXSCAN"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :scanrelid %d", node->scan.scanrelid); - appendStringInfo(str,buf); - - sprintf(buf, " :indxid "); - appendStringInfo(str,buf); - _outIntList(str, node->indxid); - - sprintf(buf, " :indxqual "); - appendStringInfo(str,buf); - _outNode(str, node->indxqual); - + char buf[500]; + + sprintf(buf, "INDEXSCAN"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :scanrelid %d", node->scan.scanrelid); + appendStringInfo(str, buf); + + sprintf(buf, " :indxid "); + appendStringInfo(str, buf); + _outIntList(str, node->indxid); + + sprintf(buf, " :indxqual "); + appendStringInfo(str, buf); + _outNode(str, node->indxqual); + } /* - * Temp is a subclass of Plan + * Temp is a subclass of Plan */ static void -_outTemp(StringInfo str, Temp *node) +_outTemp(StringInfo str, Temp * node) { - char buf[500]; - - sprintf(buf, "TEMP"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :tempid %u", node->tempid); - appendStringInfo(str,buf); - sprintf(buf, " :keycount %d", node->keycount); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "TEMP"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :tempid %u", node->tempid); + appendStringInfo(str, buf); + sprintf(buf, " :keycount %d", node->keycount); + appendStringInfo(str, buf); + } /* - * Sort is a subclass of Temp + * Sort is a subclass of Temp */ static void -_outSort(StringInfo str, Sort *node) +_outSort(StringInfo str, Sort * node) { - char buf[500]; - - sprintf(buf, "SORT"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :tempid %u", node->tempid); - appendStringInfo(str,buf); - sprintf(buf, " :keycount %d", node->keycount); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "SORT"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :tempid %u", node->tempid); + appendStringInfo(str, buf); + sprintf(buf, " :keycount %d", node->keycount); + appendStringInfo(str, buf); + } static void -_outAgg(StringInfo str, Agg *node) +_outAgg(StringInfo str, Agg * node) { - char buf[500]; - sprintf(buf, "AGG"); - appendStringInfo(str,buf); - _outPlanInfo(str,(Plan*)node); - - /* the actual Agg fields */ - sprintf(buf, " :numagg %d ", node->numAgg); - appendStringInfo(str, buf); + char buf[500]; + + sprintf(buf, "AGG"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + /* the actual Agg fields */ + sprintf(buf, " :numagg %d ", node->numAgg); + appendStringInfo(str, buf); } static void -_outGroup(StringInfo str, Group *node) +_outGroup(StringInfo str, Group * node) { - char buf[500]; - sprintf(buf, "GRP"); - appendStringInfo(str,buf); - _outPlanInfo(str,(Plan*)node); - - /* the actual Group fields */ - sprintf(buf, " :numCols %d ", node->numCols); - appendStringInfo(str, buf); - sprintf(buf, " :tuplePerGroup %s", node->tuplePerGroup ? "true" : "nil"); - appendStringInfo(str, buf); + char buf[500]; + + sprintf(buf, "GRP"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + /* the actual Group fields */ + sprintf(buf, " :numCols %d ", node->numCols); + appendStringInfo(str, buf); + sprintf(buf, " :tuplePerGroup %s", node->tuplePerGroup ? "true" : "nil"); + appendStringInfo(str, buf); } - - + + /* - * For some reason, unique is a subclass of Temp. + * For some reason, unique is a subclass of Temp. */ static void -_outUnique(StringInfo str, Unique *node) +_outUnique(StringInfo str, Unique * node) { - char buf[500]; - - sprintf(buf, "UNIQUE"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :tempid %u", node->tempid); - appendStringInfo(str,buf); - sprintf(buf, " :keycount %d", node->keycount); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "UNIQUE"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :tempid %u", node->tempid); + appendStringInfo(str, buf); + sprintf(buf, " :keycount %d", node->keycount); + appendStringInfo(str, buf); + } /* - * Hash is a subclass of Temp + * Hash is a subclass of Temp */ static void -_outHash(StringInfo str, Hash *node) +_outHash(StringInfo str, Hash * node) { - char buf[500]; - - sprintf(buf, "HASH"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :hashkey "); - appendStringInfo(str,buf); - _outNode(str, node->hashkey); - - sprintf(buf, " :hashtable 0x%x", (int) (node->hashtable)); - appendStringInfo(str,buf); - sprintf(buf, " :hashtablekey %d", node->hashtablekey); - appendStringInfo(str,buf); - sprintf(buf, " :hashtablesize %d", node->hashtablesize); - appendStringInfo(str,buf); + char buf[500]; + + sprintf(buf, "HASH"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :hashkey "); + appendStringInfo(str, buf); + _outNode(str, node->hashkey); + + sprintf(buf, " :hashtable 0x%x", (int) (node->hashtable)); + appendStringInfo(str, buf); + sprintf(buf, " :hashtablekey %d", node->hashtablekey); + appendStringInfo(str, buf); + sprintf(buf, " :hashtablesize %d", node->hashtablesize); + appendStringInfo(str, buf); } static void -_outTee(StringInfo str, Tee *node) +_outTee(StringInfo str, Tee * node) { - char buf[500]; - - sprintf(buf, "TEE"); - appendStringInfo(str,buf); - _outPlanInfo(str, (Plan*) node); - - sprintf(buf, " :leftParent %X", (int) (node->leftParent)); - appendStringInfo(str,buf); - sprintf(buf, " :rightParent %X", (int) (node->rightParent)); - appendStringInfo(str,buf); - - sprintf(buf, " :rtentries "); - appendStringInfo(str,buf); - _outNode(str, node->rtentries); + char buf[500]; + + sprintf(buf, "TEE"); + appendStringInfo(str, buf); + _outPlanInfo(str, (Plan *) node); + + sprintf(buf, " :leftParent %X", (int) (node->leftParent)); + appendStringInfo(str, buf); + sprintf(buf, " :rightParent %X", (int) (node->rightParent)); + appendStringInfo(str, buf); + + sprintf(buf, " :rtentries "); + appendStringInfo(str, buf); + _outNode(str, node->rtentries); } /***************************************************************************** * - * Stuff from primnodes.h. + * Stuff from primnodes.h. * *****************************************************************************/ /* - * Resdom is a subclass of Node + * Resdom is a subclass of Node */ static void -_outResdom(StringInfo str, Resdom *node) +_outResdom(StringInfo str, Resdom * node) { - char buf[500]; - - sprintf(buf, "RESDOM"); - appendStringInfo(str,buf); - sprintf(buf, " :resno %hd", node->resno); - appendStringInfo(str,buf); - sprintf(buf, " :restype %u", node->restype); - appendStringInfo(str,buf); - sprintf(buf, " :reslen %d", node->reslen); - appendStringInfo(str,buf); - sprintf(buf, " :resname \"%s\"", - ((node->resname) ? ((char *) node->resname) : "null")); - appendStringInfo(str,buf); - sprintf(buf, " :reskey %d", node->reskey); - appendStringInfo(str,buf); - sprintf(buf, " :reskeyop %u", node->reskeyop); - appendStringInfo(str,buf); - sprintf(buf, " :resjunk %d", node->resjunk); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "RESDOM"); + appendStringInfo(str, buf); + sprintf(buf, " :resno %hd", node->resno); + appendStringInfo(str, buf); + sprintf(buf, " :restype %u", node->restype); + appendStringInfo(str, buf); + sprintf(buf, " :reslen %d", node->reslen); + appendStringInfo(str, buf); + sprintf(buf, " :resname \"%s\"", + ((node->resname) ? ((char *) node->resname) : "null")); + appendStringInfo(str, buf); + sprintf(buf, " :reskey %d", node->reskey); + appendStringInfo(str, buf); + sprintf(buf, " :reskeyop %u", node->reskeyop); + appendStringInfo(str, buf); + sprintf(buf, " :resjunk %d", node->resjunk); + appendStringInfo(str, buf); + } static void -_outFjoin(StringInfo str, Fjoin *node) +_outFjoin(StringInfo str, Fjoin * node) { - char buf[500]; - int i; - - sprintf(buf, "FJOIN"); - appendStringInfo(str,buf); - sprintf(buf, " :initialized %s", node->fj_initialized ? "true":"nil"); - appendStringInfo(str,buf); - sprintf(buf, " :nNodes %d", node->fj_nNodes); - appendStringInfo(str,buf); - - appendStringInfo(str," :innerNode "); - appendStringInfo(str,buf); - _outNode(str, node->fj_innerNode); - - sprintf(buf, " :results @ 0x%x ", (int)(node->fj_results)); - appendStringInfo(str, buf); - - appendStringInfo( str, " :alwaysdone "); - for (i = 0; i<node->fj_nNodes; i++) + char buf[500]; + int i; + + sprintf(buf, "FJOIN"); + appendStringInfo(str, buf); + sprintf(buf, " :initialized %s", node->fj_initialized ? "true" : "nil"); + appendStringInfo(str, buf); + sprintf(buf, " :nNodes %d", node->fj_nNodes); + appendStringInfo(str, buf); + + appendStringInfo(str, " :innerNode "); + appendStringInfo(str, buf); + _outNode(str, node->fj_innerNode); + + sprintf(buf, " :results @ 0x%x ", (int) (node->fj_results)); + appendStringInfo(str, buf); + + appendStringInfo(str, " :alwaysdone "); + for (i = 0; i < node->fj_nNodes; i++) { - sprintf(buf, " %s ", ((node->fj_alwaysDone[i]) ? "true" : "nil")); - appendStringInfo(str, buf); + sprintf(buf, " %s ", ((node->fj_alwaysDone[i]) ? "true" : "nil")); + appendStringInfo(str, buf); } } /* - * Expr is a subclass of Node + * Expr is a subclass of Node */ static void -_outExpr(StringInfo str, Expr *node) +_outExpr(StringInfo str, Expr * node) { - char buf[500]; - char *opstr = NULL; - - sprintf(buf, "EXPR"); - appendStringInfo(str,buf); - - sprintf(buf, " :typeOid %u", node->typeOid); - appendStringInfo(str,buf); - switch(node->opType) { - case OP_EXPR: - opstr = "op"; - break; - case FUNC_EXPR: - opstr = "func"; - break; - case OR_EXPR: - opstr = "or"; - break; - case AND_EXPR: - opstr = "and"; - break; - case NOT_EXPR: - opstr = "not"; - break; - } - sprintf(buf, " :opType %s", opstr); - appendStringInfo(str,buf); - sprintf(buf, " :oper "); - appendStringInfo(str,buf); - _outNode(str, node->oper); - sprintf(buf, " :args "); - appendStringInfo(str,buf); - _outNode(str, node->args); + char buf[500]; + char *opstr = NULL; + + sprintf(buf, "EXPR"); + appendStringInfo(str, buf); + + sprintf(buf, " :typeOid %u", node->typeOid); + appendStringInfo(str, buf); + switch (node->opType) + { + case OP_EXPR: + opstr = "op"; + break; + case FUNC_EXPR: + opstr = "func"; + break; + case OR_EXPR: + opstr = "or"; + break; + case AND_EXPR: + opstr = "and"; + break; + case NOT_EXPR: + opstr = "not"; + break; + } + sprintf(buf, " :opType %s", opstr); + appendStringInfo(str, buf); + sprintf(buf, " :oper "); + appendStringInfo(str, buf); + _outNode(str, node->oper); + sprintf(buf, " :args "); + appendStringInfo(str, buf); + _outNode(str, node->args); } /* - * Var is a subclass of Expr + * Var is a subclass of Expr */ static void -_outVar(StringInfo str, Var *node) +_outVar(StringInfo str, Var * node) { - char buf[500]; - - sprintf(buf, "VAR"); - appendStringInfo(str,buf); - sprintf(buf, " :varno %d", node->varno); - appendStringInfo(str,buf); - sprintf(buf, " :varattno %hd", node->varattno); - appendStringInfo(str,buf); - sprintf(buf, " :vartype %u", node->vartype); - appendStringInfo(str,buf); - sprintf(buf, " :varnoold %d", node->varnoold); - appendStringInfo(str,buf); - sprintf(buf, " :varoattno %d", node->varoattno); - appendStringInfo(str,buf); + char buf[500]; + + sprintf(buf, "VAR"); + appendStringInfo(str, buf); + sprintf(buf, " :varno %d", node->varno); + appendStringInfo(str, buf); + sprintf(buf, " :varattno %hd", node->varattno); + appendStringInfo(str, buf); + sprintf(buf, " :vartype %u", node->vartype); + appendStringInfo(str, buf); + sprintf(buf, " :varnoold %d", node->varnoold); + appendStringInfo(str, buf); + sprintf(buf, " :varoattno %d", node->varoattno); + appendStringInfo(str, buf); } /* - * Const is a subclass of Expr + * Const is a subclass of Expr */ static void -_outConst(StringInfo str, Const *node) +_outConst(StringInfo str, Const * node) { - char buf[500]; - - sprintf(buf, "CONST"); - appendStringInfo(str,buf); - sprintf(buf, " :consttype %u", node->consttype); - appendStringInfo(str,buf); - sprintf(buf, " :constlen %hd", node->constlen); - appendStringInfo(str,buf); - sprintf(buf, " :constisnull %s", (node->constisnull ? "true" : "nil")); - appendStringInfo(str,buf); - sprintf(buf, " :constvalue "); - appendStringInfo(str,buf); - if (node->constisnull) { - sprintf(buf, "NIL "); - appendStringInfo(str,buf); - } else { - _outDatum(str, node->constvalue, node->consttype); - } - sprintf(buf, " :constbyval %s", (node->constbyval ? "true" : "nil")); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "CONST"); + appendStringInfo(str, buf); + sprintf(buf, " :consttype %u", node->consttype); + appendStringInfo(str, buf); + sprintf(buf, " :constlen %hd", node->constlen); + appendStringInfo(str, buf); + sprintf(buf, " :constisnull %s", (node->constisnull ? "true" : "nil")); + appendStringInfo(str, buf); + sprintf(buf, " :constvalue "); + appendStringInfo(str, buf); + if (node->constisnull) + { + sprintf(buf, "NIL "); + appendStringInfo(str, buf); + } + else + { + _outDatum(str, node->constvalue, node->consttype); + } + sprintf(buf, " :constbyval %s", (node->constbyval ? "true" : "nil")); + appendStringInfo(str, buf); + } /* - * Aggreg + * Aggreg */ static void -_outAggreg(StringInfo str, Aggreg *node) +_outAggreg(StringInfo str, Aggreg * node) { - char buf[500]; - - sprintf(buf, "AGGREG"); - appendStringInfo(str,buf); - sprintf(buf, " :aggname \"%s\"", (char*)node->aggname); - appendStringInfo(str,buf); - sprintf(buf, " :basetype %u", node->basetype); - appendStringInfo(str,buf); - sprintf(buf, " :aggtype %u", node->aggtype); - appendStringInfo(str,buf); - sprintf(buf, " :aggno %d", node->aggno); - appendStringInfo(str,buf); - - sprintf(buf, " :target "); - appendStringInfo(str,buf); - _outNode(str, node->target); + char buf[500]; + + sprintf(buf, "AGGREG"); + appendStringInfo(str, buf); + sprintf(buf, " :aggname \"%s\"", (char *) node->aggname); + appendStringInfo(str, buf); + sprintf(buf, " :basetype %u", node->basetype); + appendStringInfo(str, buf); + sprintf(buf, " :aggtype %u", node->aggtype); + appendStringInfo(str, buf); + sprintf(buf, " :aggno %d", node->aggno); + appendStringInfo(str, buf); + + sprintf(buf, " :target "); + appendStringInfo(str, buf); + _outNode(str, node->target); } /* - * Array is a subclass of Expr + * Array is a subclass of Expr */ static void -_outArray(StringInfo str, Array *node) +_outArray(StringInfo str, Array * node) { - char buf[500]; - int i; - sprintf(buf, "ARRAY"); - appendStringInfo(str, buf); - sprintf(buf, " :arrayelemtype %u", node->arrayelemtype); - appendStringInfo(str, buf); - sprintf(buf, " :arrayelemlength %d", node->arrayelemlength); - appendStringInfo(str, buf); - sprintf(buf, " :arrayelembyval %c", (node->arrayelembyval) ? 't' : 'f'); - appendStringInfo(str, buf); - sprintf(buf, " :arrayndim %d", node->arrayndim); - appendStringInfo(str, buf); - sprintf(buf, " :arraylow "); - appendStringInfo(str, buf); - for (i = 0; i < node->arrayndim; i++){ - sprintf(buf, " %d", node->arraylow.indx[i]); - appendStringInfo(str, buf); - } - sprintf(buf, " :arrayhigh "); - appendStringInfo(str, buf); - for (i = 0; i < node->arrayndim; i++){ - sprintf(buf, " %d", node->arrayhigh.indx[i]); - appendStringInfo(str, buf); - } - sprintf(buf, " :arraylen %d", node->arraylen); - appendStringInfo(str, buf); + char buf[500]; + int i; + + sprintf(buf, "ARRAY"); + appendStringInfo(str, buf); + sprintf(buf, " :arrayelemtype %u", node->arrayelemtype); + appendStringInfo(str, buf); + sprintf(buf, " :arrayelemlength %d", node->arrayelemlength); + appendStringInfo(str, buf); + sprintf(buf, " :arrayelembyval %c", (node->arrayelembyval) ? 't' : 'f'); + appendStringInfo(str, buf); + sprintf(buf, " :arrayndim %d", node->arrayndim); + appendStringInfo(str, buf); + sprintf(buf, " :arraylow "); + appendStringInfo(str, buf); + for (i = 0; i < node->arrayndim; i++) + { + sprintf(buf, " %d", node->arraylow.indx[i]); + appendStringInfo(str, buf); + } + sprintf(buf, " :arrayhigh "); + appendStringInfo(str, buf); + for (i = 0; i < node->arrayndim; i++) + { + sprintf(buf, " %d", node->arrayhigh.indx[i]); + appendStringInfo(str, buf); + } + sprintf(buf, " :arraylen %d", node->arraylen); + appendStringInfo(str, buf); } /* - * ArrayRef is a subclass of Expr + * ArrayRef is a subclass of Expr */ static void -_outArrayRef(StringInfo str, ArrayRef *node) +_outArrayRef(StringInfo str, ArrayRef * node) { - char buf[500]; - - sprintf(buf, "ARRAYREF"); - appendStringInfo(str, buf); - sprintf(buf, " :refelemtype %u", node->refelemtype); - appendStringInfo(str, buf); - sprintf(buf, " :refattrlength %d", node->refattrlength); - appendStringInfo(str, buf); - sprintf(buf, " :refelemlength %d", node->refelemlength); - appendStringInfo(str, buf); - sprintf(buf, " :refelembyval %c", (node->refelembyval) ? 't' : 'f'); - appendStringInfo(str, buf); - - sprintf(buf, " :refupperindex "); - appendStringInfo(str, buf); - _outNode(str, node->refupperindexpr); - - sprintf(buf, " :reflowerindex "); - appendStringInfo(str, buf); - _outNode(str, node->reflowerindexpr); - - sprintf(buf, " :refexpr "); - appendStringInfo(str, buf); - _outNode(str, node->refexpr); - - sprintf(buf, " :refassgnexpr "); - appendStringInfo(str, buf); - _outNode(str, node->refassgnexpr); + char buf[500]; + + sprintf(buf, "ARRAYREF"); + appendStringInfo(str, buf); + sprintf(buf, " :refelemtype %u", node->refelemtype); + appendStringInfo(str, buf); + sprintf(buf, " :refattrlength %d", node->refattrlength); + appendStringInfo(str, buf); + sprintf(buf, " :refelemlength %d", node->refelemlength); + appendStringInfo(str, buf); + sprintf(buf, " :refelembyval %c", (node->refelembyval) ? 't' : 'f'); + appendStringInfo(str, buf); + + sprintf(buf, " :refupperindex "); + appendStringInfo(str, buf); + _outNode(str, node->refupperindexpr); + + sprintf(buf, " :reflowerindex "); + appendStringInfo(str, buf); + _outNode(str, node->reflowerindexpr); + + sprintf(buf, " :refexpr "); + appendStringInfo(str, buf); + _outNode(str, node->refexpr); + + sprintf(buf, " :refassgnexpr "); + appendStringInfo(str, buf); + _outNode(str, node->refassgnexpr); } /* - * Func is a subclass of Expr + * Func is a subclass of Expr */ static void -_outFunc(StringInfo str, Func *node) +_outFunc(StringInfo str, Func * node) { - char buf[500]; - - sprintf(buf, "FUNC"); - appendStringInfo(str,buf); - sprintf(buf, " :funcid %u", node->funcid); - appendStringInfo(str,buf); - sprintf(buf, " :functype %u", node->functype); - appendStringInfo(str,buf); - sprintf(buf, " :funcisindex %s", - (node->funcisindex ? "true" : "nil")); - appendStringInfo(str,buf); - sprintf(buf, " :funcsize %d", node->funcsize); - appendStringInfo(str, buf); - sprintf(buf, " :func_fcache @ 0x%x", (int)(node->func_fcache)); - appendStringInfo(str, buf); - - appendStringInfo(str, " :func_tlist "); - _outNode(str, node->func_tlist); - - appendStringInfo(str, " :func_planlist "); - _outNode(str, node->func_planlist); + char buf[500]; + + sprintf(buf, "FUNC"); + appendStringInfo(str, buf); + sprintf(buf, " :funcid %u", node->funcid); + appendStringInfo(str, buf); + sprintf(buf, " :functype %u", node->functype); + appendStringInfo(str, buf); + sprintf(buf, " :funcisindex %s", + (node->funcisindex ? "true" : "nil")); + appendStringInfo(str, buf); + sprintf(buf, " :funcsize %d", node->funcsize); + appendStringInfo(str, buf); + sprintf(buf, " :func_fcache @ 0x%x", (int) (node->func_fcache)); + appendStringInfo(str, buf); + + appendStringInfo(str, " :func_tlist "); + _outNode(str, node->func_tlist); + + appendStringInfo(str, " :func_planlist "); + _outNode(str, node->func_planlist); } /* - * Oper is a subclass of Expr + * Oper is a subclass of Expr */ static void -_outOper(StringInfo str, Oper *node) +_outOper(StringInfo str, Oper * node) { - char buf[500]; - - sprintf(buf, "OPER"); - appendStringInfo(str,buf); - sprintf(buf, " :opno %u", node->opno); - appendStringInfo(str,buf); - sprintf(buf, " :opid %u", node->opid); - appendStringInfo(str,buf); - sprintf(buf, " :opresulttype %u", node->opresulttype); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "OPER"); + appendStringInfo(str, buf); + sprintf(buf, " :opno %u", node->opno); + appendStringInfo(str, buf); + sprintf(buf, " :opid %u", node->opid); + appendStringInfo(str, buf); + sprintf(buf, " :opresulttype %u", node->opresulttype); + appendStringInfo(str, buf); + } /* - * Param is a subclass of Expr + * Param is a subclass of Expr */ static void -_outParam(StringInfo str, Param *node) +_outParam(StringInfo str, Param * node) { - char buf[500]; - - sprintf(buf, "PARAM"); - appendStringInfo(str,buf); - sprintf(buf, " :paramkind %d", node->paramkind); - appendStringInfo(str,buf); - sprintf(buf, " :paramid %hd", node->paramid); - appendStringInfo(str,buf); - sprintf(buf, " :paramname \"%s\"", node->paramname); - appendStringInfo(str,buf); - sprintf(buf, " :paramtype %u", node->paramtype); - appendStringInfo(str,buf); - - appendStringInfo(str, " :param_tlist "); - _outNode(str, node->param_tlist); + char buf[500]; + + sprintf(buf, "PARAM"); + appendStringInfo(str, buf); + sprintf(buf, " :paramkind %d", node->paramkind); + appendStringInfo(str, buf); + sprintf(buf, " :paramid %hd", node->paramid); + appendStringInfo(str, buf); + sprintf(buf, " :paramname \"%s\"", node->paramname); + appendStringInfo(str, buf); + sprintf(buf, " :paramtype %u", node->paramtype); + appendStringInfo(str, buf); + + appendStringInfo(str, " :param_tlist "); + _outNode(str, node->param_tlist); } /* - * Stuff from execnodes.h + * Stuff from execnodes.h */ /* - * EState is a subclass of Node. + * EState is a subclass of Node. */ static void -_outEState(StringInfo str, EState *node) +_outEState(StringInfo str, EState * node) { - char buf[500]; - - sprintf(buf, "ESTATE"); - appendStringInfo(str,buf); - sprintf(buf, " :direction %d", node->es_direction); - appendStringInfo(str,buf); - - sprintf(buf, " :range_table "); - appendStringInfo(str,buf); - _outNode(str, node->es_range_table); - - sprintf(buf, " :result_relation_info @ 0x%x", - (int) (node->es_result_relation_info)); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "ESTATE"); + appendStringInfo(str, buf); + sprintf(buf, " :direction %d", node->es_direction); + appendStringInfo(str, buf); + + sprintf(buf, " :range_table "); + appendStringInfo(str, buf); + _outNode(str, node->es_range_table); + + sprintf(buf, " :result_relation_info @ 0x%x", + (int) (node->es_result_relation_info)); + appendStringInfo(str, buf); + } /* - * Stuff from relation.h + * Stuff from relation.h */ static void -_outRel(StringInfo str, Rel *node) +_outRel(StringInfo str, Rel * node) { - char buf[500]; - - sprintf(buf, "REL"); - appendStringInfo(str,buf); - - sprintf(buf, " :relids "); - appendStringInfo(str,buf); - _outIntList(str, node->relids); - - sprintf(buf, " :indexed %s", (node->indexed ? "true" : "nil")); - appendStringInfo(str,buf); - sprintf(buf, " :pages %u", node->pages); - appendStringInfo(str,buf); - sprintf(buf, " :tuples %u", node->tuples); - appendStringInfo(str,buf); - sprintf(buf, " :size %u", node->size); - appendStringInfo(str,buf); - sprintf(buf, " :width %u", node->width); - appendStringInfo(str,buf); - - sprintf(buf, " :targetlist "); - appendStringInfo(str,buf); - _outNode(str, node->targetlist); - - sprintf(buf, " :pathlist "); - appendStringInfo(str,buf); - _outNode(str, node->pathlist); - - /* - * Not sure if these are nodes or not. They're declared as - * struct Path *. Since i don't know, i'll just print the - * addresses for now. This can be changed later, if necessary. - */ - - sprintf(buf, " :unorderedpath @ 0x%x", (int)(node->unorderedpath)); - appendStringInfo(str,buf); - sprintf(buf, " :cheapestpath @ 0x%x", (int)(node->cheapestpath)); - appendStringInfo(str,buf); - - sprintf(buf, " :pruneable %s", (node->pruneable ? "true" : "nil")); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "REL"); + appendStringInfo(str, buf); + + sprintf(buf, " :relids "); + appendStringInfo(str, buf); + _outIntList(str, node->relids); + + sprintf(buf, " :indexed %s", (node->indexed ? "true" : "nil")); + appendStringInfo(str, buf); + sprintf(buf, " :pages %u", node->pages); + appendStringInfo(str, buf); + sprintf(buf, " :tuples %u", node->tuples); + appendStringInfo(str, buf); + sprintf(buf, " :size %u", node->size); + appendStringInfo(str, buf); + sprintf(buf, " :width %u", node->width); + appendStringInfo(str, buf); + + sprintf(buf, " :targetlist "); + appendStringInfo(str, buf); + _outNode(str, node->targetlist); + + sprintf(buf, " :pathlist "); + appendStringInfo(str, buf); + _outNode(str, node->pathlist); + + /* + * Not sure if these are nodes or not. They're declared as struct + * Path *. Since i don't know, i'll just print the addresses for now. + * This can be changed later, if necessary. + */ + + sprintf(buf, " :unorderedpath @ 0x%x", (int) (node->unorderedpath)); + appendStringInfo(str, buf); + sprintf(buf, " :cheapestpath @ 0x%x", (int) (node->cheapestpath)); + appendStringInfo(str, buf); + + sprintf(buf, " :pruneable %s", (node->pruneable ? "true" : "nil")); + appendStringInfo(str, buf); + #if 0 - sprintf(buf, " :classlist "); - appendStringInfo(str,buf); - _outNode(str, node->classlist); - - sprintf(buf, " :indexkeys "); - appendStringInfo(str,buf); - _outNode(str, node->indexkeys); - - sprintf(buf, " :ordering "); - appendStringInfo(str,buf); - _outNode(str, node->ordering); -#endif - - sprintf(buf, " :clauseinfo "); - appendStringInfo(str,buf); - _outNode(str, node->clauseinfo); - - sprintf(buf, " :joininfo "); - appendStringInfo(str,buf); - _outNode(str, node->joininfo); - - sprintf(buf, " :innerjoin "); - appendStringInfo(str,buf); - _outNode(str, node->innerjoin); - + sprintf(buf, " :classlist "); + appendStringInfo(str, buf); + _outNode(str, node->classlist); + + sprintf(buf, " :indexkeys "); + appendStringInfo(str, buf); + _outNode(str, node->indexkeys); + + sprintf(buf, " :ordering "); + appendStringInfo(str, buf); + _outNode(str, node->ordering); +#endif + + sprintf(buf, " :clauseinfo "); + appendStringInfo(str, buf); + _outNode(str, node->clauseinfo); + + sprintf(buf, " :joininfo "); + appendStringInfo(str, buf); + _outNode(str, node->joininfo); + + sprintf(buf, " :innerjoin "); + appendStringInfo(str, buf); + _outNode(str, node->innerjoin); + } /* - * TargetEntry is a subclass of Node. + * TargetEntry is a subclass of Node. */ static void -_outTargetEntry(StringInfo str, TargetEntry *node) +_outTargetEntry(StringInfo str, TargetEntry * node) { - char buf[500]; - - sprintf(buf, "TLE"); - appendStringInfo(str,buf); - sprintf(buf, " :resdom "); - appendStringInfo(str,buf); - _outNode(str, node->resdom); - - sprintf(buf, " :expr "); - appendStringInfo(str,buf); - if (node->expr) { - _outNode(str, node->expr); - }else { - appendStringInfo(str, "nil"); - } -} + char buf[500]; + + sprintf(buf, "TLE"); + appendStringInfo(str, buf); + sprintf(buf, " :resdom "); + appendStringInfo(str, buf); + _outNode(str, node->resdom); + + sprintf(buf, " :expr "); + appendStringInfo(str, buf); + if (node->expr) + { + _outNode(str, node->expr); + } + else + { + appendStringInfo(str, "nil"); + } +} static void -_outRangeTblEntry(StringInfo str, RangeTblEntry *node) +_outRangeTblEntry(StringInfo str, RangeTblEntry * node) { - char buf[500]; - - sprintf(buf, "RTE"); - appendStringInfo(str,buf); - - sprintf(buf, " :relname \"%s\"", - ((node->relname) ? ((char *) node->relname) : "null")); - appendStringInfo(str,buf); - - sprintf(buf, " :inh %d ", node->inh); - appendStringInfo(str,buf); - - sprintf(buf, " :refname \"%s\"", - ((node->refname) ? ((char *) node->refname) : "null")); - appendStringInfo(str,buf); - - sprintf(buf, " :relid %u ", node->relid); - appendStringInfo(str,buf); -} + char buf[500]; + + sprintf(buf, "RTE"); + appendStringInfo(str, buf); + + sprintf(buf, " :relname \"%s\"", + ((node->relname) ? ((char *) node->relname) : "null")); + appendStringInfo(str, buf); + + sprintf(buf, " :inh %d ", node->inh); + appendStringInfo(str, buf); + + sprintf(buf, " :refname \"%s\"", + ((node->refname) ? ((char *) node->refname) : "null")); + appendStringInfo(str, buf); + + sprintf(buf, " :relid %u ", node->relid); + appendStringInfo(str, buf); +} /* - * Path is a subclass of Node. + * Path is a subclass of Node. */ static void -_outPath(StringInfo str, Path *node) +_outPath(StringInfo str, Path * node) { - char buf[500]; - - sprintf(buf, "PATH"); - appendStringInfo(str,buf); - - sprintf(buf, " :pathtype %d", node->pathtype); - appendStringInfo(str,buf); - - sprintf(buf, " :cost %f", node->path_cost); - appendStringInfo(str,buf); - - sprintf(buf, " :keys "); - appendStringInfo(str,buf); - _outNode(str, node->keys); - + char buf[500]; + + sprintf(buf, "PATH"); + appendStringInfo(str, buf); + + sprintf(buf, " :pathtype %d", node->pathtype); + appendStringInfo(str, buf); + + sprintf(buf, " :cost %f", node->path_cost); + appendStringInfo(str, buf); + + sprintf(buf, " :keys "); + appendStringInfo(str, buf); + _outNode(str, node->keys); + } /* - * IndexPath is a subclass of Path. + * IndexPath is a subclass of Path. */ static void -_outIndexPath(StringInfo str, IndexPath *node) +_outIndexPath(StringInfo str, IndexPath * node) { - char buf[500]; - - sprintf(buf, "INDEXPATH"); - appendStringInfo(str,buf); - - sprintf(buf, " :pathtype %d", node->path.pathtype); - appendStringInfo(str,buf); - - /* sprintf(buf, " :parent "); - appendStringInfo(str,buf); - _outNode(str, node->parent); */ - - sprintf(buf, " :cost %f", node->path.path_cost); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "INDEXPATH"); + appendStringInfo(str, buf); + + sprintf(buf, " :pathtype %d", node->path.pathtype); + appendStringInfo(str, buf); + + /* + * sprintf(buf, " :parent "); appendStringInfo(str,buf); _outNode(str, + * node->parent); + */ + + sprintf(buf, " :cost %f", node->path.path_cost); + appendStringInfo(str, buf); + #if 0 - sprintf(buf, " :p_ordering "); - appendStringInfo(str,buf); - _outNode(str, node->path.p_ordering); -#endif - sprintf(buf, " :keys "); - appendStringInfo(str,buf); - _outNode(str, node->path.keys); - - sprintf(buf, " :indexid "); - appendStringInfo(str,buf); - _outIntList(str, node->indexid); - - sprintf(buf, " :indexqual "); - appendStringInfo(str,buf); - _outNode(str, node->indexqual); - + sprintf(buf, " :p_ordering "); + appendStringInfo(str, buf); + _outNode(str, node->path.p_ordering); +#endif + sprintf(buf, " :keys "); + appendStringInfo(str, buf); + _outNode(str, node->path.keys); + + sprintf(buf, " :indexid "); + appendStringInfo(str, buf); + _outIntList(str, node->indexid); + + sprintf(buf, " :indexqual "); + appendStringInfo(str, buf); + _outNode(str, node->indexqual); + } /* - * JoinPath is a subclass of Path + * JoinPath is a subclass of Path */ static void -_outJoinPath(StringInfo str, JoinPath *node) +_outJoinPath(StringInfo str, JoinPath * node) { - char buf[500]; - - sprintf(buf, "JOINPATH"); - appendStringInfo(str,buf); - - sprintf(buf, " :pathtype %d", node->path.pathtype); - appendStringInfo(str,buf); - - /* sprintf(buf, " :parent "); - appendStringInfo(str,buf); - _outNode(str, node->parent); */ - - sprintf(buf, " :cost %f", node->path.path_cost); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "JOINPATH"); + appendStringInfo(str, buf); + + sprintf(buf, " :pathtype %d", node->path.pathtype); + appendStringInfo(str, buf); + + /* + * sprintf(buf, " :parent "); appendStringInfo(str,buf); _outNode(str, + * node->parent); + */ + + sprintf(buf, " :cost %f", node->path.path_cost); + appendStringInfo(str, buf); + #if 0 - sprintf(buf, " :p_ordering "); - appendStringInfo(str,buf); - _outNode(str, node->path.p_ordering); -#endif - sprintf(buf, " :keys "); - appendStringInfo(str,buf); - _outNode(str, node->path.keys); - - sprintf(buf, " :pathclauseinfo "); - appendStringInfo(str,buf); - _outNode(str, node->pathclauseinfo); - - /* - * Not sure if these are nodes; they're declared as "struct path *". - * For now, i'll just print the addresses. - */ - - sprintf(buf, " :outerjoinpath @ 0x%x", (int)(node->outerjoinpath)); - appendStringInfo(str,buf); - sprintf(buf, " :innerjoinpath @ 0x%x", (int)(node->innerjoinpath)); - appendStringInfo(str,buf); - - sprintf(buf, " :outerjoincost %f", node->path.outerjoincost); - appendStringInfo(str,buf); - - sprintf(buf, " :joinid "); - appendStringInfo(str,buf); - _outIntList(str, node->path.joinid); - + sprintf(buf, " :p_ordering "); + appendStringInfo(str, buf); + _outNode(str, node->path.p_ordering); +#endif + sprintf(buf, " :keys "); + appendStringInfo(str, buf); + _outNode(str, node->path.keys); + + sprintf(buf, " :pathclauseinfo "); + appendStringInfo(str, buf); + _outNode(str, node->pathclauseinfo); + + /* + * Not sure if these are nodes; they're declared as "struct path *". + * For now, i'll just print the addresses. + */ + + sprintf(buf, " :outerjoinpath @ 0x%x", (int) (node->outerjoinpath)); + appendStringInfo(str, buf); + sprintf(buf, " :innerjoinpath @ 0x%x", (int) (node->innerjoinpath)); + appendStringInfo(str, buf); + + sprintf(buf, " :outerjoincost %f", node->path.outerjoincost); + appendStringInfo(str, buf); + + sprintf(buf, " :joinid "); + appendStringInfo(str, buf); + _outIntList(str, node->path.joinid); + } /* - * MergePath is a subclass of JoinPath. + * MergePath is a subclass of JoinPath. */ static void -_outMergePath(StringInfo str, MergePath *node) +_outMergePath(StringInfo str, MergePath * node) { - char buf[500]; - - sprintf(buf, "MERGEPATH"); - appendStringInfo(str,buf); - - sprintf(buf, " :pathtype %d", node->jpath.path.pathtype); - appendStringInfo(str,buf); - - sprintf(buf, " :cost %f", node->jpath.path.path_cost); - appendStringInfo(str,buf); - - sprintf(buf, " :keys "); - appendStringInfo(str,buf); - _outNode(str, node->jpath.path.keys); - - sprintf(buf, " :pathclauseinfo "); - appendStringInfo(str,buf); - _outNode(str, node->jpath.pathclauseinfo); - - /* - * Not sure if these are nodes; they're declared as "struct path *". - * For now, i'll just print the addresses. - */ - - sprintf(buf, " :outerjoinpath @ 0x%x", (int)(node->jpath.outerjoinpath)); - appendStringInfo(str,buf); - sprintf(buf, " :innerjoinpath @ 0x%x", (int)(node->jpath.innerjoinpath)); - appendStringInfo(str,buf); - - sprintf(buf, " :outerjoincost %f", node->jpath.path.outerjoincost); - appendStringInfo(str,buf); - - sprintf(buf, " :joinid "); - appendStringInfo(str,buf); - _outIntList(str, node->jpath.path.joinid); - - sprintf(buf, " :path_mergeclauses "); - appendStringInfo(str,buf); - _outNode(str, node->path_mergeclauses); - - sprintf(buf, " :outersortkeys "); - appendStringInfo(str,buf); - _outNode(str, node->outersortkeys); - - sprintf(buf, " :innersortkeys "); - appendStringInfo(str,buf); - _outNode(str, node->innersortkeys); - + char buf[500]; + + sprintf(buf, "MERGEPATH"); + appendStringInfo(str, buf); + + sprintf(buf, " :pathtype %d", node->jpath.path.pathtype); + appendStringInfo(str, buf); + + sprintf(buf, " :cost %f", node->jpath.path.path_cost); + appendStringInfo(str, buf); + + sprintf(buf, " :keys "); + appendStringInfo(str, buf); + _outNode(str, node->jpath.path.keys); + + sprintf(buf, " :pathclauseinfo "); + appendStringInfo(str, buf); + _outNode(str, node->jpath.pathclauseinfo); + + /* + * Not sure if these are nodes; they're declared as "struct path *". + * For now, i'll just print the addresses. + */ + + sprintf(buf, " :outerjoinpath @ 0x%x", (int) (node->jpath.outerjoinpath)); + appendStringInfo(str, buf); + sprintf(buf, " :innerjoinpath @ 0x%x", (int) (node->jpath.innerjoinpath)); + appendStringInfo(str, buf); + + sprintf(buf, " :outerjoincost %f", node->jpath.path.outerjoincost); + appendStringInfo(str, buf); + + sprintf(buf, " :joinid "); + appendStringInfo(str, buf); + _outIntList(str, node->jpath.path.joinid); + + sprintf(buf, " :path_mergeclauses "); + appendStringInfo(str, buf); + _outNode(str, node->path_mergeclauses); + + sprintf(buf, " :outersortkeys "); + appendStringInfo(str, buf); + _outNode(str, node->outersortkeys); + + sprintf(buf, " :innersortkeys "); + appendStringInfo(str, buf); + _outNode(str, node->innersortkeys); + } /* - * HashPath is a subclass of JoinPath. + * HashPath is a subclass of JoinPath. */ static void -_outHashPath(StringInfo str, HashPath *node) +_outHashPath(StringInfo str, HashPath * node) { - char buf[500]; - - sprintf(buf, "HASHPATH"); - appendStringInfo(str,buf); - - sprintf(buf, " :pathtype %d", node->jpath.path.pathtype); - appendStringInfo(str,buf); - - sprintf(buf, " :cost %f", node->jpath.path.path_cost); - appendStringInfo(str,buf); - - sprintf(buf, " :keys "); - appendStringInfo(str,buf); - _outNode(str, node->jpath.path.keys); - - sprintf(buf, " :pathclauseinfo "); - appendStringInfo(str,buf); - _outNode(str, node->jpath.pathclauseinfo); - - /* - * Not sure if these are nodes; they're declared as "struct path *". - * For now, i'll just print the addresses. - */ - - sprintf(buf, " :outerjoinpath @ 0x%x", (int) (node->jpath.outerjoinpath)); - appendStringInfo(str,buf); - sprintf(buf, " :innerjoinpath @ 0x%x", (int) (node->jpath.innerjoinpath)); - appendStringInfo(str,buf); - - sprintf(buf, " :outerjoincost %f", node->jpath.path.outerjoincost); - appendStringInfo(str,buf); - - sprintf(buf, " :joinid "); - appendStringInfo(str,buf); - _outIntList(str, node->jpath.path.joinid); - - sprintf(buf, " :path_hashclauses "); - appendStringInfo(str,buf); - _outNode(str, node->path_hashclauses); - - sprintf(buf, " :outerhashkeys "); - appendStringInfo(str,buf); - _outNode(str, node->outerhashkeys); - - sprintf(buf, " :innerhashkeys "); - appendStringInfo(str,buf); - _outNode(str, node->innerhashkeys); - + char buf[500]; + + sprintf(buf, "HASHPATH"); + appendStringInfo(str, buf); + + sprintf(buf, " :pathtype %d", node->jpath.path.pathtype); + appendStringInfo(str, buf); + + sprintf(buf, " :cost %f", node->jpath.path.path_cost); + appendStringInfo(str, buf); + + sprintf(buf, " :keys "); + appendStringInfo(str, buf); + _outNode(str, node->jpath.path.keys); + + sprintf(buf, " :pathclauseinfo "); + appendStringInfo(str, buf); + _outNode(str, node->jpath.pathclauseinfo); + + /* + * Not sure if these are nodes; they're declared as "struct path *". + * For now, i'll just print the addresses. + */ + + sprintf(buf, " :outerjoinpath @ 0x%x", (int) (node->jpath.outerjoinpath)); + appendStringInfo(str, buf); + sprintf(buf, " :innerjoinpath @ 0x%x", (int) (node->jpath.innerjoinpath)); + appendStringInfo(str, buf); + + sprintf(buf, " :outerjoincost %f", node->jpath.path.outerjoincost); + appendStringInfo(str, buf); + + sprintf(buf, " :joinid "); + appendStringInfo(str, buf); + _outIntList(str, node->jpath.path.joinid); + + sprintf(buf, " :path_hashclauses "); + appendStringInfo(str, buf); + _outNode(str, node->path_hashclauses); + + sprintf(buf, " :outerhashkeys "); + appendStringInfo(str, buf); + _outNode(str, node->outerhashkeys); + + sprintf(buf, " :innerhashkeys "); + appendStringInfo(str, buf); + _outNode(str, node->innerhashkeys); + } /* - * OrderKey is a subclass of Node. + * OrderKey is a subclass of Node. */ static void -_outOrderKey(StringInfo str, OrderKey *node) +_outOrderKey(StringInfo str, OrderKey * node) { - char buf[500]; - - sprintf(buf, "ORDERKEY"); - appendStringInfo(str,buf); - sprintf(buf, " :attribute_number %d", node->attribute_number); - appendStringInfo(str,buf); - sprintf(buf, " :array_index %d", node->array_index); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "ORDERKEY"); + appendStringInfo(str, buf); + sprintf(buf, " :attribute_number %d", node->attribute_number); + appendStringInfo(str, buf); + sprintf(buf, " :array_index %d", node->array_index); + appendStringInfo(str, buf); + } /* - * JoinKey is a subclass of Node. + * JoinKey is a subclass of Node. */ static void -_outJoinKey(StringInfo str, JoinKey *node) +_outJoinKey(StringInfo str, JoinKey * node) { - char buf[500]; - - sprintf(buf, "JOINKEY"); - appendStringInfo(str,buf); - - sprintf(buf, " :outer "); - appendStringInfo(str,buf); - _outNode(str, node->outer); - - sprintf(buf, " :inner "); - appendStringInfo(str,buf); - _outNode(str, node->inner); - + char buf[500]; + + sprintf(buf, "JOINKEY"); + appendStringInfo(str, buf); + + sprintf(buf, " :outer "); + appendStringInfo(str, buf); + _outNode(str, node->outer); + + sprintf(buf, " :inner "); + appendStringInfo(str, buf); + _outNode(str, node->inner); + } /* - * MergeOrder is a subclass of Node. + * MergeOrder is a subclass of Node. */ static void -_outMergeOrder(StringInfo str, MergeOrder *node) +_outMergeOrder(StringInfo str, MergeOrder * node) { - char buf[500]; - - sprintf(buf, "MERGEORDER"); - appendStringInfo(str,buf); - - sprintf(buf, " :join_operator %d", node->join_operator); - appendStringInfo(str,buf); - sprintf(buf, " :left_operator %d", node->left_operator); - appendStringInfo(str,buf); - sprintf(buf, " :right_operator %d", node->right_operator); - appendStringInfo(str,buf); - sprintf(buf, " :left_type %d", node->left_type); - appendStringInfo(str,buf); - sprintf(buf, " :right_type %d", node->right_type); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "MERGEORDER"); + appendStringInfo(str, buf); + + sprintf(buf, " :join_operator %d", node->join_operator); + appendStringInfo(str, buf); + sprintf(buf, " :left_operator %d", node->left_operator); + appendStringInfo(str, buf); + sprintf(buf, " :right_operator %d", node->right_operator); + appendStringInfo(str, buf); + sprintf(buf, " :left_type %d", node->left_type); + appendStringInfo(str, buf); + sprintf(buf, " :right_type %d", node->right_type); + appendStringInfo(str, buf); + } /* - * CInfo is a subclass of Node. + * CInfo is a subclass of Node. */ static void -_outCInfo(StringInfo str, CInfo *node) +_outCInfo(StringInfo str, CInfo * node) { - char buf[500]; - - sprintf(buf, "CINFO"); - appendStringInfo(str,buf); - - sprintf(buf, " :clause "); - appendStringInfo(str,buf); - _outNode(str, node->clause); - - sprintf(buf, " :selectivity %f", node->selectivity); - appendStringInfo(str,buf); - sprintf(buf, " :notclause %s", (node->notclause ? "true" : "nil")); - appendStringInfo(str,buf); - - sprintf(buf, " :indexids "); - appendStringInfo(str,buf); - _outNode(str, node->indexids); - - sprintf(buf, " :mergesortorder "); - appendStringInfo(str,buf); - _outNode(str, node->mergesortorder); - - sprintf(buf, " :hashjoinoperator %u", node->hashjoinoperator); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "CINFO"); + appendStringInfo(str, buf); + + sprintf(buf, " :clause "); + appendStringInfo(str, buf); + _outNode(str, node->clause); + + sprintf(buf, " :selectivity %f", node->selectivity); + appendStringInfo(str, buf); + sprintf(buf, " :notclause %s", (node->notclause ? "true" : "nil")); + appendStringInfo(str, buf); + + sprintf(buf, " :indexids "); + appendStringInfo(str, buf); + _outNode(str, node->indexids); + + sprintf(buf, " :mergesortorder "); + appendStringInfo(str, buf); + _outNode(str, node->mergesortorder); + + sprintf(buf, " :hashjoinoperator %u", node->hashjoinoperator); + appendStringInfo(str, buf); + } /* - * JoinMethod is a subclass of Node. + * JoinMethod is a subclass of Node. */ static void -_outJoinMethod(StringInfo str, JoinMethod *node) +_outJoinMethod(StringInfo str, JoinMethod * node) { - char buf[500]; - - sprintf(buf, "JOINMETHOD"); - appendStringInfo(str,buf); - - sprintf(buf, " :jmkeys "); - appendStringInfo(str,buf); - _outNode(str, node->jmkeys); - - sprintf(buf, " :clauses "); - appendStringInfo(str,buf); - _outNode(str, node->clauses); - - + char buf[500]; + + sprintf(buf, "JOINMETHOD"); + appendStringInfo(str, buf); + + sprintf(buf, " :jmkeys "); + appendStringInfo(str, buf); + _outNode(str, node->jmkeys); + + sprintf(buf, " :clauses "); + appendStringInfo(str, buf); + _outNode(str, node->clauses); + + } /* * HInfo is a subclass of JoinMethod. */ static void -_outHInfo(StringInfo str, HInfo *node) +_outHInfo(StringInfo str, HInfo * node) { - char buf[500]; - - sprintf(buf, "HASHINFO"); - appendStringInfo(str,buf); - - sprintf(buf, " :hashop "); - appendStringInfo(str,buf); - sprintf(buf, "%u",node->hashop); - appendStringInfo(str,buf); - - sprintf(buf, " :jmkeys "); - appendStringInfo(str,buf); - _outNode(str, node->jmethod.jmkeys); - - sprintf(buf, " :clauses "); - appendStringInfo(str,buf); - _outNode(str, node->jmethod.clauses); - + char buf[500]; + + sprintf(buf, "HASHINFO"); + appendStringInfo(str, buf); + + sprintf(buf, " :hashop "); + appendStringInfo(str, buf); + sprintf(buf, "%u", node->hashop); + appendStringInfo(str, buf); + + sprintf(buf, " :jmkeys "); + appendStringInfo(str, buf); + _outNode(str, node->jmethod.jmkeys); + + sprintf(buf, " :clauses "); + appendStringInfo(str, buf); + _outNode(str, node->jmethod.clauses); + } /* - * JInfo is a subclass of Node. + * JInfo is a subclass of Node. */ static void -_outJInfo(StringInfo str, JInfo *node) +_outJInfo(StringInfo str, JInfo * node) { - char buf[500]; - - sprintf(buf, "JINFO"); - appendStringInfo(str,buf); - - sprintf(buf, " :otherrels "); - appendStringInfo(str,buf); - _outIntList(str, node->otherrels); - - sprintf(buf, " :jinfoclauseinfo "); - appendStringInfo(str,buf); - _outNode(str, node->jinfoclauseinfo); - - sprintf(buf, " :mergesortable %s", - (node->mergesortable ? "true" : "nil")); - appendStringInfo(str,buf); - sprintf(buf, " :hashjoinable %s", - (node->hashjoinable ? "true" : "nil")); - appendStringInfo(str,buf); - + char buf[500]; + + sprintf(buf, "JINFO"); + appendStringInfo(str, buf); + + sprintf(buf, " :otherrels "); + appendStringInfo(str, buf); + _outIntList(str, node->otherrels); + + sprintf(buf, " :jinfoclauseinfo "); + appendStringInfo(str, buf); + _outNode(str, node->jinfoclauseinfo); + + sprintf(buf, " :mergesortable %s", + (node->mergesortable ? "true" : "nil")); + appendStringInfo(str, buf); + sprintf(buf, " :hashjoinable %s", + (node->hashjoinable ? "true" : "nil")); + appendStringInfo(str, buf); + } /* @@ -1352,319 +1369,340 @@ _outJInfo(StringInfo str, JInfo *node) static void _outDatum(StringInfo str, Datum value, Oid type) { - char buf[500]; - Size length, typeLength; - bool byValue; - int i; - char *s; - - /* - * find some information about the type and the "real" length - * of the datum. - */ - byValue = get_typbyval(type); - typeLength = get_typlen(type); - length = datumGetSize(value, type, byValue, typeLength); - - if (byValue) { - s = (char *) (&value); - sprintf(buf, " %d [ ", length); - appendStringInfo(str,buf); - for (i=0; i<sizeof(Datum); i++) { - sprintf(buf, "%d ", (int) (s[i]) ); - appendStringInfo(str,buf); + char buf[500]; + Size length, + typeLength; + bool byValue; + int i; + char *s; + + /* + * find some information about the type and the "real" length of the + * datum. + */ + byValue = get_typbyval(type); + typeLength = get_typlen(type); + length = datumGetSize(value, type, byValue, typeLength); + + if (byValue) + { + s = (char *) (&value); + sprintf(buf, " %d [ ", length); + appendStringInfo(str, buf); + for (i = 0; i < sizeof(Datum); i++) + { + sprintf(buf, "%d ", (int) (s[i])); + appendStringInfo(str, buf); + } + sprintf(buf, "] "); + appendStringInfo(str, buf); } - sprintf(buf, "] "); - appendStringInfo(str,buf); - } else { /* !byValue */ - s = (char *) DatumGetPointer(value); - if (!PointerIsValid(s)) { - sprintf(buf, " 0 [ ] "); - appendStringInfo(str,buf); - } else { - /* - * length is unsigned - very bad to do < comparison to -1 without - * casting it to int first!! -mer 8 Jan 1991 - */ - if (((int)length) <= -1) { - length = VARSIZE(s); - } - sprintf(buf, " %d [ ", length); - appendStringInfo(str,buf); - for (i=0; i<length; i++) { - sprintf(buf, "%d ", (int) (s[i]) ); - appendStringInfo(str,buf); - } - sprintf(buf, "] "); - appendStringInfo(str,buf); + else + { /* !byValue */ + s = (char *) DatumGetPointer(value); + if (!PointerIsValid(s)) + { + sprintf(buf, " 0 [ ] "); + appendStringInfo(str, buf); + } + else + { + + /* + * length is unsigned - very bad to do < comparison to -1 + * without casting it to int first!! -mer 8 Jan 1991 + */ + if (((int) length) <= -1) + { + length = VARSIZE(s); + } + sprintf(buf, " %d [ ", length); + appendStringInfo(str, buf); + for (i = 0; i < length; i++) + { + sprintf(buf, "%d ", (int) (s[i])); + appendStringInfo(str, buf); + } + sprintf(buf, "] "); + appendStringInfo(str, buf); + } } - } - + } static void -_outIter(StringInfo str, Iter *node) +_outIter(StringInfo str, Iter * node) { - appendStringInfo(str,"ITER"); - - appendStringInfo(str," :iterexpr "); - _outNode(str, node->iterexpr); + appendStringInfo(str, "ITER"); + + appendStringInfo(str, " :iterexpr "); + _outNode(str, node->iterexpr); } static void -_outStream(StringInfo str, Stream *node) +_outStream(StringInfo str, Stream * node) { - char buf[500]; - - appendStringInfo(str,"STREAM"); - - sprintf(buf, " :pathptr @ 0x%x", (int)(node->pathptr)); - appendStringInfo(str,buf); - - sprintf(buf, " :cinfo @ 0x%x", (int)(node->cinfo)); - appendStringInfo(str,buf); - - sprintf(buf, " :clausetype %d", (int)(node->clausetype)); - appendStringInfo(str,buf); - - sprintf(buf, " :upstream @ 0x%x", (int)(node->upstream)); - appendStringInfo(str,buf); - - sprintf(buf, " :downstream @ 0x%x", (int)(node->downstream)); - appendStringInfo(str,buf); - - sprintf(buf, " :groupup %d", node->groupup); - appendStringInfo(str,buf); - - sprintf(buf, " :groupcost %f", node->groupcost); - appendStringInfo(str,buf); - - sprintf(buf, " :groupsel %f", node->groupsel); - appendStringInfo(str,buf); -} + char buf[500]; + + appendStringInfo(str, "STREAM"); + + sprintf(buf, " :pathptr @ 0x%x", (int) (node->pathptr)); + appendStringInfo(str, buf); + + sprintf(buf, " :cinfo @ 0x%x", (int) (node->cinfo)); + appendStringInfo(str, buf); + + sprintf(buf, " :clausetype %d", (int) (node->clausetype)); + appendStringInfo(str, buf); + + sprintf(buf, " :upstream @ 0x%x", (int) (node->upstream)); + appendStringInfo(str, buf); + + sprintf(buf, " :downstream @ 0x%x", (int) (node->downstream)); + appendStringInfo(str, buf); + + sprintf(buf, " :groupup %d", node->groupup); + appendStringInfo(str, buf); + + sprintf(buf, " :groupcost %f", node->groupcost); + appendStringInfo(str, buf); + + sprintf(buf, " :groupsel %f", node->groupsel); + appendStringInfo(str, buf); +} static void -_outValue(StringInfo str, Value *value) +_outValue(StringInfo str, Value * value) { - char buf[500]; - - switch(value->type) { - case T_String: - sprintf(buf, "\"%s\"", value->val.str); - appendStringInfo(str, buf); - break; - case T_Integer: - sprintf(buf, "%ld", value->val.ival); - appendStringInfo(str, buf); - break; - case T_Float: - sprintf(buf, "%f", value->val.dval); - appendStringInfo(str, buf); - break; - default: - break; - } - return; + char buf[500]; + + switch (value->type) + { + case T_String: + sprintf(buf, "\"%s\"", value->val.str); + appendStringInfo(str, buf); + break; + case T_Integer: + sprintf(buf, "%ld", value->val.ival); + appendStringInfo(str, buf); + break; + case T_Float: + sprintf(buf, "%f", value->val.dval); + appendStringInfo(str, buf); + break; + default: + break; + } + return; } /* * _outNode - - * converts a Node into ascii string and append it to 'str' + * converts a Node into ascii string and append it to 'str' */ static void _outNode(StringInfo str, void *obj) { - if (obj==NULL) { - appendStringInfo(str, "nil"); - return; - } + if (obj == NULL) + { + appendStringInfo(str, "nil"); + return; + } - if (nodeTag(obj)==T_List) { - List *l; - appendStringInfo(str, "("); - foreach(l, (List*)obj) { - _outNode(str, lfirst(l)); - if (lnext(l)) - appendStringInfo(str, " "); + if (nodeTag(obj) == T_List) + { + List *l; + + appendStringInfo(str, "("); + foreach(l, (List *) obj) + { + _outNode(str, lfirst(l)); + if (lnext(l)) + appendStringInfo(str, " "); + } + appendStringInfo(str, ")"); } - appendStringInfo(str, ")"); - }else { - appendStringInfo(str, "{"); - switch(nodeTag(obj)) { - case T_Query: - _outQuery(str, obj); - break; - case T_Plan: - _outPlan(str, obj); - break; - case T_Result: - _outResult(str, obj); - break; - case T_Existential: - _outExistential(str, obj); - break; - case T_Append: - _outAppend(str, obj); - break; - case T_Join: - _outJoin(str, obj); - break; - case T_NestLoop: - _outNestLoop(str, obj); - break; - case T_MergeJoin: - _outMergeJoin(str, obj); - break; - case T_HashJoin: - _outHashJoin(str, obj); - break; - case T_Scan: - _outScan(str, obj); - break; - case T_SeqScan: - _outSeqScan(str, obj); - break; - case T_IndexScan: - _outIndexScan(str, obj); - break; - case T_Temp: - _outTemp(str, obj); - break; - case T_Sort: - _outSort(str, obj); - break; - case T_Agg: - _outAgg(str, obj); - break; - case T_Group: - _outGroup(str, obj); - break; - case T_Unique: - _outUnique(str, obj); - break; - case T_Hash: - _outHash(str, obj); - break; - case T_Tee: - _outTee(str, obj); - break; - case T_Resdom: - _outResdom(str, obj); - break; - case T_Fjoin: - _outFjoin(str, obj); - break; - case T_Expr: - _outExpr(str, obj); - break; - case T_Var: - _outVar(str, obj); - break; - case T_Const: - _outConst(str, obj); - break; - case T_Aggreg: - _outAggreg(str, obj); - break; - case T_Array: - _outArray(str, obj); - break; - case T_ArrayRef: - _outArrayRef(str, obj); - break; - case T_Func: - _outFunc(str, obj); - break; - case T_Oper: - _outOper(str, obj); - break; - case T_Param: - _outParam(str, obj); - break; - case T_EState: - _outEState(str, obj); - break; - case T_Rel: - _outRel(str, obj); - break; - case T_TargetEntry: - _outTargetEntry(str, obj); - break; - case T_RangeTblEntry: - _outRangeTblEntry(str, obj); - break; - case T_Path: - _outPath(str, obj); - break; - case T_IndexPath: - _outIndexPath (str, obj); - break; - case T_JoinPath: - _outJoinPath(str, obj); - break; - case T_MergePath: - _outMergePath(str, obj); - break; - case T_HashPath: - _outHashPath(str, obj); - break; - case T_OrderKey: - _outOrderKey(str, obj); - break; - case T_JoinKey: - _outJoinKey(str, obj); - break; - case T_MergeOrder: - _outMergeOrder(str, obj); - break; - case T_CInfo: - _outCInfo(str, obj); - break; - case T_JoinMethod: - _outJoinMethod(str, obj); - break; - case T_HInfo: - _outHInfo(str, obj); - break; - case T_JInfo: - _outJInfo(str, obj); - break; - case T_Iter: - _outIter(str, obj); - break; - case T_Stream: - _outStream(str, obj); - break; - case T_Integer: case T_String: case T_Float: - _outValue(str, obj); - break; - default: - elog(NOTICE, "_outNode: don't know how to print type %d", - nodeTag(obj)); - break; + else + { + appendStringInfo(str, "{"); + switch (nodeTag(obj)) + { + case T_Query: + _outQuery(str, obj); + break; + case T_Plan: + _outPlan(str, obj); + break; + case T_Result: + _outResult(str, obj); + break; + case T_Existential: + _outExistential(str, obj); + break; + case T_Append: + _outAppend(str, obj); + break; + case T_Join: + _outJoin(str, obj); + break; + case T_NestLoop: + _outNestLoop(str, obj); + break; + case T_MergeJoin: + _outMergeJoin(str, obj); + break; + case T_HashJoin: + _outHashJoin(str, obj); + break; + case T_Scan: + _outScan(str, obj); + break; + case T_SeqScan: + _outSeqScan(str, obj); + break; + case T_IndexScan: + _outIndexScan(str, obj); + break; + case T_Temp: + _outTemp(str, obj); + break; + case T_Sort: + _outSort(str, obj); + break; + case T_Agg: + _outAgg(str, obj); + break; + case T_Group: + _outGroup(str, obj); + break; + case T_Unique: + _outUnique(str, obj); + break; + case T_Hash: + _outHash(str, obj); + break; + case T_Tee: + _outTee(str, obj); + break; + case T_Resdom: + _outResdom(str, obj); + break; + case T_Fjoin: + _outFjoin(str, obj); + break; + case T_Expr: + _outExpr(str, obj); + break; + case T_Var: + _outVar(str, obj); + break; + case T_Const: + _outConst(str, obj); + break; + case T_Aggreg: + _outAggreg(str, obj); + break; + case T_Array: + _outArray(str, obj); + break; + case T_ArrayRef: + _outArrayRef(str, obj); + break; + case T_Func: + _outFunc(str, obj); + break; + case T_Oper: + _outOper(str, obj); + break; + case T_Param: + _outParam(str, obj); + break; + case T_EState: + _outEState(str, obj); + break; + case T_Rel: + _outRel(str, obj); + break; + case T_TargetEntry: + _outTargetEntry(str, obj); + break; + case T_RangeTblEntry: + _outRangeTblEntry(str, obj); + break; + case T_Path: + _outPath(str, obj); + break; + case T_IndexPath: + _outIndexPath(str, obj); + break; + case T_JoinPath: + _outJoinPath(str, obj); + break; + case T_MergePath: + _outMergePath(str, obj); + break; + case T_HashPath: + _outHashPath(str, obj); + break; + case T_OrderKey: + _outOrderKey(str, obj); + break; + case T_JoinKey: + _outJoinKey(str, obj); + break; + case T_MergeOrder: + _outMergeOrder(str, obj); + break; + case T_CInfo: + _outCInfo(str, obj); + break; + case T_JoinMethod: + _outJoinMethod(str, obj); + break; + case T_HInfo: + _outHInfo(str, obj); + break; + case T_JInfo: + _outJInfo(str, obj); + break; + case T_Iter: + _outIter(str, obj); + break; + case T_Stream: + _outStream(str, obj); + break; + case T_Integer: + case T_String: + case T_Float: + _outValue(str, obj); + break; + default: + elog(NOTICE, "_outNode: don't know how to print type %d", + nodeTag(obj)); + break; + } + appendStringInfo(str, "}"); } - appendStringInfo(str, "}"); - } - return; + return; } /* * nodeToString - - * returns the ascii representation of the Node + * returns the ascii representation of the Node */ -char * +char * nodeToString(void *obj) { - StringInfo str; - char *s; - - if (obj==NULL) - return ""; - Assert(obj!=NULL); - str = makeStringInfo(); - _outNode(str, obj); - s = str->data; - pfree(str); - - return s; + StringInfo str; + char *s; + + if (obj == NULL) + return ""; + Assert(obj != NULL); + str = makeStringInfo(); + _outNode(str, obj); + s = str->data; + pfree(str); + + return s; } diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index 0f92c189e51..9fb61ed3ea7 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * print.c-- - * various print routines (used mostly for debugging) + * various print routines (used mostly for debugging) * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.6 1997/08/19 21:31:43 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.7 1997/09/07 04:42:55 momjian Exp $ * * HISTORY - * AUTHOR DATE MAJOR EVENT - * Andrew Yu Oct 26, 1994 file creation + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Oct 26, 1994 file creation * *------------------------------------------------------------------------- */ @@ -33,21 +33,21 @@ #include "nodes/plannodes.h" #include "optimizer/clauses.h" -static char *plannode_type (Plan* p); +static char *plannode_type(Plan * p); /* * print-- - * print contents of Node to stdout + * print contents of Node to stdout */ void print(void *obj) { - char *s; + char *s; - s = nodeToString(obj); - printf("%s\n", s); - fflush(stdout); - return; + s = nodeToString(obj); + printf("%s\n", s); + fflush(stdout); + return; } /* @@ -56,327 +56,365 @@ print(void *obj) void pprint(void *obj) { - char *s; - int i; - char line[80]; - int indentLev; - int j; - - s = nodeToString(obj); - - indentLev = 0; - i = 0; - for(;;) { - for(j=0; j<indentLev*3; j++) { - line[j] = ' '; - } - for( ; j<75 && s[i]!='\0'; i++, j++) { - line[j] = s[i]; - switch (line[j]) { - case '}': - if (j != indentLev*3) { - line[j] = '\0'; - printf("%s\n",line); - line[indentLev*3] = '\0'; - printf("%s}\n",line); - }else { - line[j] = '\0'; - printf("%s}\n",line); - } - indentLev--; - j = indentLev*3-1; /* print the line before : and resets */ - break; - case ')': - line[j+1] = '\0'; - printf("%s\n", line); - j = indentLev*3-1; - break; - case '{': - indentLev++; - /* !!! FALLS THROUGH */ - case ':': - if (j != 0) { - line[j] = '\0'; - printf("%s\n",line); - /* print the line before : and resets */ - for(j=0; j<indentLev*3; j++) { + char *s; + int i; + char line[80]; + int indentLev; + int j; + + s = nodeToString(obj); + + indentLev = 0; + i = 0; + for (;;) + { + for (j = 0; j < indentLev * 3; j++) + { line[j] = ' '; - } } - line[j] = s[i]; - break; - } + for (; j < 75 && s[i] != '\0'; i++, j++) + { + line[j] = s[i]; + switch (line[j]) + { + case '}': + if (j != indentLev * 3) + { + line[j] = '\0'; + printf("%s\n", line); + line[indentLev * 3] = '\0'; + printf("%s}\n", line); + } + else + { + line[j] = '\0'; + printf("%s}\n", line); + } + indentLev--; + j = indentLev * 3 - 1; /* print the line before : and + * resets */ + break; + case ')': + line[j + 1] = '\0'; + printf("%s\n", line); + j = indentLev * 3 - 1; + break; + case '{': + indentLev++; + /* !!! FALLS THROUGH */ + case ':': + if (j != 0) + { + line[j] = '\0'; + printf("%s\n", line); + /* print the line before : and resets */ + for (j = 0; j < indentLev * 3; j++) + { + line[j] = ' '; + } + } + line[j] = s[i]; + break; + } + } + line[j] = '\0'; + if (s[i] == '\0') + break; + printf("%s\n", line); } - line[j] = '\0'; - if (s[i]=='\0') - break; - printf("%s\n", line); - } - if (j!=0) { - printf("%s\n", line); - } - fflush(stdout); - return; + if (j != 0) + { + printf("%s\n", line); + } + fflush(stdout); + return; } /* * print_rt-- - * print contents of range table + * print contents of range table */ void -print_rt(List *rtable) +print_rt(List * rtable) { - List *l; - int i=1; - - printf("resno\trelname(refname)\trelid\tinFromCl\n"); - printf("-----\t----------------\t-----\t--------\n"); - foreach(l, rtable) { - RangeTblEntry *rte = lfirst(l); - printf("%d\t%s(%s)\t%d\t%d\t%s\n", - i,rte->relname,rte->refname,rte->relid, - rte->inFromCl, - (rte->inh?"inh":"")); - i++; - } + List *l; + int i = 1; + + printf("resno\trelname(refname)\trelid\tinFromCl\n"); + printf("-----\t----------------\t-----\t--------\n"); + foreach(l, rtable) + { + RangeTblEntry *rte = lfirst(l); + + printf("%d\t%s(%s)\t%d\t%d\t%s\n", + i, rte->relname, rte->refname, rte->relid, + rte->inFromCl, + (rte->inh ? "inh" : "")); + i++; + } } /* * print_expr-- - * print an expression + * print an expression */ void -print_expr(Node *expr, List *rtable) +print_expr(Node * expr, List * rtable) { - if (expr==NULL) { - printf("nil"); - return; - } - - if (IsA(expr,Var)) { - Var *var = (Var*)expr; - RangeTblEntry *rt; - char *relname, *attname; - - switch (var->varno) { - case INNER: - relname = "INNER"; - attname = "?"; - break; - case OUTER: - relname = "OUTER"; - attname = "?"; - break; - default: - { - Relation r; - rt = rt_fetch(var->varno, rtable); - relname = rt->relname; - r = heap_openr(relname); - if (rt->refname) - relname = rt->refname; /* table renamed */ - attname = getAttrName(r, var->varattno); - heap_close(r); - } - break; + if (expr == NULL) + { + printf("nil"); + return; } - printf("%s.%s",relname,attname); - } else if (IsA(expr,Expr)) { - Expr *e = (Expr*)expr; - if (is_opclause(expr)) { - char *opname; - - print_expr((Node*)get_leftop(e), rtable); - opname = get_opname(((Oper*)e->oper)->opno); - printf(" %s ", opname); - print_expr((Node*)get_rightop(e), rtable); - } else { - printf("an expr"); + + if (IsA(expr, Var)) + { + Var *var = (Var *) expr; + RangeTblEntry *rt; + char *relname, + *attname; + + switch (var->varno) + { + case INNER: + relname = "INNER"; + attname = "?"; + break; + case OUTER: + relname = "OUTER"; + attname = "?"; + break; + default: + { + Relation r; + + rt = rt_fetch(var->varno, rtable); + relname = rt->relname; + r = heap_openr(relname); + if (rt->refname) + relname = rt->refname; /* table renamed */ + attname = getAttrName(r, var->varattno); + heap_close(r); + } + break; + } + printf("%s.%s", relname, attname); + } + else if (IsA(expr, Expr)) + { + Expr *e = (Expr *) expr; + + if (is_opclause(expr)) + { + char *opname; + + print_expr((Node *) get_leftop(e), rtable); + opname = get_opname(((Oper *) e->oper)->opno); + printf(" %s ", opname); + print_expr((Node *) get_rightop(e), rtable); + } + else + { + printf("an expr"); + } + } + else + { + printf("not an expr"); } - } else { - printf("not an expr"); - } } /* * print_keys - - * temporary here. where is keys list of list?? + * temporary here. where is keys list of list?? */ void -print_keys(List *keys, List *rtable) +print_keys(List * keys, List * rtable) { - List *k; - - printf("("); - foreach(k, keys) { - Node *var = lfirst((List*)lfirst(k)); - print_expr(var, rtable); - if (lnext(k)) printf(", "); - } - printf(")\n"); + List *k; + + printf("("); + foreach(k, keys) + { + Node *var = lfirst((List *) lfirst(k)); + + print_expr(var, rtable); + if (lnext(k)) + printf(", "); + } + printf(")\n"); } /* * print_tl -- - * print targetlist in a more legible way. + * print targetlist in a more legible way. */ -void -print_tl(List *tlist, List *rtable) +void +print_tl(List * tlist, List * rtable) { - List *tl; + List *tl; - printf("(\n"); - foreach(tl, tlist) { - TargetEntry *tle = lfirst(tl); + printf("(\n"); + foreach(tl, tlist) + { + TargetEntry *tle = lfirst(tl); - printf("\t%d %s\t", tle->resdom->resno, tle->resdom->resname); - if (tle->resdom->reskey!=0) { - printf("(%d):\t", tle->resdom->reskey); - } else { - printf(" :\t"); + printf("\t%d %s\t", tle->resdom->resno, tle->resdom->resname); + if (tle->resdom->reskey != 0) + { + printf("(%d):\t", tle->resdom->reskey); + } + else + { + printf(" :\t"); + } + print_expr(tle->expr, rtable); + printf("\n"); } - print_expr(tle->expr, rtable); - printf("\n"); - } - printf(")\n"); + printf(")\n"); } /* * print_slot-- - * print out the tuple with the given TupleTableSlot + * print out the tuple with the given TupleTableSlot */ void -print_slot(TupleTableSlot *slot) +print_slot(TupleTableSlot * slot) { - if (!slot->val) { - printf("tuple is null.\n"); - return; - } - if (!slot->ttc_tupleDescriptor) { - printf("no tuple descriptor.\n"); - return; - } - - debugtup(slot->val, slot->ttc_tupleDescriptor); + if (!slot->val) + { + printf("tuple is null.\n"); + return; + } + if (!slot->ttc_tupleDescriptor) + { + printf("no tuple descriptor.\n"); + return; + } + + debugtup(slot->val, slot->ttc_tupleDescriptor); } -static char * -plannode_type (Plan* p) +static char * +plannode_type(Plan * p) { - switch(nodeTag(p)) { - case T_Plan: - return "PLAN"; - break; - case T_Existential: - return "EXISTENTIAL"; - break; - case T_Result: - return "RESULT"; - break; - case T_Append: - return "APPEND"; - break; - case T_Scan: - return "SCAN"; - break; - case T_SeqScan: - return "SEQSCAN"; - break; - case T_IndexScan: - return "INDEXSCAN"; - break; - case T_Join: - return "JOIN"; - break; - case T_NestLoop: - return "NESTLOOP"; - break; - case T_MergeJoin: - return "MERGEJOIN"; - break; - case T_HashJoin: - return "HASHJOIN"; - break; - case T_Temp: - return "TEMP"; - break; - case T_Material: - return "MATERIAL"; - break; - case T_Sort: - return "SORT"; - break; - case T_Agg: - return "AGG"; - break; - case T_Unique: - return "UNIQUE"; - break; - case T_Hash: - return "HASH"; - break; - case T_Tee: - return "TEE"; - break; - case T_Choose: - return "CHOOSE"; - break; - case T_Group: - return "GROUP"; - break; - default: - return "UNKNOWN"; - break; - } + switch (nodeTag(p)) + { + case T_Plan: + return "PLAN"; + break; + case T_Existential: + return "EXISTENTIAL"; + break; + case T_Result: + return "RESULT"; + break; + case T_Append: + return "APPEND"; + break; + case T_Scan: + return "SCAN"; + break; + case T_SeqScan: + return "SEQSCAN"; + break; + case T_IndexScan: + return "INDEXSCAN"; + break; + case T_Join: + return "JOIN"; + break; + case T_NestLoop: + return "NESTLOOP"; + break; + case T_MergeJoin: + return "MERGEJOIN"; + break; + case T_HashJoin: + return "HASHJOIN"; + break; + case T_Temp: + return "TEMP"; + break; + case T_Material: + return "MATERIAL"; + break; + case T_Sort: + return "SORT"; + break; + case T_Agg: + return "AGG"; + break; + case T_Unique: + return "UNIQUE"; + break; + case T_Hash: + return "HASH"; + break; + case T_Tee: + return "TEE"; + break; + case T_Choose: + return "CHOOSE"; + break; + case T_Group: + return "GROUP"; + break; + default: + return "UNKNOWN"; + break; + } } + /* prints the ascii description of the plan nodes does this recursively by doing a depth-first traversal of the plan tree. for SeqScan and IndexScan, the name of the table is also - printed out + printed out */ void -print_plan_recursive (Plan* p, Query *parsetree, int indentLevel, char* label) +print_plan_recursive(Plan * p, Query * parsetree, int indentLevel, char *label) { - int i; - char extraInfo[100]; + int i; + char extraInfo[100]; - if (!p) - return; - for (i=0;i<indentLevel;i++) - printf(" "); - printf("%s%s :c=%.4f :s=%d :w=%d ",label, plannode_type(p), - p->cost, p->plan_size, p->plan_width); - if (IsA(p,Scan) || IsA(p,SeqScan)) { - RangeTblEntry *rte; - rte = rt_fetch(((Scan*)p)->scanrelid, parsetree->rtable); - strNcpy(extraInfo, rte->relname, NAMEDATALEN-1); - } else - if (IsA(p,IndexScan)) { - strNcpy(extraInfo, - ((RangeTblEntry*)(nth(((IndexScan*)p)->scan.scanrelid - 1, - parsetree->rtable)))->relname, - NAMEDATALEN-1); - } else - extraInfo[0] = '\0'; - if (extraInfo[0] != '\0') - printf(" ( %s )\n", extraInfo); - else - printf("\n"); - print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: "); - print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: "); + if (!p) + return; + for (i = 0; i < indentLevel; i++) + printf(" "); + printf("%s%s :c=%.4f :s=%d :w=%d ", label, plannode_type(p), + p->cost, p->plan_size, p->plan_width); + if (IsA(p, Scan) || IsA(p, SeqScan)) + { + RangeTblEntry *rte; + + rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable); + strNcpy(extraInfo, rte->relname, NAMEDATALEN - 1); + } + else if (IsA(p, IndexScan)) + { + strNcpy(extraInfo, + ((RangeTblEntry *) (nth(((IndexScan *) p)->scan.scanrelid - 1, + parsetree->rtable)))->relname, + NAMEDATALEN - 1); + } + else + extraInfo[0] = '\0'; + if (extraInfo[0] != '\0') + printf(" ( %s )\n", extraInfo); + else + printf("\n"); + print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: "); + print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: "); } -/* print_plan +/* print_plan prints just the plan node types */ void -print_plan (Plan* p, Query* parsetree) +print_plan(Plan * p, Query * parsetree) { - print_plan_recursive(p, parsetree, 0, ""); + print_plan_recursive(p, parsetree, 0, ""); } - - - diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c index 7e89f9d74d3..f0baa4f14d7 100644 --- a/src/backend/nodes/read.c +++ b/src/backend/nodes/read.c @@ -1,18 +1,18 @@ /*------------------------------------------------------------------------- * * read.c-- - * routines to convert a string (legal ascii representation of node) back - * to nodes + * routines to convert a string (legal ascii representation of node) back + * to nodes * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.3 1997/08/12 22:53:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.4 1997/09/07 04:42:56 momjian Exp $ * * HISTORY - * AUTHOR DATE MAJOR EVENT - * Andrew Yu Nov 2, 1994 file creation + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Nov 2, 1994 file creation * *------------------------------------------------------------------------- */ @@ -26,17 +26,17 @@ /* * stringToNode - - * returns a Node with a given legal ascii representation + * returns a Node with a given legal ascii representation */ -void * +void * stringToNode(char *str) { - void *retval; + void *retval; - lsptok(str, NULL); /* set the string used in lsptok */ - retval = nodeRead(true); /* start reading */ + lsptok(str, NULL); /* set the string used in lsptok */ + retval = nodeRead(true); /* start reading */ - return retval; + return retval; } /***************************************************************************** @@ -46,115 +46,126 @@ stringToNode(char *str) *****************************************************************************/ #define RIGHT_PAREN (1000000 + 1) -#define LEFT_PAREN (1000000 + 2) -#define PLAN_SYM (1000000 + 3) -#define AT_SYMBOL (1000000 + 4) -#define ATOM_TOKEN (1000000 + 5) +#define LEFT_PAREN (1000000 + 2) +#define PLAN_SYM (1000000 + 3) +#define AT_SYMBOL (1000000 + 4) +#define ATOM_TOKEN (1000000 + 5) /* * nodeTokenType - - * returns the type of the node token contained in token. - * It returns one of the following valid NodeTags: - * T_Integer, T_Float, T_String - * and some of its own: - * RIGHT_PAREN, LEFT_PAREN, PLAN_SYM, AT_SYMBOL, ATOM_TOKEN + * returns the type of the node token contained in token. + * It returns one of the following valid NodeTags: + * T_Integer, T_Float, T_String + * and some of its own: + * RIGHT_PAREN, LEFT_PAREN, PLAN_SYM, AT_SYMBOL, ATOM_TOKEN * - * Assumption: the ascii representation is legal + * Assumption: the ascii representation is legal */ -static NodeTag +static NodeTag nodeTokenType(char *token, int length) { - NodeTag retval = 0; - - /* - * Check if the token is a number (decimal or integer, - * positive or negative - */ - if (isdigit(*token) || - (length>=2 && *token=='-' && isdigit(*(token+1)) )) + NodeTag retval = 0; + + /* + * Check if the token is a number (decimal or integer, positive or + * negative + */ + if (isdigit(*token) || + (length >= 2 && *token == '-' && isdigit(*(token + 1)))) { - /* - * skip the optional '-' (i.e. negative number) - */ - if (*token == '-') { - token++; - } - - /* - * See if there is a decimal point - */ - - for (; length && *token != '.'; token++, length--); - - /* - * if there isn't, token's an int, otherwise it's a float. - */ - - retval = (*token != '.') ? T_Integer : T_Float; + + /* + * skip the optional '-' (i.e. negative number) + */ + if (*token == '-') + { + token++; + } + + /* + * See if there is a decimal point + */ + + for (; length && *token != '.'; token++, length--); + + /* + * if there isn't, token's an int, otherwise it's a float. + */ + + retval = (*token != '.') ? T_Integer : T_Float; } - else if (isalpha(*token)) - retval = ATOM_TOKEN; - else if (*token == '(') - retval = LEFT_PAREN; - else if (*token == ')') - retval = RIGHT_PAREN; - else if (*token == '@') - retval = AT_SYMBOL; - else if (*token == '\"') - retval = T_String; - else if (*token == '{') - retval = PLAN_SYM; - return(retval); + else if (isalpha(*token)) + retval = ATOM_TOKEN; + else if (*token == '(') + retval = LEFT_PAREN; + else if (*token == ')') + retval = RIGHT_PAREN; + else if (*token == '@') + retval = AT_SYMBOL; + else if (*token == '\"') + retval = T_String; + else if (*token == '{') + retval = PLAN_SYM; + return (retval); } /* * Works kinda like strtok, except it doesn't put nulls into string. - * + * * Returns the length in length instead. The string can be set without * returning a token by calling lsptok with length == NULL. * */ -char * +char * lsptok(char *string, int *length) { - static char *local_str; - char *ret_string; - - if (string != NULL) { - local_str = string; - if (length == NULL) { - return(NULL); + static char *local_str; + char *ret_string; + + if (string != NULL) + { + local_str = string; + if (length == NULL) + { + return (NULL); + } + } + + for (; *local_str == ' ' + || *local_str == '\n' + || *local_str == '\t'; local_str++); + + /* + * Now pointing at next token. + */ + ret_string = local_str; + if (*local_str == '\0') + return (NULL); + *length = 1; + + if (*local_str == '\"') + { + for (local_str++; *local_str != '\"'; (*length)++, local_str++); + (*length)++; + local_str++; + } + else if (*local_str == ')' || *local_str == '(' || + *local_str == '}' || *local_str == '{') + { + local_str++; + } + else + { + for (; *local_str != ' ' + && *local_str != '\n' + && *local_str != '\t' + && *local_str != '{' + && *local_str != '}' + && *local_str != '(' + && *local_str != ')'; local_str++, (*length)++); + (*length)--; } - } - - for (; *local_str == ' ' - || *local_str == '\n' - || *local_str == '\t'; local_str++); - - /* - * Now pointing at next token. - */ - ret_string = local_str; - if (*local_str == '\0') return(NULL); - *length = 1; - - if (*local_str == '\"') { - for (local_str++; *local_str != '\"'; (*length)++, local_str++); - (*length)++; local_str++; - }else if (*local_str == ')' || *local_str == '(' || - *local_str == '}' || *local_str == '{') { - local_str++; - }else { - for (; *local_str != ' ' - && *local_str != '\n' - && *local_str != '\t' - && *local_str != '{' - && *local_str != '}' - && *local_str != '(' - && *local_str != ')'; local_str++, (*length)++); - (*length)--; - } - return(ret_string); + return (ret_string); } /* @@ -163,108 +174,128 @@ lsptok(char *string, int *length) * Secrets: He assumes that lsptok already has the string (see below). * Any callers should set read_car_only to true. */ -void * +void * nodeRead(bool read_car_only) { - char *token; - NodeTag type; - Node *this_value = NULL, *return_value = NULL; - int tok_len; - char tmp; - bool make_dotted_pair_cell = false; - - token = lsptok(NULL, &tok_len); - - if (token == NULL) return(NULL); - - type = nodeTokenType(token, tok_len); - - switch(type) { - case PLAN_SYM: - this_value = parsePlanString(); + char *token; + NodeTag type; + Node *this_value = NULL, + *return_value = NULL; + int tok_len; + char tmp; + bool make_dotted_pair_cell = false; + token = lsptok(NULL, &tok_len); - if (token[0] != '}') return(NULL); - if (!read_car_only) - make_dotted_pair_cell = true; - else - make_dotted_pair_cell = false; - break; - case LEFT_PAREN: - if (!read_car_only) { - List *l = makeNode(List); + if (token == NULL) + return (NULL); - lfirst(l) = nodeRead(false); - lnext(l) = nodeRead(false); - this_value = (Node*)l; - }else { - this_value = nodeRead(false); - } - break; - case RIGHT_PAREN: - this_value = NULL; - break; - case AT_SYMBOL: - break; - case ATOM_TOKEN: - if (!strncmp(token, "nil", 3)) { - this_value = NULL; - /* - * It might be "nil" but it is an atom! - */ - if (read_car_only) { - make_dotted_pair_cell = false; - } else { + type = nodeTokenType(token, tok_len); + + switch (type) + { + case PLAN_SYM: + this_value = parsePlanString(); + token = lsptok(NULL, &tok_len); + if (token[0] != '}') + return (NULL); + + if (!read_car_only) + make_dotted_pair_cell = true; + else + make_dotted_pair_cell = false; + break; + case LEFT_PAREN: + if (!read_car_only) + { + List *l = makeNode(List); + + lfirst(l) = nodeRead(false); + lnext(l) = nodeRead(false); + this_value = (Node *) l; + } + else + { + this_value = nodeRead(false); + } + break; + case RIGHT_PAREN: + this_value = NULL; + break; + case AT_SYMBOL: + break; + case ATOM_TOKEN: + if (!strncmp(token, "nil", 3)) + { + this_value = NULL; + + /* + * It might be "nil" but it is an atom! + */ + if (read_car_only) + { + make_dotted_pair_cell = false; + } + else + { + make_dotted_pair_cell = true; + } + } + else + { + tmp = token[tok_len]; + token[tok_len] = '\0'; + this_value = (Node *) pstrdup(token); /* !attention! not a + * Node. use with + * caution */ + token[tok_len] = tmp; + make_dotted_pair_cell = true; + } + break; + case T_Float: + tmp = token[tok_len]; + token[tok_len] = '\0'; + this_value = (Node *) makeFloat(atof(token)); + token[tok_len] = tmp; + make_dotted_pair_cell = true; + break; + case T_Integer: + tmp = token[tok_len]; + token[tok_len] = '\0'; + this_value = (Node *) makeInteger(atoi(token)); + token[tok_len] = tmp; make_dotted_pair_cell = true; - } - }else { - tmp = token[tok_len]; - token[tok_len] = '\0'; - this_value = (Node*)pstrdup(token); /* !attention! not a Node. - use with caution */ - token[tok_len] = tmp; - make_dotted_pair_cell = true; + break; + case T_String: + tmp = token[tok_len - 1]; + token[tok_len - 1] = '\0'; + token++; + this_value = (Node *) makeString(token); /* !! not strdup'd */ + token[tok_len - 2] = tmp; + make_dotted_pair_cell = true; + break; + default: + elog(WARN, "nodeRead: Bad type %d", type); + break; } - break; - case T_Float: - tmp = token[tok_len]; - token[tok_len] = '\0'; - this_value = (Node*)makeFloat(atof(token)); - token[tok_len] = tmp; - make_dotted_pair_cell = true; - break; - case T_Integer: - tmp = token[tok_len]; - token[tok_len] = '\0'; - this_value = (Node*)makeInteger(atoi(token)); - token[tok_len] = tmp; - make_dotted_pair_cell = true; - break; - case T_String: - tmp = token[tok_len - 1]; - token[tok_len - 1] = '\0'; - token++; - this_value = (Node*)makeString(token); /* !! not strdup'd */ - token[tok_len - 2] = tmp; - make_dotted_pair_cell = true; - break; - default: - elog(WARN, "nodeRead: Bad type %d", type); - break; - } - if (make_dotted_pair_cell) { - List *l = makeNode(List); + if (make_dotted_pair_cell) + { + List *l = makeNode(List); - lfirst(l) = this_value; - if (!read_car_only) { - lnext(l) = nodeRead(false); - }else { - lnext(l) = NULL; + lfirst(l) = this_value; + if (!read_car_only) + { + lnext(l) = nodeRead(false); + } + else + { + lnext(l) = NULL; + } + return_value = (Node *) l; } - return_value = (Node*)l; - }else { - return_value = this_value; - } - return(return_value); + else + { + return_value = this_value; + } + return (return_value); } - diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 736873bcba2..f42d5d536ee 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1,25 +1,25 @@ /*------------------------------------------------------------------------- * * readfuncs.c-- - * Reader functions for Postgres tree nodes. + * Reader functions for Postgres tree nodes. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.6 1997/08/12 20:15:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.7 1997/09/07 04:42:57 momjian Exp $ * * NOTES - * Most of the read functions for plan nodes are tested. (In fact, they - * pass the regression test as of 11/8/94.) The rest (for path selection) - * are probably never used. No effort has been made to get them to work. - * The simplest way to test these functions is by doing the following in - * ProcessQuery (before executing the plan): - * plan = stringToNode(nodeToString(plan)); - * Then, run the regression test. Let's just say you'll notice if either - * of the above function are not properly done. - * - ay 11/94 - * + * Most of the read functions for plan nodes are tested. (In fact, they + * pass the regression test as of 11/8/94.) The rest (for path selection) + * are probably never used. No effort has been made to get them to work. + * The simplest way to test these functions is by doing the following in + * ProcessQuery (before executing the plan): + * plan = stringToNode(nodeToString(plan)); + * Then, run the regression test. Let's just say you'll notice if either + * of the above function are not properly done. + * - ay 11/94 + * *------------------------------------------------------------------------- */ #include <stdio.h> @@ -47,697 +47,720 @@ #include "nodes/readfuncs.h" /* ---------------- - * node creator declarations + * node creator declarations * ---------------- */ -static Datum readDatum(Oid type); +static Datum readDatum(Oid type); -static List *toIntList(List *list) +static List * +toIntList(List * list) { - List *l; - foreach(l, list) { - /* ugly manipulation, should probably free the Value node too */ - lfirst(l) = (void*)intVal(lfirst(l)); - } - return list; + List *l; + + foreach(l, list) + { + /* ugly manipulation, should probably free the Value node too */ + lfirst(l) = (void *) intVal(lfirst(l)); + } + return list; } /* ---------------- - * _readQuery + * _readQuery * ---------------- */ -static Query * +static Query * _readQuery() { - Query *local_node; - char *token; - int length; - - local_node = makeNode(Query); - - token = lsptok(NULL, &length); /* skip the :command */ - token = lsptok(NULL, &length); /* get the commandType */ - local_node->commandType = atoi(token); - - token = lsptok(NULL, &length); /* skip the :utility */ - token = lsptok(NULL, &length); /* get the notify name if any*/ - if (token[0] == '"' && token[1] == '"') - local_node->utilityStmt = NULL; - else { - NotifyStmt *n = makeNode(NotifyStmt); - n->relname = palloc(length + 1); - strNcpy(n->relname,token,length); - local_node->utilityStmt = (Node*)n; - } - - token = lsptok(NULL, &length); /* skip the :resrel */ - token = lsptok(NULL, &length); /* get the resultRelation */ - local_node->resultRelation = atoi(token); - - token = lsptok(NULL, &length); /* skip :rtable */ - local_node->rtable = nodeRead(true); - - token = lsptok(NULL, &length); /* skip the :unique */ - token = lsptok(NULL, &length); /* get the uniqueFlag */ -/* local_node->uniqueFlag = (bool)atoi(token); */ - if (token[0]=='"' && token[1] == '"') /* non-unique */ - local_node->uniqueFlag = NULL; - else { - local_node->uniqueFlag = palloc(length + 1); - strNcpy(local_node->uniqueFlag,token,length); - } - - token = lsptok(NULL, &length); /* skip :targetlist */ - local_node->targetList = nodeRead(true); - - token = lsptok(NULL, &length); /* skip :qual */ - local_node->qual = nodeRead(true); - - return (local_node); + Query *local_node; + char *token; + int length; + + local_node = makeNode(Query); + + token = lsptok(NULL, &length); /* skip the :command */ + token = lsptok(NULL, &length); /* get the commandType */ + local_node->commandType = atoi(token); + + token = lsptok(NULL, &length); /* skip the :utility */ + token = lsptok(NULL, &length); /* get the notify name if any */ + if (token[0] == '"' && token[1] == '"') + local_node->utilityStmt = NULL; + else + { + NotifyStmt *n = makeNode(NotifyStmt); + + n->relname = palloc(length + 1); + strNcpy(n->relname, token, length); + local_node->utilityStmt = (Node *) n; + } + + token = lsptok(NULL, &length); /* skip the :resrel */ + token = lsptok(NULL, &length); /* get the resultRelation */ + local_node->resultRelation = atoi(token); + + token = lsptok(NULL, &length); /* skip :rtable */ + local_node->rtable = nodeRead(true); + + token = lsptok(NULL, &length); /* skip the :unique */ + token = lsptok(NULL, &length); /* get the uniqueFlag */ +/* local_node->uniqueFlag = (bool)atoi(token); */ + if (token[0] == '"' && token[1] == '"') /* non-unique */ + local_node->uniqueFlag = NULL; + else + { + local_node->uniqueFlag = palloc(length + 1); + strNcpy(local_node->uniqueFlag, token, length); + } + + token = lsptok(NULL, &length); /* skip :targetlist */ + local_node->targetList = nodeRead(true); + + token = lsptok(NULL, &length); /* skip :qual */ + local_node->qual = nodeRead(true); + + return (local_node); } /* ---------------- - * _getPlan + * _getPlan * ---------------- */ static void -_getPlan(Plan *node) +_getPlan(Plan * node) { - char *token; - int length; - - token = lsptok(NULL, &length); /* first token is :cost */ - token = lsptok(NULL, &length); /* next is the actual cost */ - node->cost = (Cost) atof(token); - - token = lsptok(NULL, &length); /* skip the :size */ - token = lsptok(NULL, &length); /* get the plan_size */ - node->plan_size = atoi(token); - - token = lsptok(NULL, &length); /* skip the :width */ - token = lsptok(NULL, &length); /* get the plan_width */ - node->plan_width = atoi(token); - - token = lsptok(NULL, &length); /* eat the :state stuff */ - token = lsptok(NULL, &length); /* now get the state */ - - if (!strncmp(token, "nil", 3)) { - node->state = (EState*) NULL; - }else { /* Disgusting hack until I figure out what to do here */ - node->state = (EState*) ! NULL; - } - - token = lsptok(NULL, &length); /* eat :qptargetlist */ - node->targetlist = nodeRead(true); - - token = lsptok(NULL, &length); /* eat :qpqual */ - node->qual = nodeRead(true); - - token = lsptok(NULL, &length); /* eat :lefttree */ - node->lefttree = (Plan*) nodeRead(true); - - token = lsptok(NULL, &length); /* eat :righttree */ - node->righttree = (Plan*) nodeRead(true); - - return; + char *token; + int length; + + token = lsptok(NULL, &length); /* first token is :cost */ + token = lsptok(NULL, &length); /* next is the actual cost */ + node->cost = (Cost) atof(token); + + token = lsptok(NULL, &length); /* skip the :size */ + token = lsptok(NULL, &length); /* get the plan_size */ + node->plan_size = atoi(token); + + token = lsptok(NULL, &length); /* skip the :width */ + token = lsptok(NULL, &length); /* get the plan_width */ + node->plan_width = atoi(token); + + token = lsptok(NULL, &length); /* eat the :state stuff */ + token = lsptok(NULL, &length); /* now get the state */ + + if (!strncmp(token, "nil", 3)) + { + node->state = (EState *) NULL; + } + else + { /* Disgusting hack until I figure out what + * to do here */ + node->state = (EState *) ! NULL; + } + + token = lsptok(NULL, &length); /* eat :qptargetlist */ + node->targetlist = nodeRead(true); + + token = lsptok(NULL, &length); /* eat :qpqual */ + node->qual = nodeRead(true); + + token = lsptok(NULL, &length); /* eat :lefttree */ + node->lefttree = (Plan *) nodeRead(true); + + token = lsptok(NULL, &length); /* eat :righttree */ + node->righttree = (Plan *) nodeRead(true); + + return; } /* - * Stuff from plannodes.h + * Stuff from plannodes.h */ /* ---------------- - * _readPlan + * _readPlan * ---------------- */ -static Plan * +static Plan * _readPlan() { - Plan *local_node; - - local_node = makeNode(Plan); - - _getPlan(local_node); - - return (local_node); + Plan *local_node; + + local_node = makeNode(Plan); + + _getPlan(local_node); + + return (local_node); } /* ---------------- - * _readResult + * _readResult * - * Does some obscene, possibly unportable, magic with - * sizes of things. + * Does some obscene, possibly unportable, magic with + * sizes of things. * ---------------- */ -static Result * +static Result * _readResult() { - Result *local_node; - char *token; - int length; - - local_node = makeNode(Result); - - _getPlan((Plan*)local_node); - - token = lsptok(NULL, &length); /* eat :resconstantqual */ - local_node->resconstantqual = nodeRead(true); /* now read it */ - - return( local_node ); + Result *local_node; + char *token; + int length; + + local_node = makeNode(Result); + + _getPlan((Plan *) local_node); + + token = lsptok(NULL, &length); /* eat :resconstantqual */ + local_node->resconstantqual = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readExistential + * _readExistential * - * Existential nodes are only used by the planner. + * Existential nodes are only used by the planner. * ---------------- */ static Existential * _readExistential() { - Existential *local_node; - - local_node = makeNode(Existential); - - _getPlan((Plan*)local_node); - - return( local_node ); + Existential *local_node; + + local_node = makeNode(Existential); + + _getPlan((Plan *) local_node); + + return (local_node); } /* ---------------- - * _readAppend + * _readAppend * - * Append is a subclass of Plan. + * Append is a subclass of Plan. * ---------------- */ -static Append * +static Append * _readAppend() { - Append *local_node; - char *token; - int length; - - local_node = makeNode(Append); - - _getPlan((Plan*)local_node); - - token = lsptok(NULL, &length); /* eat :unionplans */ - local_node->unionplans = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* eat :unionrelid */ - token = lsptok(NULL, &length); /* get unionrelid */ - local_node->unionrelid = atoi(token); - - token = lsptok(NULL, &length); /* eat :unionrtentries */ - local_node->unionrtentries = nodeRead(true); /* now read it */ - - return(local_node); + Append *local_node; + char *token; + int length; + + local_node = makeNode(Append); + + _getPlan((Plan *) local_node); + + token = lsptok(NULL, &length); /* eat :unionplans */ + local_node->unionplans = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* eat :unionrelid */ + token = lsptok(NULL, &length); /* get unionrelid */ + local_node->unionrelid = atoi(token); + + token = lsptok(NULL, &length); /* eat :unionrtentries */ + local_node->unionrtentries = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _getJoin + * _getJoin * * In case Join is not the same structure as Plan someday. * ---------------- */ static void -_getJoin(Join *node) +_getJoin(Join * node) { - _getPlan((Plan*)node); + _getPlan((Plan *) node); } /* ---------------- - * _readJoin + * _readJoin * - * Join is a subclass of Plan + * Join is a subclass of Plan * ---------------- */ -static Join * +static Join * _readJoin() { - Join *local_node; - - local_node = makeNode(Join); - - _getJoin(local_node); - - return( local_node ); + Join *local_node; + + local_node = makeNode(Join); + + _getJoin(local_node); + + return (local_node); } /* ---------------- - * _readNestLoop - * - * NestLoop is a subclass of Join + * _readNestLoop + * + * NestLoop is a subclass of Join * ---------------- */ static NestLoop * _readNestLoop() { - NestLoop *local_node; - - local_node = makeNode(NestLoop); - - _getJoin((Join*)local_node); - - return( local_node ); + NestLoop *local_node; + + local_node = makeNode(NestLoop); + + _getJoin((Join *) local_node); + + return (local_node); } /* ---------------- - * _readMergeJoin - * - * MergeJoin is a subclass of Join + * _readMergeJoin + * + * MergeJoin is a subclass of Join * ---------------- */ static MergeJoin * _readMergeJoin() { - MergeJoin *local_node; - char *token; - int length; - - local_node = makeNode(MergeJoin); - - _getJoin((Join*)local_node); - token = lsptok(NULL, &length); /* eat :mergeclauses */ - local_node->mergeclauses = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* eat :mergesortop */ - token = lsptok(NULL, &length); /* get mergesortop */ - local_node->mergesortop = atol(token); - - return( local_node ); + MergeJoin *local_node; + char *token; + int length; + + local_node = makeNode(MergeJoin); + + _getJoin((Join *) local_node); + token = lsptok(NULL, &length); /* eat :mergeclauses */ + local_node->mergeclauses = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* eat :mergesortop */ + token = lsptok(NULL, &length); /* get mergesortop */ + local_node->mergesortop = atol(token); + + return (local_node); } /* ---------------- - * _readHashJoin - * - * HashJoin is a subclass of Join. + * _readHashJoin + * + * HashJoin is a subclass of Join. * ---------------- */ static HashJoin * _readHashJoin() { - HashJoin *local_node; - char *token; - int length; - - local_node = makeNode(HashJoin); - - _getJoin((Join*)local_node); - - token = lsptok(NULL, &length); /* eat :hashclauses */ - local_node->hashclauses = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* eat :hashjoinop */ - token = lsptok(NULL, &length); /* get hashjoinop */ - local_node->hashjoinop = atoi(token); - - token = lsptok(NULL, &length); /* eat :hashjointable */ - token = lsptok(NULL, &length); /* eat hashjointable */ - local_node->hashjointable = NULL; - - token = lsptok(NULL, &length); /* eat :hashjointablekey */ - token = lsptok(NULL, &length); /* eat hashjointablekey */ - local_node->hashjointablekey = 0; - - token = lsptok(NULL, &length); /* eat :hashjointablesize */ - token = lsptok(NULL, &length); /* eat hashjointablesize */ - local_node->hashjointablesize = 0; - - token = lsptok(NULL, &length); /* eat :hashdone */ - token = lsptok(NULL, &length); /* eat hashdone */ - local_node->hashdone = false; - - return( local_node ); + HashJoin *local_node; + char *token; + int length; + + local_node = makeNode(HashJoin); + + _getJoin((Join *) local_node); + + token = lsptok(NULL, &length); /* eat :hashclauses */ + local_node->hashclauses = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* eat :hashjoinop */ + token = lsptok(NULL, &length); /* get hashjoinop */ + local_node->hashjoinop = atoi(token); + + token = lsptok(NULL, &length); /* eat :hashjointable */ + token = lsptok(NULL, &length); /* eat hashjointable */ + local_node->hashjointable = NULL; + + token = lsptok(NULL, &length); /* eat :hashjointablekey */ + token = lsptok(NULL, &length); /* eat hashjointablekey */ + local_node->hashjointablekey = 0; + + token = lsptok(NULL, &length); /* eat :hashjointablesize */ + token = lsptok(NULL, &length); /* eat hashjointablesize */ + local_node->hashjointablesize = 0; + + token = lsptok(NULL, &length); /* eat :hashdone */ + token = lsptok(NULL, &length); /* eat hashdone */ + local_node->hashdone = false; + + return (local_node); } /* ---------------- - * _getScan + * _getScan * - * Scan is a subclass of Node - * (Actually, according to the plannodes.h include file, it is a - * subclass of Plan. This is why _getPlan is used here.) + * Scan is a subclass of Node + * (Actually, according to the plannodes.h include file, it is a + * subclass of Plan. This is why _getPlan is used here.) * - * Scan gets its own get function since stuff inherits it. + * Scan gets its own get function since stuff inherits it. * ---------------- */ -static void -_getScan(Scan *node) +static void +_getScan(Scan * node) { - char *token; - int length; - - _getPlan((Plan*)node); - - token = lsptok(NULL, &length); /* eat :scanrelid */ - token = lsptok(NULL, &length); /* get scanrelid */ - node->scanrelid = atoi(token); + char *token; + int length; + + _getPlan((Plan *) node); + + token = lsptok(NULL, &length); /* eat :scanrelid */ + token = lsptok(NULL, &length); /* get scanrelid */ + node->scanrelid = atoi(token); } /* ---------------- - * _readScan - * + * _readScan + * * Scan is a subclass of Plan (Not Node, see above). * ---------------- */ -static Scan * +static Scan * _readScan() { - Scan *local_node; - - local_node = makeNode(Scan); - - _getScan(local_node); - - return(local_node); + Scan *local_node; + + local_node = makeNode(Scan); + + _getScan(local_node); + + return (local_node); } /* ---------------- - * _readSeqScan - * - * SeqScan is a subclass of Scan + * _readSeqScan + * + * SeqScan is a subclass of Scan * ---------------- */ static SeqScan * _readSeqScan() { - SeqScan *local_node; - - local_node = makeNode(SeqScan); - - _getScan((Scan*)local_node); - - return(local_node); + SeqScan *local_node; + + local_node = makeNode(SeqScan); + + _getScan((Scan *) local_node); + + return (local_node); } /* ---------------- - * _readIndexScan - * - * IndexScan is a subclass of Scan + * _readIndexScan + * + * IndexScan is a subclass of Scan * ---------------- */ static IndexScan * _readIndexScan() { - IndexScan *local_node; - char *token; - int length; - - local_node = makeNode(IndexScan); - - _getScan((Scan*)local_node); - - token = lsptok(NULL, &length); /* eat :indxid */ - local_node->indxid = - toIntList(nodeRead(true)); /* now read it */ - - token = lsptok(NULL, &length); /* eat :indxqual */ - local_node->indxqual = nodeRead(true); /* now read it */ - - return(local_node); + IndexScan *local_node; + char *token; + int length; + + local_node = makeNode(IndexScan); + + _getScan((Scan *) local_node); + + token = lsptok(NULL, &length); /* eat :indxid */ + local_node->indxid = + toIntList(nodeRead(true)); /* now read it */ + + token = lsptok(NULL, &length); /* eat :indxqual */ + local_node->indxqual = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readTemp - * - * Temp is a subclass of Plan + * _readTemp + * + * Temp is a subclass of Plan * ---------------- */ -static Temp * +static Temp * _readTemp() { - Temp *local_node; - char *token; - int length; - - local_node = makeNode(Temp); - - _getPlan((Plan*)local_node); - - token = lsptok(NULL, &length); /* eat :tempid */ - token = lsptok(NULL, &length); /* get tempid */ - local_node->tempid = atol(token); - - token = lsptok(NULL, &length); /* eat :keycount */ - token = lsptok(NULL, &length); /* get keycount */ - local_node->keycount = atoi(token); - - return(local_node); + Temp *local_node; + char *token; + int length; + + local_node = makeNode(Temp); + + _getPlan((Plan *) local_node); + + token = lsptok(NULL, &length); /* eat :tempid */ + token = lsptok(NULL, &length); /* get tempid */ + local_node->tempid = atol(token); + + token = lsptok(NULL, &length); /* eat :keycount */ + token = lsptok(NULL, &length); /* get keycount */ + local_node->keycount = atoi(token); + + return (local_node); } /* ---------------- - * _readSort - * - * Sort is a subclass of Temp + * _readSort + * + * Sort is a subclass of Temp * ---------------- */ -static Sort * +static Sort * _readSort() { - Sort *local_node; - char *token; - int length; - - local_node = makeNode(Sort); - - _getPlan((Plan*)local_node); - - token = lsptok(NULL, &length); /* eat :tempid */ - token = lsptok(NULL, &length); /* get tempid */ - local_node->tempid = atol(token); - - token = lsptok(NULL, &length); /* eat :keycount */ - token = lsptok(NULL, &length); /* get keycount */ - local_node->keycount = atoi(token); - - return(local_node); + Sort *local_node; + char *token; + int length; + + local_node = makeNode(Sort); + + _getPlan((Plan *) local_node); + + token = lsptok(NULL, &length); /* eat :tempid */ + token = lsptok(NULL, &length); /* get tempid */ + local_node->tempid = atol(token); + + token = lsptok(NULL, &length); /* eat :keycount */ + token = lsptok(NULL, &length); /* get keycount */ + local_node->keycount = atoi(token); + + return (local_node); } -static Agg * +static Agg * _readAgg() { - Agg *local_node; - char *token; - int length; - - local_node = makeNode(Agg); - _getPlan((Plan*)local_node); - - token = lsptok(NULL, &length); /* eat :numagg */ - token = lsptok(NULL, &length); /* get numagg */ - local_node->numAgg = atoi(token); - - return(local_node); + Agg *local_node; + char *token; + int length; + + local_node = makeNode(Agg); + _getPlan((Plan *) local_node); + + token = lsptok(NULL, &length); /* eat :numagg */ + token = lsptok(NULL, &length); /* get numagg */ + local_node->numAgg = atoi(token); + + return (local_node); } /* ---------------- - * _readUnique + * _readUnique * * For some reason, unique is a subclass of Temp. */ -static Unique * +static Unique * _readUnique() { - Unique *local_node; - char *token; - int length; - - local_node = makeNode(Unique); - - _getPlan((Plan*)local_node); - - token = lsptok(NULL, &length); /* eat :tempid */ - token = lsptok(NULL, &length); /* get :tempid */ - local_node->tempid = atol(token); - - token = lsptok(NULL, &length); /* eat :keycount */ - token = lsptok(NULL, &length); /* get :keycount */ - local_node->keycount = atoi(token); - - return(local_node); + Unique *local_node; + char *token; + int length; + + local_node = makeNode(Unique); + + _getPlan((Plan *) local_node); + + token = lsptok(NULL, &length); /* eat :tempid */ + token = lsptok(NULL, &length); /* get :tempid */ + local_node->tempid = atol(token); + + token = lsptok(NULL, &length); /* eat :keycount */ + token = lsptok(NULL, &length); /* get :keycount */ + local_node->keycount = atoi(token); + + return (local_node); } /* ---------------- - * _readHash - * - * Hash is a subclass of Temp + * _readHash + * + * Hash is a subclass of Temp * ---------------- */ -static Hash * +static Hash * _readHash() { - Hash *local_node; - char *token; - int length; - - local_node = makeNode(Hash); - - _getPlan((Plan*)local_node); - - token = lsptok(NULL, &length); /* eat :hashkey */ - local_node->hashkey = (Var*) nodeRead(true); - - token = lsptok(NULL, &length); /* eat :hashtable */ - token = lsptok(NULL, &length); /* eat hashtable address*/ - local_node->hashtable = NULL; - - token = lsptok(NULL, &length); /* eat :hashtablekey*/ - token = lsptok(NULL, &length); /* get hashtablekey */ - local_node->hashtablekey = 0; - - token = lsptok(NULL, &length); /* eat :hashtablesize*/ - token = lsptok(NULL, &length); /* get hashtablesize */ - local_node->hashtablesize = 0; - - return(local_node); + Hash *local_node; + char *token; + int length; + + local_node = makeNode(Hash); + + _getPlan((Plan *) local_node); + + token = lsptok(NULL, &length); /* eat :hashkey */ + local_node->hashkey = (Var *) nodeRead(true); + + token = lsptok(NULL, &length); /* eat :hashtable */ + token = lsptok(NULL, &length); /* eat hashtable address */ + local_node->hashtable = NULL; + + token = lsptok(NULL, &length); /* eat :hashtablekey */ + token = lsptok(NULL, &length); /* get hashtablekey */ + local_node->hashtablekey = 0; + + token = lsptok(NULL, &length); /* eat :hashtablesize */ + token = lsptok(NULL, &length); /* get hashtablesize */ + local_node->hashtablesize = 0; + + return (local_node); } /* - * Stuff from primnodes.h. + * Stuff from primnodes.h. */ /* ---------------- - * _readResdom - * - * Resdom is a subclass of Node + * _readResdom + * + * Resdom is a subclass of Node * ---------------- */ -static Resdom * +static Resdom * _readResdom() { - Resdom *local_node; - char *token; - int length; - - local_node = makeNode(Resdom); - - token = lsptok(NULL, &length); /* eat :resno */ - token = lsptok(NULL, &length); /* get resno */ - local_node->resno = atoi(token); - - token = lsptok(NULL, &length); /* eat :restype */ - token = lsptok(NULL, &length); /* get restype */ - local_node->restype = atol(token); - - token = lsptok(NULL, &length); /* eat :reslen */ - token = lsptok(NULL, &length); /* get reslen */ - local_node->reslen = atoi(token); - - token = lsptok(NULL, &length); /* eat :resname */ - token = lsptok(NULL, &length); /* get the name */ - - if (!strncmp(token, "\"null\"", 5)) { - local_node->resname = NULL; - }else { - /* - * Peel off ""'s, then make a true copy. - */ - - token++; - token[length - 2] = '\0'; - - local_node->resname = palloc(length); - strcpy(local_node->resname, token); - token[length - 2] = '\"'; - } - - token = lsptok(NULL, &length); /* eat :reskey */ - token = lsptok(NULL, &length); /* get reskey */ - local_node->reskey = atoi(token); - - token = lsptok(NULL, &length); /* eat :reskeyop */ - token = lsptok(NULL, &length); /* get reskeyop */ - local_node->reskeyop = (Oid) atol(token); - - token = lsptok(NULL, &length); /* eat :resjunk */ - token = lsptok(NULL, &length); /* get resjunk */ - local_node->resjunk = atoi(token); - - return(local_node); + Resdom *local_node; + char *token; + int length; + + local_node = makeNode(Resdom); + + token = lsptok(NULL, &length); /* eat :resno */ + token = lsptok(NULL, &length); /* get resno */ + local_node->resno = atoi(token); + + token = lsptok(NULL, &length); /* eat :restype */ + token = lsptok(NULL, &length); /* get restype */ + local_node->restype = atol(token); + + token = lsptok(NULL, &length); /* eat :reslen */ + token = lsptok(NULL, &length); /* get reslen */ + local_node->reslen = atoi(token); + + token = lsptok(NULL, &length); /* eat :resname */ + token = lsptok(NULL, &length); /* get the name */ + + if (!strncmp(token, "\"null\"", 5)) + { + local_node->resname = NULL; + } + else + { + + /* + * Peel off ""'s, then make a true copy. + */ + + token++; + token[length - 2] = '\0'; + + local_node->resname = palloc(length); + strcpy(local_node->resname, token); + token[length - 2] = '\"'; + } + + token = lsptok(NULL, &length); /* eat :reskey */ + token = lsptok(NULL, &length); /* get reskey */ + local_node->reskey = atoi(token); + + token = lsptok(NULL, &length); /* eat :reskeyop */ + token = lsptok(NULL, &length); /* get reskeyop */ + local_node->reskeyop = (Oid) atol(token); + + token = lsptok(NULL, &length); /* eat :resjunk */ + token = lsptok(NULL, &length); /* get resjunk */ + local_node->resjunk = atoi(token); + + return (local_node); } /* ---------------- - * _readExpr - * - * Expr is a subclass of Node + * _readExpr + * + * Expr is a subclass of Node * ---------------- */ -static Expr * +static Expr * _readExpr() { - Expr *local_node; - char *token; - int length; - - local_node = makeNode(Expr); - - token = lsptok(NULL, &length); /* eat :typeOid */ - token = lsptok(NULL, &length); /* get typeOid */ - local_node->typeOid = (Oid)atol(token); - - token = lsptok(NULL, &length); /* eat :opType */ - token = lsptok(NULL, &length); /* get opType */ - if (!strncmp(token, "op", 2)) { - local_node->opType = OP_EXPR; - } else if (!strncmp(token, "func", 4)) { - local_node->opType = FUNC_EXPR; - } else if (!strncmp(token, "or", 2)) { - local_node->opType = OR_EXPR; - } else if (!strncmp(token, "and", 3)) { - local_node->opType = AND_EXPR; - } else if (!strncmp(token, "not", 3)) { - local_node->opType = NOT_EXPR; - } - - token = lsptok(NULL, &length); /* eat :oper */ - local_node->oper = nodeRead(true); - - token = lsptok(NULL, &length); /* eat :args */ - local_node->args = nodeRead(true); /* now read it */ - - return(local_node); + Expr *local_node; + char *token; + int length; + + local_node = makeNode(Expr); + + token = lsptok(NULL, &length); /* eat :typeOid */ + token = lsptok(NULL, &length); /* get typeOid */ + local_node->typeOid = (Oid) atol(token); + + token = lsptok(NULL, &length); /* eat :opType */ + token = lsptok(NULL, &length); /* get opType */ + if (!strncmp(token, "op", 2)) + { + local_node->opType = OP_EXPR; + } + else if (!strncmp(token, "func", 4)) + { + local_node->opType = FUNC_EXPR; + } + else if (!strncmp(token, "or", 2)) + { + local_node->opType = OR_EXPR; + } + else if (!strncmp(token, "and", 3)) + { + local_node->opType = AND_EXPR; + } + else if (!strncmp(token, "not", 3)) + { + local_node->opType = NOT_EXPR; + } + + token = lsptok(NULL, &length); /* eat :oper */ + local_node->oper = nodeRead(true); + + token = lsptok(NULL, &length); /* eat :args */ + local_node->args = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readVar - * - * Var is a subclass of Expr + * _readVar + * + * Var is a subclass of Expr * ---------------- */ -static Var * +static Var * _readVar() { - Var *local_node; - char *token; - int length; - - local_node = makeNode(Var); - - token = lsptok(NULL, &length); /* eat :varno */ - token = lsptok(NULL, &length); /* get varno */ - local_node->varno = atoi(token); - - token = lsptok(NULL, &length); /* eat :varattno */ - token = lsptok(NULL, &length); /* get varattno */ - local_node->varattno = atoi(token); - - token = lsptok(NULL, &length); /* eat :vartype */ - token = lsptok(NULL, &length); /* get vartype */ - local_node->vartype = (Oid) atol(token); - - token = lsptok(NULL, &length); /* eat :varnoold */ - token = lsptok(NULL, &length); /* get varnoold */ - local_node->varnoold = (Oid) atol(token); - - token = lsptok(NULL, &length); /* eat :varoattno */ - token = lsptok(NULL, &length); /* eat :varoattno */ - local_node->varoattno = (int) atol(token); - - return(local_node); + Var *local_node; + char *token; + int length; + + local_node = makeNode(Var); + + token = lsptok(NULL, &length); /* eat :varno */ + token = lsptok(NULL, &length); /* get varno */ + local_node->varno = atoi(token); + + token = lsptok(NULL, &length); /* eat :varattno */ + token = lsptok(NULL, &length); /* get varattno */ + local_node->varattno = atoi(token); + + token = lsptok(NULL, &length); /* eat :vartype */ + token = lsptok(NULL, &length); /* get vartype */ + local_node->vartype = (Oid) atol(token); + + token = lsptok(NULL, &length); /* eat :varnoold */ + token = lsptok(NULL, &length); /* get varnoold */ + local_node->varnoold = (Oid) atol(token); + + token = lsptok(NULL, &length); /* eat :varoattno */ + token = lsptok(NULL, &length); /* eat :varoattno */ + local_node->varoattno = (int) atol(token); + + return (local_node); } /* ---------------- @@ -746,40 +769,40 @@ _readVar() * Array is a subclass of Expr * ---------------- */ -static Array * +static Array * _readArray() { - Array *local_node; - char *token; - int length; - - local_node = makeNode(Array); - - token = lsptok(NULL, &length); /* eat :arrayelemtype */ - token = lsptok(NULL, &length); /* get arrayelemtype */ - local_node->arrayelemtype = (Oid) atoi(token); - - token = lsptok(NULL, &length); /* eat :arrayelemlength */ - token = lsptok(NULL, &length); /* get arrayelemlength */ - local_node->arrayelemlength = atoi(token); - - token = lsptok(NULL, &length); /* eat :arrayelembyval */ - token = lsptok(NULL, &length); /* get arrayelembyval */ - local_node->arrayelembyval = (token[0] == 't') ? true : false; - - token = lsptok(NULL, &length); /* eat :arraylow */ - token = lsptok(NULL, &length); /* get arraylow */ - local_node->arraylow.indx[0] = atoi(token); - - token = lsptok(NULL, &length); /* eat :arrayhigh */ - token = lsptok(NULL, &length); /* get arrayhigh */ - local_node->arrayhigh.indx[0] = atoi(token); - - token = lsptok(NULL, &length); /* eat :arraylen */ - token = lsptok(NULL, &length); /* get arraylen */ - local_node->arraylen = atoi(token); - - return(local_node); + Array *local_node; + char *token; + int length; + + local_node = makeNode(Array); + + token = lsptok(NULL, &length); /* eat :arrayelemtype */ + token = lsptok(NULL, &length); /* get arrayelemtype */ + local_node->arrayelemtype = (Oid) atoi(token); + + token = lsptok(NULL, &length); /* eat :arrayelemlength */ + token = lsptok(NULL, &length); /* get arrayelemlength */ + local_node->arrayelemlength = atoi(token); + + token = lsptok(NULL, &length); /* eat :arrayelembyval */ + token = lsptok(NULL, &length); /* get arrayelembyval */ + local_node->arrayelembyval = (token[0] == 't') ? true : false; + + token = lsptok(NULL, &length); /* eat :arraylow */ + token = lsptok(NULL, &length); /* get arraylow */ + local_node->arraylow.indx[0] = atoi(token); + + token = lsptok(NULL, &length); /* eat :arrayhigh */ + token = lsptok(NULL, &length); /* get arrayhigh */ + local_node->arrayhigh.indx[0] = atoi(token); + + token = lsptok(NULL, &length); /* eat :arraylen */ + token = lsptok(NULL, &length); /* get arraylen */ + local_node->arraylen = atoi(token); + + return (local_node); } /* ---------------- @@ -791,1031 +814,1052 @@ _readArray() static ArrayRef * _readArrayRef() { - ArrayRef *local_node; - char *token; - int length; - - local_node = makeNode(ArrayRef); - - token = lsptok(NULL, &length); /* eat :refelemtype */ - token = lsptok(NULL, &length); /* get refelemtype */ - local_node->refelemtype = (Oid) atoi(token); - - token = lsptok(NULL, &length); /* eat :refattrlength */ - token = lsptok(NULL, &length); /* get refattrlength */ - local_node->refattrlength = atoi(token); - - token = lsptok(NULL, &length); /* eat :refelemlength */ - token = lsptok(NULL, &length); /* get refelemlength */ - local_node->refelemlength = atoi(token); - - token = lsptok(NULL, &length); /* eat :refelembyval */ - token = lsptok(NULL, &length); /* get refelembyval */ - local_node->refelembyval = (token[0] == 't') ? true : false; - - token = lsptok(NULL, &length); /* eat :refupperindex */ - local_node->refupperindexpr = nodeRead(true); - - token = lsptok(NULL, &length); /* eat :reflowerindex */ - local_node->reflowerindexpr = nodeRead(true); - - token = lsptok(NULL, &length); /* eat :refexpr */ - local_node->refexpr = nodeRead(true); - - token = lsptok(NULL, &length); /* eat :refassgnexpr */ - local_node->refassgnexpr = nodeRead(true); - - return(local_node); + ArrayRef *local_node; + char *token; + int length; + + local_node = makeNode(ArrayRef); + + token = lsptok(NULL, &length); /* eat :refelemtype */ + token = lsptok(NULL, &length); /* get refelemtype */ + local_node->refelemtype = (Oid) atoi(token); + + token = lsptok(NULL, &length); /* eat :refattrlength */ + token = lsptok(NULL, &length); /* get refattrlength */ + local_node->refattrlength = atoi(token); + + token = lsptok(NULL, &length); /* eat :refelemlength */ + token = lsptok(NULL, &length); /* get refelemlength */ + local_node->refelemlength = atoi(token); + + token = lsptok(NULL, &length); /* eat :refelembyval */ + token = lsptok(NULL, &length); /* get refelembyval */ + local_node->refelembyval = (token[0] == 't') ? true : false; + + token = lsptok(NULL, &length); /* eat :refupperindex */ + local_node->refupperindexpr = nodeRead(true); + + token = lsptok(NULL, &length); /* eat :reflowerindex */ + local_node->reflowerindexpr = nodeRead(true); + + token = lsptok(NULL, &length); /* eat :refexpr */ + local_node->refexpr = nodeRead(true); + + token = lsptok(NULL, &length); /* eat :refassgnexpr */ + local_node->refassgnexpr = nodeRead(true); + + return (local_node); } /* ---------------- - * _readConst - * - * Const is a subclass of Expr + * _readConst + * + * Const is a subclass of Expr * ---------------- */ -static Const * +static Const * _readConst() { - Const *local_node; - char *token; - int length; - - local_node = makeNode(Const); - - token = lsptok(NULL, &length); /* get :consttype */ - token = lsptok(NULL, &length); /* now read it */ - local_node->consttype = atol(token); - - - token = lsptok(NULL, &length); /* get :constlen */ - token = lsptok(NULL, &length); /* now read it */ - local_node->constlen = atoi(token); - - token = lsptok(NULL, &length); /* get :constisnull */ - token = lsptok(NULL, &length); /* now read it */ - - if (!strncmp(token, "true", 4)) { - local_node->constisnull = true; - }else { - local_node->constisnull = false; - } - - - token = lsptok(NULL, &length); /* get :constvalue */ - - if (local_node->constisnull) { - token = lsptok(NULL, &length); /* skip "NIL" */ - }else { - /* - * read the value - */ - local_node->constvalue = readDatum(local_node->consttype); - } - - token = lsptok(NULL, &length); /* get :constbyval */ - token = lsptok(NULL, &length); /* now read it */ - - if (!strncmp(token, "true", 4)) { - local_node->constbyval = true; - }else { - local_node->constbyval = false; - } - - return(local_node); + Const *local_node; + char *token; + int length; + + local_node = makeNode(Const); + + token = lsptok(NULL, &length); /* get :consttype */ + token = lsptok(NULL, &length); /* now read it */ + local_node->consttype = atol(token); + + + token = lsptok(NULL, &length); /* get :constlen */ + token = lsptok(NULL, &length); /* now read it */ + local_node->constlen = atoi(token); + + token = lsptok(NULL, &length); /* get :constisnull */ + token = lsptok(NULL, &length); /* now read it */ + + if (!strncmp(token, "true", 4)) + { + local_node->constisnull = true; + } + else + { + local_node->constisnull = false; + } + + + token = lsptok(NULL, &length); /* get :constvalue */ + + if (local_node->constisnull) + { + token = lsptok(NULL, &length); /* skip "NIL" */ + } + else + { + + /* + * read the value + */ + local_node->constvalue = readDatum(local_node->consttype); + } + + token = lsptok(NULL, &length); /* get :constbyval */ + token = lsptok(NULL, &length); /* now read it */ + + if (!strncmp(token, "true", 4)) + { + local_node->constbyval = true; + } + else + { + local_node->constbyval = false; + } + + return (local_node); } /* ---------------- - * _readFunc - * - * Func is a subclass of Expr + * _readFunc + * + * Func is a subclass of Expr * ---------------- */ -static Func * +static Func * _readFunc() { - Func *local_node; - char *token; - int length; - - local_node = makeNode(Func); - - token = lsptok(NULL, &length); /* get :funcid */ - token = lsptok(NULL, &length); /* now read it */ - local_node->funcid = atol(token); - - token = lsptok(NULL, &length); /* get :functype */ - token = lsptok(NULL, &length); /* now read it */ - local_node->functype = atol(token); - - token = lsptok(NULL, &length); /* get :funcisindex */ - token = lsptok(NULL, &length); /* now read it */ - - if (!strncmp(token, "true", 4)) { - local_node->funcisindex = true; - }else { - local_node->funcisindex = false; - } - - token = lsptok(NULL, &length); /* get :funcsize */ - token = lsptok(NULL, &length); /* now read it */ - local_node->funcsize = atol(token); - - token = lsptok(NULL, &length); /* get :func_fcache */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->func_fcache = (FunctionCache *) NULL; - - token = lsptok(NULL, &length); /* get :func_tlist */ - local_node->func_tlist = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :func_planlist */ - local_node->func_planlist = nodeRead(true); /* now read it */ - - return(local_node); + Func *local_node; + char *token; + int length; + + local_node = makeNode(Func); + + token = lsptok(NULL, &length); /* get :funcid */ + token = lsptok(NULL, &length); /* now read it */ + local_node->funcid = atol(token); + + token = lsptok(NULL, &length); /* get :functype */ + token = lsptok(NULL, &length); /* now read it */ + local_node->functype = atol(token); + + token = lsptok(NULL, &length); /* get :funcisindex */ + token = lsptok(NULL, &length); /* now read it */ + + if (!strncmp(token, "true", 4)) + { + local_node->funcisindex = true; + } + else + { + local_node->funcisindex = false; + } + + token = lsptok(NULL, &length); /* get :funcsize */ + token = lsptok(NULL, &length); /* now read it */ + local_node->funcsize = atol(token); + + token = lsptok(NULL, &length); /* get :func_fcache */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->func_fcache = (FunctionCache *) NULL; + + token = lsptok(NULL, &length); /* get :func_tlist */ + local_node->func_tlist = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :func_planlist */ + local_node->func_planlist = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readOper - * - * Oper is a subclass of Expr + * _readOper + * + * Oper is a subclass of Expr * ---------------- */ -static Oper * +static Oper * _readOper() { - Oper *local_node; - char *token; - int length; - - local_node = makeNode(Oper); - - token = lsptok(NULL, &length); /* get :opno */ - token = lsptok(NULL, &length); /* now read it */ - local_node->opno = atol(token); - - token = lsptok(NULL, &length); /* get :opid */ - token = lsptok(NULL, &length); /* now read it */ - local_node->opid = atol(token); - - token = lsptok(NULL, &length); /* get :opresulttype */ - token = lsptok(NULL, &length); /* now read it */ - local_node->opresulttype = atol(token); - - /* - * NOTE: Alternatively we can call 'replace_opid' - * which initializes both 'opid' and 'op_fcache'. - */ - local_node->op_fcache = (FunctionCache *) NULL; - - return(local_node); + Oper *local_node; + char *token; + int length; + + local_node = makeNode(Oper); + + token = lsptok(NULL, &length); /* get :opno */ + token = lsptok(NULL, &length); /* now read it */ + local_node->opno = atol(token); + + token = lsptok(NULL, &length); /* get :opid */ + token = lsptok(NULL, &length); /* now read it */ + local_node->opid = atol(token); + + token = lsptok(NULL, &length); /* get :opresulttype */ + token = lsptok(NULL, &length); /* now read it */ + local_node->opresulttype = atol(token); + + /* + * NOTE: Alternatively we can call 'replace_opid' which initializes + * both 'opid' and 'op_fcache'. + */ + local_node->op_fcache = (FunctionCache *) NULL; + + return (local_node); } /* ---------------- - * _readParam - * - * Param is a subclass of Expr + * _readParam + * + * Param is a subclass of Expr * ---------------- */ -static Param * +static Param * _readParam() { - Param *local_node; - char *token; - int length; - - local_node = makeNode(Param); - - token = lsptok(NULL, &length); /* get :paramkind */ - token = lsptok(NULL, &length); /* now read it */ - local_node->paramkind = atoi(token); - - token = lsptok(NULL, &length); /* get :paramid */ - token = lsptok(NULL, &length); /* now read it */ - local_node->paramid = atol(token); - - token = lsptok(NULL, &length); /* get :paramname */ - token = lsptok(NULL, &length); /* now read it */ - token++; /* skip the first `"' */ - token[length - 2] = '\0'; /* this is the 2nd `"' */ - - local_node->paramname = pstrdup(token); - token[length - 2] = '\"'; /* restore the 2nd `"' */ - - token = lsptok(NULL, &length); /* get :paramtype */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->paramtype = atol(token); - token = lsptok(NULL, &length); /* get :param_tlist */ - local_node->param_tlist = nodeRead(true); /* now read it */ - - return(local_node); + Param *local_node; + char *token; + int length; + + local_node = makeNode(Param); + + token = lsptok(NULL, &length); /* get :paramkind */ + token = lsptok(NULL, &length); /* now read it */ + local_node->paramkind = atoi(token); + + token = lsptok(NULL, &length); /* get :paramid */ + token = lsptok(NULL, &length); /* now read it */ + local_node->paramid = atol(token); + + token = lsptok(NULL, &length); /* get :paramname */ + token = lsptok(NULL, &length); /* now read it */ + token++; /* skip the first `"' */ + token[length - 2] = '\0'; /* this is the 2nd `"' */ + + local_node->paramname = pstrdup(token); + token[length - 2] = '\"'; /* restore the 2nd `"' */ + + token = lsptok(NULL, &length); /* get :paramtype */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->paramtype = atol(token); + token = lsptok(NULL, &length); /* get :param_tlist */ + local_node->param_tlist = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readAggreg - * - * Aggreg is a subclass of Node + * _readAggreg + * + * Aggreg is a subclass of Node * ---------------- */ -static Aggreg * +static Aggreg * _readAggreg() { - Aggreg *local_node; - char *token; - int length; - - local_node = makeNode(Aggreg); - - token = lsptok(NULL, &length); /* eat :aggname */ - token = lsptok(NULL, &length); /* get aggname */ - local_node->aggname = (char*) palloc (length + 1); - strNcpy (local_node->aggname, token, length); - - token = lsptok(NULL, &length); /* eat :basetype */ - token = lsptok(NULL, &length); /* get basetype */ - local_node->basetype = (Oid)atol(token); - - token = lsptok(NULL, &length); /* eat :aggtype */ - token = lsptok(NULL, &length); /* get aggtype */ - local_node->aggtype = (Oid)atol(token); - - token = lsptok(NULL, &length); /* eat :aggno */ - token = lsptok(NULL, &length); /* get aggno */ - local_node->aggno = atoi(token); - - token = lsptok(NULL, &length); /* eat :target */ - local_node->target = nodeRead(true); /* now read it */ - - return(local_node); + Aggreg *local_node; + char *token; + int length; + + local_node = makeNode(Aggreg); + + token = lsptok(NULL, &length); /* eat :aggname */ + token = lsptok(NULL, &length); /* get aggname */ + local_node->aggname = (char *) palloc(length + 1); + strNcpy(local_node->aggname, token, length); + + token = lsptok(NULL, &length); /* eat :basetype */ + token = lsptok(NULL, &length); /* get basetype */ + local_node->basetype = (Oid) atol(token); + + token = lsptok(NULL, &length); /* eat :aggtype */ + token = lsptok(NULL, &length); /* get aggtype */ + local_node->aggtype = (Oid) atol(token); + + token = lsptok(NULL, &length); /* eat :aggno */ + token = lsptok(NULL, &length); /* get aggno */ + local_node->aggno = atoi(token); + + token = lsptok(NULL, &length); /* eat :target */ + local_node->target = nodeRead(true); /* now read it */ + + return (local_node); } /* - * Stuff from execnodes.h + * Stuff from execnodes.h */ /* ---------------- - * _readEState - * - * EState is a subclass of Node. + * _readEState + * + * EState is a subclass of Node. * ---------------- */ -static EState * +static EState * _readEState() { - EState *local_node; - char *token; - int length; - - local_node = makeNode(EState); - - token = lsptok(NULL, &length); /* get :direction */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->es_direction = atoi(token); - - token = lsptok(NULL, &length); /* get :range_table */ - - local_node->es_range_table = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :result_relation_info */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - sscanf(token, "%x",(unsigned int *)&local_node->es_result_relation_info); - - return(local_node); + EState *local_node; + char *token; + int length; + + local_node = makeNode(EState); + + token = lsptok(NULL, &length); /* get :direction */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->es_direction = atoi(token); + + token = lsptok(NULL, &length); /* get :range_table */ + + local_node->es_range_table = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :result_relation_info */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + sscanf(token, "%x", (unsigned int *) &local_node->es_result_relation_info); + + return (local_node); } /* - * Stuff from relation.h + * Stuff from relation.h */ /* ---------------- - * _readRel + * _readRel * ---------------- */ -static Rel * +static Rel * _readRel() { - Rel *local_node; - char *token; - int length; - - local_node = makeNode(Rel); - - token = lsptok(NULL, &length); /* get :relids */ - local_node->relids = - toIntList(nodeRead(true)); /* now read it */ - - token = lsptok(NULL, &length); /* get :indexed */ - token = lsptok(NULL, &length); /* now read it */ - - if (!strncmp(token, "true", 4)) - { - local_node->indexed = true; - } - else - { - local_node->indexed = false; - } - - token = lsptok(NULL, &length); /* get :pages */ - token = lsptok(NULL, &length); /* now read it */ - local_node->pages = (unsigned int) atoi(token); - - token = lsptok(NULL, &length); /* get :tuples */ - token = lsptok(NULL, &length); /* now read it */ - local_node->tuples = (unsigned int) atoi(token); - - token = lsptok(NULL, &length); /* get :size */ - token = lsptok(NULL, &length); /* now read it */ - local_node->size = (unsigned int) atoi(token); - - token = lsptok(NULL, &length); /* get :width */ - token = lsptok(NULL, &length); /* now read it */ - local_node->width = (unsigned int) atoi(token); - - token = lsptok(NULL, &length); /* get :targetlist */ - local_node->targetlist = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :pathlist */ - local_node->pathlist = nodeRead(true); /* now read it */ - - /* - * Not sure if these are nodes or not. They're declared as - * struct Path *. Since i don't know, i'll just print the - * addresses for now. This can be changed later, if necessary. - */ - - token = lsptok(NULL, &length); /* get :unorderpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - sscanf(token, "%x", (unsigned int *)&local_node->unorderedpath); - - token = lsptok(NULL, &length); /* get :cheapestpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - sscanf(token, "%x", (unsigned int *)&local_node->cheapestpath); - - - token = lsptok(NULL, &length); /* get :clauseinfo */ - local_node->clauseinfo = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :joininfo */ - local_node->joininfo = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :innerjoin */ - local_node->innerjoin = nodeRead(true); /* now read it */ - - return(local_node); + Rel *local_node; + char *token; + int length; + + local_node = makeNode(Rel); + + token = lsptok(NULL, &length); /* get :relids */ + local_node->relids = + toIntList(nodeRead(true)); /* now read it */ + + token = lsptok(NULL, &length); /* get :indexed */ + token = lsptok(NULL, &length); /* now read it */ + + if (!strncmp(token, "true", 4)) + { + local_node->indexed = true; + } + else + { + local_node->indexed = false; + } + + token = lsptok(NULL, &length); /* get :pages */ + token = lsptok(NULL, &length); /* now read it */ + local_node->pages = (unsigned int) atoi(token); + + token = lsptok(NULL, &length); /* get :tuples */ + token = lsptok(NULL, &length); /* now read it */ + local_node->tuples = (unsigned int) atoi(token); + + token = lsptok(NULL, &length); /* get :size */ + token = lsptok(NULL, &length); /* now read it */ + local_node->size = (unsigned int) atoi(token); + + token = lsptok(NULL, &length); /* get :width */ + token = lsptok(NULL, &length); /* now read it */ + local_node->width = (unsigned int) atoi(token); + + token = lsptok(NULL, &length); /* get :targetlist */ + local_node->targetlist = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :pathlist */ + local_node->pathlist = nodeRead(true); /* now read it */ + + /* + * Not sure if these are nodes or not. They're declared as struct + * Path *. Since i don't know, i'll just print the addresses for now. + * This can be changed later, if necessary. + */ + + token = lsptok(NULL, &length); /* get :unorderpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + sscanf(token, "%x", (unsigned int *) &local_node->unorderedpath); + + token = lsptok(NULL, &length); /* get :cheapestpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + sscanf(token, "%x", (unsigned int *) &local_node->cheapestpath); + + + token = lsptok(NULL, &length); /* get :clauseinfo */ + local_node->clauseinfo = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :joininfo */ + local_node->joininfo = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :innerjoin */ + local_node->innerjoin = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readTargetEntry + * _readTargetEntry * ---------------- */ static TargetEntry * _readTargetEntry() { - TargetEntry *local_node; - char *token; - int length; + TargetEntry *local_node; + char *token; + int length; - local_node = makeNode(TargetEntry); + local_node = makeNode(TargetEntry); - token = lsptok(NULL, &length); /* get :resdom */ - local_node->resdom = nodeRead(true); /* now read it */ + token = lsptok(NULL, &length); /* get :resdom */ + local_node->resdom = nodeRead(true); /* now read it */ - token = lsptok(NULL, &length); /* get :expr */ - local_node->expr = nodeRead(true); /* now read it */ + token = lsptok(NULL, &length); /* get :expr */ + local_node->expr = nodeRead(true); /* now read it */ - return (local_node); + return (local_node); } /* ---------------- - * _readTargetEntry + * _readTargetEntry * ---------------- */ static RangeTblEntry * _readRangeTblEntry() { - RangeTblEntry *local_node; - char *token; - int length; + RangeTblEntry *local_node; + char *token; + int length; - local_node = makeNode(RangeTblEntry); + local_node = makeNode(RangeTblEntry); - token = lsptok(NULL, &length); /* eat :relname */ - token = lsptok(NULL, &length); /* get :relname */ - if (!strncmp(token, "\"null\"", 5)) { - local_node->relname = NULL; - }else { - /* - * Peel off ""'s, then make a true copy. - */ - - token++; - token[length - 2] = '\0'; - - local_node->relname = (char *) palloc(NAMEDATALEN); - strcpy(local_node->relname, token); - token[length - 2] = '\"'; - } - - token = lsptok(NULL, &length); /* eat :inh */ - token = lsptok(NULL, &length); /* get :inh */ - local_node->inh = atoi(token); - - token = lsptok(NULL, &length); /* eat :refname */ - token = lsptok(NULL, &length); /* get :refname */ - if (!strncmp(token, "\"null\"", 5)) { - local_node->refname = NULL; - }else { - /* - * Peel off ""'s, then make a true copy. - */ - - token++; - token[length - 2] = '\0'; - - local_node->refname = (char*)pstrdup(token); - token[length - 2] = '\"'; - } - - token = lsptok(NULL, &length); /* eat :relid */ - token = lsptok(NULL, &length); /* get :relid */ - local_node->relid = atoi(token); - - return (local_node); + token = lsptok(NULL, &length); /* eat :relname */ + token = lsptok(NULL, &length); /* get :relname */ + if (!strncmp(token, "\"null\"", 5)) + { + local_node->relname = NULL; + } + else + { + + /* + * Peel off ""'s, then make a true copy. + */ + + token++; + token[length - 2] = '\0'; + + local_node->relname = (char *) palloc(NAMEDATALEN); + strcpy(local_node->relname, token); + token[length - 2] = '\"'; + } + + token = lsptok(NULL, &length); /* eat :inh */ + token = lsptok(NULL, &length); /* get :inh */ + local_node->inh = atoi(token); + + token = lsptok(NULL, &length); /* eat :refname */ + token = lsptok(NULL, &length); /* get :refname */ + if (!strncmp(token, "\"null\"", 5)) + { + local_node->refname = NULL; + } + else + { + + /* + * Peel off ""'s, then make a true copy. + */ + + token++; + token[length - 2] = '\0'; + + local_node->refname = (char *) pstrdup(token); + token[length - 2] = '\"'; + } + + token = lsptok(NULL, &length); /* eat :relid */ + token = lsptok(NULL, &length); /* get :relid */ + local_node->relid = atoi(token); + + return (local_node); } /* ---------------- - * _readPath - * - * Path is a subclass of Node. + * _readPath + * + * Path is a subclass of Node. * ---------------- */ -static Path * +static Path * _readPath() { - Path *local_node; - char *token; - int length; - - local_node = makeNode(Path); - - token = lsptok(NULL, &length); /* get :pathtype */ - token = lsptok(NULL, &length); /* now read it */ - local_node->pathtype = atol(token); - - token = lsptok(NULL, &length); /* get :cost */ - token = lsptok(NULL, &length); /* now read it */ - local_node->path_cost = (Cost) atof(token); - + Path *local_node; + char *token; + int length; + + local_node = makeNode(Path); + + token = lsptok(NULL, &length); /* get :pathtype */ + token = lsptok(NULL, &length); /* now read it */ + local_node->pathtype = atol(token); + + token = lsptok(NULL, &length); /* get :cost */ + token = lsptok(NULL, &length); /* now read it */ + local_node->path_cost = (Cost) atof(token); + #if 0 - token = lsptok(NULL, &length); /* get :p_ordering */ - local_node->p_ordering = - nodeRead(true); /* now read it */ + token = lsptok(NULL, &length); /* get :p_ordering */ + local_node->p_ordering = + nodeRead(true); /* now read it */ #endif - - token = lsptok(NULL, &length); /* get :keys */ - local_node->keys = nodeRead(true); /* now read it */ - - return(local_node); + + token = lsptok(NULL, &length); /* get :keys */ + local_node->keys = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readIndexPath - * - * IndexPath is a subclass of Path. + * _readIndexPath + * + * IndexPath is a subclass of Path. * ---------------- */ static IndexPath * _readIndexPath() { - IndexPath *local_node; - char *token; - int length; - - local_node = makeNode(IndexPath); - - token = lsptok(NULL, &length); /* get :pathtype */ - token = lsptok(NULL, &length); /* now read it */ - local_node->path.pathtype = atol(token); - - token = lsptok(NULL, &length); /* get :cost */ - token = lsptok(NULL, &length); /* now read it */ - local_node->path.path_cost = (Cost) atof(token); - + IndexPath *local_node; + char *token; + int length; + + local_node = makeNode(IndexPath); + + token = lsptok(NULL, &length); /* get :pathtype */ + token = lsptok(NULL, &length); /* now read it */ + local_node->path.pathtype = atol(token); + + token = lsptok(NULL, &length); /* get :cost */ + token = lsptok(NULL, &length); /* now read it */ + local_node->path.path_cost = (Cost) atof(token); + #if 0 - token = lsptok(NULL, &length); /* get :p_ordering */ - local_node->path.p_ordering = nodeRead(true); /* now read it */ + token = lsptok(NULL, &length); /* get :p_ordering */ + local_node->path.p_ordering = nodeRead(true); /* now read it */ #endif - - token = lsptok(NULL, &length); /* get :keys */ - local_node->path.keys = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :indexid */ - local_node->indexid = - toIntList(nodeRead(true)); - - token = lsptok(NULL, &length); /* get :indexqual */ - local_node->indexqual = nodeRead(true); /* now read it */ - - return(local_node); + + token = lsptok(NULL, &length); /* get :keys */ + local_node->path.keys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :indexid */ + local_node->indexid = + toIntList(nodeRead(true)); + + token = lsptok(NULL, &length); /* get :indexqual */ + local_node->indexqual = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readJoinPath - * - * JoinPath is a subclass of Path + * _readJoinPath + * + * JoinPath is a subclass of Path * ---------------- */ static JoinPath * _readJoinPath() { - JoinPath *local_node; - char *token; - int length; - - - local_node = makeNode(JoinPath); - - token = lsptok(NULL, &length); /* get :pathtype */ - token = lsptok(NULL, &length); /* now read it */ - local_node->path.pathtype = atol(token); - - token = lsptok(NULL, &length); /* get :cost */ - token = lsptok(NULL, &length); /* now read it */ - local_node->path.path_cost = (Cost) atof(token); - + JoinPath *local_node; + char *token; + int length; + + + local_node = makeNode(JoinPath); + + token = lsptok(NULL, &length); /* get :pathtype */ + token = lsptok(NULL, &length); /* now read it */ + local_node->path.pathtype = atol(token); + + token = lsptok(NULL, &length); /* get :cost */ + token = lsptok(NULL, &length); /* now read it */ + local_node->path.path_cost = (Cost) atof(token); + #if 0 - token = lsptok(NULL, &length); /* get :p_ordering */ - local_node->path.p_ordering = nodeRead(true); /* now read it */ + token = lsptok(NULL, &length); /* get :p_ordering */ + local_node->path.p_ordering = nodeRead(true); /* now read it */ #endif - - token = lsptok(NULL, &length); /* get :keys */ - local_node->path.keys = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :pathclauseinfo */ - local_node->pathclauseinfo = nodeRead(true); /* now read it */ - - /* - * Not sure if these are nodes; they're declared as "struct path *". - * For now, i'll just print the addresses. - * - * GJK: Since I am parsing this stuff, I'll just ignore the addresses, - * and initialize these pointers to NULL. - */ - - token = lsptok(NULL, &length); /* get :outerjoinpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->outerjoinpath = NULL; - - token = lsptok(NULL, &length); /* get :innerjoinpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->innerjoinpath = NULL; - - token = lsptok(NULL, &length); /* get :outerjoincost */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->path.outerjoincost = (Cost) atof(token); - - token = lsptok(NULL, &length); /* get :joinid */ - local_node->path.joinid = - toIntList(nodeRead(true)); /* now read it */ - - return(local_node); + + token = lsptok(NULL, &length); /* get :keys */ + local_node->path.keys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :pathclauseinfo */ + local_node->pathclauseinfo = nodeRead(true); /* now read it */ + + /* + * Not sure if these are nodes; they're declared as "struct path *". + * For now, i'll just print the addresses. + * + * GJK: Since I am parsing this stuff, I'll just ignore the addresses, + * and initialize these pointers to NULL. + */ + + token = lsptok(NULL, &length); /* get :outerjoinpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->outerjoinpath = NULL; + + token = lsptok(NULL, &length); /* get :innerjoinpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->innerjoinpath = NULL; + + token = lsptok(NULL, &length); /* get :outerjoincost */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->path.outerjoincost = (Cost) atof(token); + + token = lsptok(NULL, &length); /* get :joinid */ + local_node->path.joinid = + toIntList(nodeRead(true)); /* now read it */ + + return (local_node); } /* ---------------- - * _readMergePath - * - * MergePath is a subclass of JoinPath. + * _readMergePath + * + * MergePath is a subclass of JoinPath. * ---------------- */ static MergePath * _readMergePath() { - MergePath *local_node; - char *token; - int length; - - local_node = makeNode(MergePath); - - token = lsptok(NULL, &length); /* get :pathtype */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.path.pathtype = atol(token); - - token = lsptok(NULL, &length); /* get :cost */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.path.path_cost = (Cost) atof(token); - + MergePath *local_node; + char *token; + int length; + + local_node = makeNode(MergePath); + + token = lsptok(NULL, &length); /* get :pathtype */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.path.pathtype = atol(token); + + token = lsptok(NULL, &length); /* get :cost */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.path.path_cost = (Cost) atof(token); + #if 0 - token = lsptok(NULL, &length); /* get :p_ordering */ - local_node->jpath.path.p_ordering = nodeRead(true); /* now read it */ + token = lsptok(NULL, &length); /* get :p_ordering */ + local_node->jpath.path.p_ordering = nodeRead(true); /* now read it */ #endif - - token = lsptok(NULL, &length); /* get :keys */ - local_node->jpath.path.keys = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :pathclauseinfo */ - local_node->jpath.pathclauseinfo = nodeRead(true); /* now read it */ - - /* - * Not sure if these are nodes; they're declared as "struct path *". - * For now, i'll just print the addresses. - * - * GJK: Since I am parsing this stuff, I'll just ignore the addresses, - * and initialize these pointers to NULL. - */ - - token = lsptok(NULL, &length); /* get :outerjoinpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.outerjoinpath = NULL; - - token = lsptok(NULL, &length); /* get :innerjoinpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.innerjoinpath = NULL; - - token = lsptok(NULL, &length); /* get :outerjoincost */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.path.outerjoincost = (Cost) atof(token); - - token = lsptok(NULL, &length); /* get :joinid */ - local_node->jpath.path.joinid = - toIntList(nodeRead(true)); /* now read it */ - - token = lsptok(NULL, &length); /* get :path_mergeclauses */ - local_node->path_mergeclauses = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :outersortkeys */ - local_node->outersortkeys = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :innersortkeys */ - local_node->innersortkeys = nodeRead(true); /* now read it */ - - return(local_node); + + token = lsptok(NULL, &length); /* get :keys */ + local_node->jpath.path.keys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :pathclauseinfo */ + local_node->jpath.pathclauseinfo = nodeRead(true); /* now read it */ + + /* + * Not sure if these are nodes; they're declared as "struct path *". + * For now, i'll just print the addresses. + * + * GJK: Since I am parsing this stuff, I'll just ignore the addresses, + * and initialize these pointers to NULL. + */ + + token = lsptok(NULL, &length); /* get :outerjoinpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.outerjoinpath = NULL; + + token = lsptok(NULL, &length); /* get :innerjoinpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.innerjoinpath = NULL; + + token = lsptok(NULL, &length); /* get :outerjoincost */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.path.outerjoincost = (Cost) atof(token); + + token = lsptok(NULL, &length); /* get :joinid */ + local_node->jpath.path.joinid = + toIntList(nodeRead(true)); /* now read it */ + + token = lsptok(NULL, &length); /* get :path_mergeclauses */ + local_node->path_mergeclauses = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :outersortkeys */ + local_node->outersortkeys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :innersortkeys */ + local_node->innersortkeys = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readHashPath - * - * HashPath is a subclass of JoinPath. + * _readHashPath + * + * HashPath is a subclass of JoinPath. * ---------------- */ static HashPath * _readHashPath() { - HashPath *local_node; - char *token; - int length; - - local_node = makeNode(HashPath); - - token = lsptok(NULL, &length); /* get :pathtype */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.path.pathtype = atol(token); - - token = lsptok(NULL, &length); /* get :cost */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.path.path_cost = (Cost) atof(token); - + HashPath *local_node; + char *token; + int length; + + local_node = makeNode(HashPath); + + token = lsptok(NULL, &length); /* get :pathtype */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.path.pathtype = atol(token); + + token = lsptok(NULL, &length); /* get :cost */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.path.path_cost = (Cost) atof(token); + #if 0 - token = lsptok(NULL, &length); /* get :p_ordering */ - local_node->jpath.path.p_ordering = nodeRead(true); /* now read it */ + token = lsptok(NULL, &length); /* get :p_ordering */ + local_node->jpath.path.p_ordering = nodeRead(true); /* now read it */ #endif - - token = lsptok(NULL, &length); /* get :keys */ - local_node->jpath.path.keys = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :pathclauseinfo */ - local_node->jpath.pathclauseinfo = nodeRead(true); /* now read it */ - - /* - * Not sure if these are nodes; they're declared as "struct path *". - * For now, i'll just print the addresses. - * - * GJK: Since I am parsing this stuff, I'll just ignore the addresses, - * and initialize these pointers to NULL. - */ - - token = lsptok(NULL, &length); /* get :outerjoinpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.outerjoinpath = NULL; - - token = lsptok(NULL, &length); /* get :innerjoinpath */ - token = lsptok(NULL, &length); /* get @ */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.innerjoinpath = NULL; - - token = lsptok(NULL, &length); /* get :outerjoincost */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->jpath.path.outerjoincost = (Cost) atof(token); - - token = lsptok(NULL, &length); /* get :joinid */ - local_node->jpath.path.joinid = - toIntList(nodeRead(true)); /* now read it */ - - token = lsptok(NULL, &length); /* get :path_hashclauses */ - local_node->path_hashclauses = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :outerhashkeys */ - local_node->outerhashkeys = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :innerhashkeys */ - local_node->innerhashkeys = nodeRead(true); /* now read it */ - - return(local_node); + + token = lsptok(NULL, &length); /* get :keys */ + local_node->jpath.path.keys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :pathclauseinfo */ + local_node->jpath.pathclauseinfo = nodeRead(true); /* now read it */ + + /* + * Not sure if these are nodes; they're declared as "struct path *". + * For now, i'll just print the addresses. + * + * GJK: Since I am parsing this stuff, I'll just ignore the addresses, + * and initialize these pointers to NULL. + */ + + token = lsptok(NULL, &length); /* get :outerjoinpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.outerjoinpath = NULL; + + token = lsptok(NULL, &length); /* get :innerjoinpath */ + token = lsptok(NULL, &length); /* get @ */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.innerjoinpath = NULL; + + token = lsptok(NULL, &length); /* get :outerjoincost */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->jpath.path.outerjoincost = (Cost) atof(token); + + token = lsptok(NULL, &length); /* get :joinid */ + local_node->jpath.path.joinid = + toIntList(nodeRead(true)); /* now read it */ + + token = lsptok(NULL, &length); /* get :path_hashclauses */ + local_node->path_hashclauses = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :outerhashkeys */ + local_node->outerhashkeys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :innerhashkeys */ + local_node->innerhashkeys = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readOrderKey - * - * OrderKey is a subclass of Node. + * _readOrderKey + * + * OrderKey is a subclass of Node. * ---------------- */ static OrderKey * _readOrderKey() { - OrderKey *local_node; - char *token; - int length; - - local_node = makeNode(OrderKey); - - token = lsptok(NULL, &length); /* get :attribute_number */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->attribute_number = atoi(token); - - token = lsptok(NULL, &length); /* get :array_index */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->array_index = atoi(token); - - return(local_node); + OrderKey *local_node; + char *token; + int length; + + local_node = makeNode(OrderKey); + + token = lsptok(NULL, &length); /* get :attribute_number */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->attribute_number = atoi(token); + + token = lsptok(NULL, &length); /* get :array_index */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->array_index = atoi(token); + + return (local_node); } /* ---------------- - * _readJoinKey - * - * JoinKey is a subclass of Node. + * _readJoinKey + * + * JoinKey is a subclass of Node. * ---------------- */ static JoinKey * _readJoinKey() { - JoinKey *local_node; - char *token; - int length; - - local_node = makeNode(JoinKey); - - token = lsptok(NULL, &length); /* get :outer */ - local_node->outer = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :inner */ - local_node->inner = nodeRead(true); /* now read it */ - - return(local_node); + JoinKey *local_node; + char *token; + int length; + + local_node = makeNode(JoinKey); + + token = lsptok(NULL, &length); /* get :outer */ + local_node->outer = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :inner */ + local_node->inner = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readMergeOrder - * - * MergeOrder is a subclass of Node. + * _readMergeOrder + * + * MergeOrder is a subclass of Node. * ---------------- */ static MergeOrder * _readMergeOrder() { - MergeOrder *local_node; - char *token; - int length; - - local_node = makeNode(MergeOrder); - token = lsptok(NULL, &length); /* get :join_operator */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->join_operator = atol(token); - - token = lsptok(NULL, &length); /* get :left_operator */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->left_operator = atol(token); - - token = lsptok(NULL, &length); /* get :right_operator */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->right_operator = atol(token); - - token = lsptok(NULL, &length); /* get :left_type */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->left_type = atol(token); - - token = lsptok(NULL, &length); /* get :right_type */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->right_type = atol(token); - - return(local_node); + MergeOrder *local_node; + char *token; + int length; + + local_node = makeNode(MergeOrder); + token = lsptok(NULL, &length); /* get :join_operator */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->join_operator = atol(token); + + token = lsptok(NULL, &length); /* get :left_operator */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->left_operator = atol(token); + + token = lsptok(NULL, &length); /* get :right_operator */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->right_operator = atol(token); + + token = lsptok(NULL, &length); /* get :left_type */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->left_type = atol(token); + + token = lsptok(NULL, &length); /* get :right_type */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->right_type = atol(token); + + return (local_node); } /* ---------------- - * _readCInfo - * - * CInfo is a subclass of Node. + * _readCInfo + * + * CInfo is a subclass of Node. * ---------------- */ -static CInfo * +static CInfo * _readCInfo() { - CInfo *local_node; - char *token; - int length; - - local_node = makeNode(CInfo); - - token = lsptok(NULL, &length); /* get :clause */ - local_node->clause = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :selectivity */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->selectivity = atof(token); - - token = lsptok(NULL, &length); /* get :notclause */ - token = lsptok(NULL, &length); /* now read it */ - - if (!strncmp(token, "true", 4)) - { - local_node->notclause = true; - } - else - { - local_node->notclause = false; - } - - token = lsptok(NULL, &length); /* get :indexids */ - local_node->indexids = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :mergesortorder */ - local_node->mergesortorder = (MergeOrder*) nodeRead(true); - - token = lsptok(NULL, &length); /* get :hashjoinoperator */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->hashjoinoperator = atol(token); - - return(local_node); + CInfo *local_node; + char *token; + int length; + + local_node = makeNode(CInfo); + + token = lsptok(NULL, &length); /* get :clause */ + local_node->clause = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :selectivity */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->selectivity = atof(token); + + token = lsptok(NULL, &length); /* get :notclause */ + token = lsptok(NULL, &length); /* now read it */ + + if (!strncmp(token, "true", 4)) + { + local_node->notclause = true; + } + else + { + local_node->notclause = false; + } + + token = lsptok(NULL, &length); /* get :indexids */ + local_node->indexids = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :mergesortorder */ + local_node->mergesortorder = (MergeOrder *) nodeRead(true); + + token = lsptok(NULL, &length); /* get :hashjoinoperator */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->hashjoinoperator = atol(token); + + return (local_node); } /* ---------------- - * _readJoinMethod - * - * JoinMethod is a subclass of Node. + * _readJoinMethod + * + * JoinMethod is a subclass of Node. * ---------------- */ static JoinMethod * _readJoinMethod() { - JoinMethod *local_node; - char *token; - int length; - - local_node = makeNode(JoinMethod); - - token = lsptok(NULL, &length); /* get :jmkeys */ - local_node->jmkeys = nodeRead(true);/* now read it */ - - token = lsptok(NULL, &length); /* get :clauses */ - local_node->clauses = nodeRead(true); /* now read it */ - - return(local_node); + JoinMethod *local_node; + char *token; + int length; + + local_node = makeNode(JoinMethod); + + token = lsptok(NULL, &length); /* get :jmkeys */ + local_node->jmkeys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :clauses */ + local_node->clauses = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readHInfo - * + * _readHInfo + * * HInfo is a subclass of JoinMethod. * ---------------- */ -static HInfo * +static HInfo * _readHInfo() { - HInfo *local_node; - char *token; - int length; - - local_node = makeNode(HInfo); - - token = lsptok(NULL, &length); /* get :hashop */ - token = lsptok(NULL, &length); /* now read it */ - - local_node->hashop = atoi(token); - - token = lsptok(NULL, &length); /* get :jmkeys */ - local_node->jmethod.jmkeys = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :clauses */ - local_node->jmethod.clauses = nodeRead(true); /* now read it */ - - return(local_node); + HInfo *local_node; + char *token; + int length; + + local_node = makeNode(HInfo); + + token = lsptok(NULL, &length); /* get :hashop */ + token = lsptok(NULL, &length); /* now read it */ + + local_node->hashop = atoi(token); + + token = lsptok(NULL, &length); /* get :jmkeys */ + local_node->jmethod.jmkeys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :clauses */ + local_node->jmethod.clauses = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * _readJInfo() - * - * JInfo is a subclass of Node. + * _readJInfo() + * + * JInfo is a subclass of Node. * ---------------- */ -static JInfo * +static JInfo * _readJInfo() { - JInfo *local_node; - char *token; - int length; - - local_node = makeNode(JInfo); - - token = lsptok(NULL, &length); /* get :otherrels */ - local_node->otherrels = - toIntList(nodeRead(true)); /* now read it */ - - token = lsptok(NULL, &length); /* get :jinfoclauseinfo */ - local_node->jinfoclauseinfo = nodeRead(true); /* now read it */ - - token = lsptok(NULL, &length); /* get :mergesortable */ - - if (!strncmp(token, "true", 4)) - { - local_node->mergesortable = true; - } - else - { - local_node->mergesortable = false; - } - - token = lsptok(NULL, &length); /* get :hashjoinable */ - - if (!strncmp(token, "true", 4)) - { - local_node->hashjoinable = true; - } - else - { - local_node->hashjoinable = false; - } - - return(local_node); + JInfo *local_node; + char *token; + int length; + + local_node = makeNode(JInfo); + + token = lsptok(NULL, &length); /* get :otherrels */ + local_node->otherrels = + toIntList(nodeRead(true)); /* now read it */ + + token = lsptok(NULL, &length); /* get :jinfoclauseinfo */ + local_node->jinfoclauseinfo = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :mergesortable */ + + if (!strncmp(token, "true", 4)) + { + local_node->mergesortable = true; + } + else + { + local_node->mergesortable = false; + } + + token = lsptok(NULL, &length); /* get :hashjoinable */ + + if (!strncmp(token, "true", 4)) + { + local_node->hashjoinable = true; + } + else + { + local_node->hashjoinable = false; + } + + return (local_node); } /* ---------------- - * _readIter() + * _readIter() * * ---------------- */ -static Iter * +static Iter * _readIter() { - Iter *local_node; - char *token; - int length; - - local_node = makeNode(Iter); - - token = lsptok(NULL, &length); /* eat :iterexpr */ - local_node->iterexpr = nodeRead(true); /* now read it */ - - return(local_node); + Iter *local_node; + char *token; + int length; + + local_node = makeNode(Iter); + + token = lsptok(NULL, &length); /* eat :iterexpr */ + local_node->iterexpr = nodeRead(true); /* now read it */ + + return (local_node); } /* ---------------- - * parsePlanString + * parsePlanString * * Given a character string containing a plan, parsePlanString sets up the * plan structure representing that plan. @@ -1823,164 +1867,263 @@ _readIter() * The string passed to parsePlanString must be null-terminated. * ---------------- */ -Node * +Node * parsePlanString(void) { - char *token; - int length; - void *return_value = NULL; - - token = lsptok(NULL, &length); - - if (!strncmp(token, "PLAN", 4)) { - return_value = _readPlan(); - }else if (!strncmp(token, "RESULT", 6)) { - return_value = _readResult(); - }else if (!strncmp(token, "EXISTENTIAL", 11)) { - return_value = _readExistential(); - }else if (!strncmp(token, "APPEND", 6)) { - return_value = _readAppend(); - }else if (!strncmp(token, "JOIN", 4)) { - return_value = _readJoin(); - }else if (!strncmp(token, "NESTLOOP", 8)) { - return_value = _readNestLoop(); - }else if (!strncmp(token, "MERGEJOIN", 9)) { - return_value = _readMergeJoin(); - }else if (!strncmp(token, "HASHJOIN", 8)) { - return_value = _readHashJoin(); - }else if (!strncmp(token, "SCAN", 4)) { - return_value = _readScan(); - }else if (!strncmp(token, "SEQSCAN", 7)) { - return_value = _readSeqScan(); - }else if (!strncmp(token, "INDEXSCAN", 9)) { - return_value = _readIndexScan(); - }else if (!strncmp(token, "TEMP", 4)) { - return_value = _readTemp(); - }else if (!strncmp(token, "SORT", 4)) { - return_value = _readSort(); - }else if (!strncmp(token, "AGGREG", 6)) { - return_value = _readAggreg(); - }else if (!strncmp(token, "AGG", 3)) { - return_value = _readAgg(); - }else if (!strncmp(token, "UNIQUE", 4)) { - return_value = _readUnique(); - }else if (!strncmp(token, "HASH", 4)) { - return_value = _readHash(); - }else if (!strncmp(token, "RESDOM", 6)) { - return_value = _readResdom(); - }else if (!strncmp(token, "EXPR", 4)) { - return_value = _readExpr(); - }else if (!strncmp(token, "ARRAYREF", 7)) { - /* make sure this strncmp is done before that of ARRAY */ - return_value = _readArrayRef(); - }else if (!strncmp(token, "ARRAY", 5)) { - return_value = _readArray(); - }else if (!strncmp(token, "VAR", 3)) { - return_value = _readVar(); - }else if (!strncmp(token, "CONST", 5)) { - return_value = _readConst(); - }else if (!strncmp(token, "FUNC", 4)) { - return_value = _readFunc(); - }else if (!strncmp(token, "OPER", 4)) { - return_value = _readOper(); - }else if (!strncmp(token, "PARAM", 5)) { - return_value = _readParam(); - }else if (!strncmp(token, "ESTATE", 6)) { - return_value = _readEState(); - }else if (!strncmp(token, "REL", 3)) { - return_value = _readRel(); - }else if (!strncmp(token, "TLE", 3)) { - return_value = _readTargetEntry(); - }else if (!strncmp(token, "RTE", 3)) { - return_value = _readRangeTblEntry(); - }else if (!strncmp(token, "PATH", 4)) { - return_value = _readPath(); - }else if (!strncmp(token, "INDEXPATH", 9)) { - return_value = _readIndexPath(); - }else if (!strncmp(token, "JOINPATH", 8)) { - return_value = _readJoinPath(); - }else if (!strncmp(token, "MERGEPATH", 9)) { - return_value = _readMergePath(); - }else if (!strncmp(token, "HASHPATH", 8)) { - return_value = _readHashPath(); - }else if (!strncmp(token, "ORDERKEY", 8)) { - return_value = _readOrderKey(); - }else if (!strncmp(token, "JOINKEY", 7)) { - return_value = _readJoinKey(); - }else if (!strncmp(token, "MERGEORDER", 10)) { - return_value = _readMergeOrder(); - }else if (!strncmp(token, "CINFO", 5)) { - return_value = _readCInfo(); - }else if (!strncmp(token, "JOINMETHOD", 10)) { - return_value = _readJoinMethod(); - }else if (!strncmp(token, "JINFO", 5)) { - return_value = _readJInfo(); - }else if (!strncmp(token, "HINFO", 5)) { - return_value = _readHInfo(); - }else if (!strncmp(token, "ITER", 4)) { - return_value = _readIter(); - }else if (!strncmp(token, "QUERY", 5)) { - return_value = _readQuery(); - }else { - elog(WARN, "badly formatted planstring \"%.10s\"...\n", token); - } - - return ((Node*)return_value); + char *token; + int length; + void *return_value = NULL; + + token = lsptok(NULL, &length); + + if (!strncmp(token, "PLAN", 4)) + { + return_value = _readPlan(); + } + else if (!strncmp(token, "RESULT", 6)) + { + return_value = _readResult(); + } + else if (!strncmp(token, "EXISTENTIAL", 11)) + { + return_value = _readExistential(); + } + else if (!strncmp(token, "APPEND", 6)) + { + return_value = _readAppend(); + } + else if (!strncmp(token, "JOIN", 4)) + { + return_value = _readJoin(); + } + else if (!strncmp(token, "NESTLOOP", 8)) + { + return_value = _readNestLoop(); + } + else if (!strncmp(token, "MERGEJOIN", 9)) + { + return_value = _readMergeJoin(); + } + else if (!strncmp(token, "HASHJOIN", 8)) + { + return_value = _readHashJoin(); + } + else if (!strncmp(token, "SCAN", 4)) + { + return_value = _readScan(); + } + else if (!strncmp(token, "SEQSCAN", 7)) + { + return_value = _readSeqScan(); + } + else if (!strncmp(token, "INDEXSCAN", 9)) + { + return_value = _readIndexScan(); + } + else if (!strncmp(token, "TEMP", 4)) + { + return_value = _readTemp(); + } + else if (!strncmp(token, "SORT", 4)) + { + return_value = _readSort(); + } + else if (!strncmp(token, "AGGREG", 6)) + { + return_value = _readAggreg(); + } + else if (!strncmp(token, "AGG", 3)) + { + return_value = _readAgg(); + } + else if (!strncmp(token, "UNIQUE", 4)) + { + return_value = _readUnique(); + } + else if (!strncmp(token, "HASH", 4)) + { + return_value = _readHash(); + } + else if (!strncmp(token, "RESDOM", 6)) + { + return_value = _readResdom(); + } + else if (!strncmp(token, "EXPR", 4)) + { + return_value = _readExpr(); + } + else if (!strncmp(token, "ARRAYREF", 7)) + { + /* make sure this strncmp is done before that of ARRAY */ + return_value = _readArrayRef(); + } + else if (!strncmp(token, "ARRAY", 5)) + { + return_value = _readArray(); + } + else if (!strncmp(token, "VAR", 3)) + { + return_value = _readVar(); + } + else if (!strncmp(token, "CONST", 5)) + { + return_value = _readConst(); + } + else if (!strncmp(token, "FUNC", 4)) + { + return_value = _readFunc(); + } + else if (!strncmp(token, "OPER", 4)) + { + return_value = _readOper(); + } + else if (!strncmp(token, "PARAM", 5)) + { + return_value = _readParam(); + } + else if (!strncmp(token, "ESTATE", 6)) + { + return_value = _readEState(); + } + else if (!strncmp(token, "REL", 3)) + { + return_value = _readRel(); + } + else if (!strncmp(token, "TLE", 3)) + { + return_value = _readTargetEntry(); + } + else if (!strncmp(token, "RTE", 3)) + { + return_value = _readRangeTblEntry(); + } + else if (!strncmp(token, "PATH", 4)) + { + return_value = _readPath(); + } + else if (!strncmp(token, "INDEXPATH", 9)) + { + return_value = _readIndexPath(); + } + else if (!strncmp(token, "JOINPATH", 8)) + { + return_value = _readJoinPath(); + } + else if (!strncmp(token, "MERGEPATH", 9)) + { + return_value = _readMergePath(); + } + else if (!strncmp(token, "HASHPATH", 8)) + { + return_value = _readHashPath(); + } + else if (!strncmp(token, "ORDERKEY", 8)) + { + return_value = _readOrderKey(); + } + else if (!strncmp(token, "JOINKEY", 7)) + { + return_value = _readJoinKey(); + } + else if (!strncmp(token, "MERGEORDER", 10)) + { + return_value = _readMergeOrder(); + } + else if (!strncmp(token, "CINFO", 5)) + { + return_value = _readCInfo(); + } + else if (!strncmp(token, "JOINMETHOD", 10)) + { + return_value = _readJoinMethod(); + } + else if (!strncmp(token, "JINFO", 5)) + { + return_value = _readJInfo(); + } + else if (!strncmp(token, "HINFO", 5)) + { + return_value = _readHInfo(); + } + else if (!strncmp(token, "ITER", 4)) + { + return_value = _readIter(); + } + else if (!strncmp(token, "QUERY", 5)) + { + return_value = _readQuery(); + } + else + { + elog(WARN, "badly formatted planstring \"%.10s\"...\n", token); + } + + return ((Node *) return_value); } + /*------------------------------------------------------------*/ /* ---------------- - * readDatum + * readDatum * * given a string representation of the value of the given type, * create the appropriate Datum * ---------------- */ -static Datum +static Datum readDatum(Oid type) { - int length; - int tokenLength; - char *token; - bool byValue; - Datum res; - char *s; - int i; - - byValue = get_typbyval(type); - - /* - * read the actual length of the value - */ - token = lsptok(NULL, &tokenLength); - length = atoi(token); - token = lsptok(NULL, &tokenLength); /* skip the '[' */ - - if (byValue) { - if (length > sizeof(Datum)) { - elog(WARN, "readValue: byval & length = %d", length); - } - s = (char *) (&res); - for (i=0; i<sizeof(Datum); i++) { - token = lsptok(NULL, &tokenLength); - s[i] = (char) atoi(token); - } - } else if (length <= 0) { - s = NULL; - } else if (length >= 1) { - s = (char*)palloc(length); - Assert( s!=NULL ); - for (i=0; i<length; i++) { - token = lsptok(NULL, &tokenLength); - s[i] = (char) atoi(token); - } - res = PointerGetDatum(s); - } - - token = lsptok(NULL, &tokenLength); /* skip the ']' */ - if (token[0] != ']') { - elog(WARN, "readValue: ']' expected, length =%d", length); - } - - return(res); + int length; + int tokenLength; + char *token; + bool byValue; + Datum res; + char *s; + int i; + + byValue = get_typbyval(type); + + /* + * read the actual length of the value + */ + token = lsptok(NULL, &tokenLength); + length = atoi(token); + token = lsptok(NULL, &tokenLength); /* skip the '[' */ + + if (byValue) + { + if (length > sizeof(Datum)) + { + elog(WARN, "readValue: byval & length = %d", length); + } + s = (char *) (&res); + for (i = 0; i < sizeof(Datum); i++) + { + token = lsptok(NULL, &tokenLength); + s[i] = (char) atoi(token); + } + } + else if (length <= 0) + { + s = NULL; + } + else if (length >= 1) + { + s = (char *) palloc(length); + Assert(s != NULL); + for (i = 0; i < length; i++) + { + token = lsptok(NULL, &tokenLength); + s[i] = (char) atoi(token); + } + res = PointerGetDatum(s); + } + + token = lsptok(NULL, &tokenLength); /* skip the ']' */ + if (token[0] != ']') + { + elog(WARN, "readValue: ']' expected, length =%d", length); + } + + return (res); } diff --git a/src/backend/optimizer/geqo/geqo_copy.c b/src/backend/optimizer/geqo/geqo_copy.c index 3356f8d5475..4c35f99f9f5 100644 --- a/src/backend/optimizer/geqo/geqo_copy.c +++ b/src/backend/optimizer/geqo/geqo_copy.c @@ -4,32 +4,32 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_copy.c,v 1.1 1997/02/19 12:56:40 scrappy Exp $ + * $Id: geqo_copy.c,v 1.2 1997/09/07 04:43:01 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* this is adopted from D. Whitley's Genitor algorithm */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ #include "postgres.h" @@ -52,16 +52,16 @@ /* geqo_copy-- * - * copies one gene to another + * copies one gene to another * */ void -geqo_copy (Chromosome *chromo1, Chromosome *chromo2, int string_length) +geqo_copy(Chromosome * chromo1, Chromosome * chromo2, int string_length) { - int i; + int i; - for (i=0; i<string_length; i++) - chromo1->string[i] = chromo2->string[i]; + for (i = 0; i < string_length; i++) + chromo1->string[i] = chromo2->string[i]; - chromo1->worth = chromo2->worth; + chromo1->worth = chromo2->worth; } diff --git a/src/backend/optimizer/geqo/geqo_cx.c b/src/backend/optimizer/geqo/geqo_cx.c index a3aa6fce4f4..dfde1bdc530 100644 --- a/src/backend/optimizer/geqo/geqo_cx.c +++ b/src/backend/optimizer/geqo/geqo_cx.c @@ -2,35 +2,35 @@ * * geqo_cx.c-- * -* cycle crossover [CX] routines; -* CX operator according to Oliver et al -* (Proc 2nd Int'l Conf on GA's) +* cycle crossover [CX] routines; +* CX operator according to Oliver et al +* (Proc 2nd Int'l Conf on GA's) * -* $Id: geqo_cx.c,v 1.1 1997/02/19 12:56:48 scrappy Exp $ +* $Id: geqo_cx.c,v 1.2 1997/09/07 04:43:02 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* the cx algorithm is adopted from Genitor : */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ @@ -57,73 +57,81 @@ /* cx-- * - * cycle crossover + * cycle crossover */ int -cx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table) +cx(Gene * tour1, Gene * tour2, Gene * offspring, int num_gene, City * city_table) { - int i, start_pos, curr_pos; - int count = 0; - int num_diffs = 0; + int i, + start_pos, + curr_pos; + int count = 0; + int num_diffs = 0; - /* initialize city table */ - for (i=1; i<=num_gene; i++) { - city_table[i].used = 0; - city_table[tour2[i-1]].tour2_position = i-1; - city_table[tour1[i-1]].tour1_position = i-1; - } + /* initialize city table */ + for (i = 1; i <= num_gene; i++) + { + city_table[i].used = 0; + city_table[tour2[i - 1]].tour2_position = i - 1; + city_table[tour1[i - 1]].tour1_position = i - 1; + } - /* choose random cycle starting position */ - start_pos = geqo_randint(num_gene - 1, 0); + /* choose random cycle starting position */ + start_pos = geqo_randint(num_gene - 1, 0); - /* child inherits first city */ - offspring[start_pos] = tour1[start_pos]; + /* child inherits first city */ + offspring[start_pos] = tour1[start_pos]; - /* begin cycle with tour1 */ - curr_pos = start_pos; - city_table[(int) tour1[start_pos]].used = 1; + /* begin cycle with tour1 */ + curr_pos = start_pos; + city_table[(int) tour1[start_pos]].used = 1; - count++; + count++; - /* cx main part */ + /* cx main part */ /* STEP 1 */ - while (tour2[curr_pos] != tour1[start_pos]) { - city_table[(int) tour2[curr_pos]].used = 1; - curr_pos = city_table[(int) tour2[curr_pos]].tour1_position; - offspring[curr_pos] = tour1[curr_pos]; - count++; - } + while (tour2[curr_pos] != tour1[start_pos]) + { + city_table[(int) tour2[curr_pos]].used = 1; + curr_pos = city_table[(int) tour2[curr_pos]].tour1_position; + offspring[curr_pos] = tour1[curr_pos]; + count++; + } /* STEP 2 */ - /* failed to create a complete tour */ - if (count < num_gene) { - for (i=1; i<=num_gene; i++) { - if (!city_table[i].used) { - offspring[city_table[i].tour2_position] = - tour2[(int) city_table[i].tour2_position]; - count++; - } - } - } + /* failed to create a complete tour */ + if (count < num_gene) + { + for (i = 1; i <= num_gene; i++) + { + if (!city_table[i].used) + { + offspring[city_table[i].tour2_position] = + tour2[(int) city_table[i].tour2_position]; + count++; + } + } + } /* STEP 3 */ - /* still failed to create a complete tour */ - if (count < num_gene) { + /* still failed to create a complete tour */ + if (count < num_gene) + { - /* count the number of differences between mom and offspring */ - for (i=0; i<num_gene; i++) - if (tour1[i] != offspring[i]) num_diffs++; + /* count the number of differences between mom and offspring */ + for (i = 0; i < num_gene; i++) + if (tour1[i] != offspring[i]) + num_diffs++; - } - - return(num_diffs); - } + } + return (num_diffs); +} diff --git a/src/backend/optimizer/geqo/geqo_erx.c b/src/backend/optimizer/geqo/geqo_erx.c index 8c3c63d755c..9d0f93efe8c 100644 --- a/src/backend/optimizer/geqo/geqo_erx.c +++ b/src/backend/optimizer/geqo/geqo_erx.c @@ -1,33 +1,33 @@ /*------------------------------------------------------------------------ * * geqo_erx.c-- -* edge recombination crossover [ER] +* edge recombination crossover [ER] * -* $Id: geqo_erx.c,v 1.2 1997/06/06 00:37:23 scrappy Exp $ +* $Id: geqo_erx.c,v 1.3 1997/09/07 04:43:04 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* the edge recombination algorithm is adopted from Genitor : */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ @@ -52,384 +52,441 @@ #include "optimizer/geqo_random.h" -static int gimme_edge (Gene gene1, Gene gene2, Edge *edge_table); -static void remove_gene(Gene gene, Edge edge, Edge *edge_table); -static Gene gimme_gene(Edge edge, Edge *edge_table); +static int gimme_edge(Gene gene1, Gene gene2, Edge * edge_table); +static void remove_gene(Gene gene, Edge edge, Edge * edge_table); +static Gene gimme_gene(Edge edge, Edge * edge_table); -static Gene edge_failure(Gene *gene, int index, Edge *edge_table, int num_gene); +static Gene edge_failure(Gene * gene, int index, Edge * edge_table, int num_gene); /* alloc_edge_table-- * - * allocate memory for edge table + * allocate memory for edge table * */ -Edge * +Edge * alloc_edge_table(int num_gene) { - Edge *edge_table; + Edge *edge_table; - /* palloc one extra location so that nodes numbered - 1..n can be indexed directly; 0 will not be used */ + /* + * palloc one extra location so that nodes numbered 1..n can be + * indexed directly; 0 will not be used + */ - edge_table = (Edge *) palloc ((num_gene+1)*sizeof(Edge)); + edge_table = (Edge *) palloc((num_gene + 1) * sizeof(Edge)); - return (edge_table); - } + return (edge_table); +} /* free_edge_table-- * - * deallocate memory of edge table + * deallocate memory of edge table * */ - void - free_edge_table(Edge *edge_table) - { - pfree(edge_table); - } +void +free_edge_table(Edge * edge_table) +{ + pfree(edge_table); +} /* gimme_edge_table-- * - * fills a data structure which represents the set of explicit - * edges between points in the (2) input genes + * fills a data structure which represents the set of explicit + * edges between points in the (2) input genes + * + * assumes circular tours and bidirectional edges * - * assumes circular tours and bidirectional edges - * - * gimme_edge() will set "shared" edges to negative values + * gimme_edge() will set "shared" edges to negative values * - * returns average number edges/city in range 2.0 - 4.0 - * where 2.0=homogeneous; 4.0=diverse + * returns average number edges/city in range 2.0 - 4.0 + * where 2.0=homogeneous; 4.0=diverse * */ float -gimme_edge_table (Gene *tour1, Gene *tour2, int num_gene, Edge *edge_table) +gimme_edge_table(Gene * tour1, Gene * tour2, int num_gene, Edge * edge_table) { - int i, index1, index2; - int edge_total; /* total number of unique edges in two genes */ - - /* at first clear the edge table's old data */ - for (i = 1; i <= num_gene; i++) { - edge_table[i].total_edges = 0; - edge_table[i].unused_edges = 0; + int i, + index1, + index2; + int edge_total; /* total number of unique edges in two + * genes */ + + /* at first clear the edge table's old data */ + for (i = 1; i <= num_gene; i++) + { + edge_table[i].total_edges = 0; + edge_table[i].unused_edges = 0; } - /* fill edge table with new data */ + /* fill edge table with new data */ - edge_total = 0; + edge_total = 0; - for (index1 = 0; index1 < num_gene; index1++) { + for (index1 = 0; index1 < num_gene; index1++) + { - /* presume the tour is circular, i.e. 1->2, 2->3, 3->1 - this operaton maps n back to 1 */ + /* + * presume the tour is circular, i.e. 1->2, 2->3, 3->1 this + * operaton maps n back to 1 + */ - index2 = (index1 + 1) % num_gene; + index2 = (index1 + 1) % num_gene; - /* edges are bidirectional, i.e. 1->2 is same as 2->1 - call gimme_edge twice per edge */ + /* + * edges are bidirectional, i.e. 1->2 is same as 2->1 call + * gimme_edge twice per edge + */ - edge_total += gimme_edge(tour1[index1], tour1[index2], edge_table); - gimme_edge(tour1[index2], tour1[index1], edge_table); + edge_total += gimme_edge(tour1[index1], tour1[index2], edge_table); + gimme_edge(tour1[index2], tour1[index1], edge_table); - edge_total += gimme_edge(tour2[index1], tour2[index2], edge_table); - gimme_edge(tour2[index2], tour2[index1], edge_table); - } + edge_total += gimme_edge(tour2[index1], tour2[index2], edge_table); + gimme_edge(tour2[index2], tour2[index1], edge_table); + } - /* return average number of edges per index */ - return (((float) (edge_total * 2)/ (float) num_gene)); + /* return average number of edges per index */ + return (((float) (edge_total * 2) / (float) num_gene)); } /* gimme_edge-- * - * registers edge from city1 to city2 in input edge table + * registers edge from city1 to city2 in input edge table * - * no assumptions about directionality are made; - * therefor it is up to the calling routine to - * call gimme_edge twice to make a bi-directional edge - * between city1 and city2; - * uni-directional edges are possible as well (just call gimme_edge - * once with the direction from city1 to city2) + * no assumptions about directionality are made; + * therefor it is up to the calling routine to + * call gimme_edge twice to make a bi-directional edge + * between city1 and city2; + * uni-directional edges are possible as well (just call gimme_edge + * once with the direction from city1 to city2) * - * returns 1 if edge was not already registered and was just added; - * 0 if edge was already registered and edge_table is unchanged + * returns 1 if edge was not already registered and was just added; + * 0 if edge was already registered and edge_table is unchanged */ static int -gimme_edge (Gene gene1, Gene gene2, Edge *edge_table) +gimme_edge(Gene gene1, Gene gene2, Edge * edge_table) { - int i; - int edges; - int city1 = (int) gene1; - int city2 = (int) gene2; + int i; + int edges; + int city1 = (int) gene1; + int city2 = (int) gene2; - /* check whether edge city1->city2 already exists */ - edges = edge_table[city1].total_edges; + /* check whether edge city1->city2 already exists */ + edges = edge_table[city1].total_edges; - for (i=0; i<edges; i++) { - if ((Gene) Abs(edge_table[city1].edge_list[i]) == city2) { + for (i = 0; i < edges; i++) + { + if ((Gene) Abs(edge_table[city1].edge_list[i]) == city2) + { - /* mark shared edges as negative */ - edge_table[city1].edge_list[i] = 0-city2; + /* mark shared edges as negative */ + edge_table[city1].edge_list[i] = 0 - city2; - return (0); - } - } + return (0); + } + } - /* add city1->city2; */ - edge_table[city1].edge_list[edges] = city2; + /* add city1->city2; */ + edge_table[city1].edge_list[edges] = city2; - /* increment the number of edges from city1 */ - edge_table[city1].total_edges++; - edge_table[city1].unused_edges++; + /* increment the number of edges from city1 */ + edge_table[city1].total_edges++; + edge_table[city1].unused_edges++; - return (1); + return (1); } /* gimme_tour-- * - * creates a new tour using edges from the edge table. - * priority is given to "shared" edges (i.e. edges which - * all parent genes possess and are marked as negative - * in the edge table.) + * creates a new tour using edges from the edge table. + * priority is given to "shared" edges (i.e. edges which + * all parent genes possess and are marked as negative + * in the edge table.) * */ int -gimme_tour (Edge *edge_table, Gene *new_gene, int num_gene) +gimme_tour(Edge * edge_table, Gene * new_gene, int num_gene) { - int i; - int edge_failures=0; + int i; + int edge_failures = 0; - new_gene[0] = (Gene) geqo_randint(num_gene, 1); /* choose int between 1 and num_gene */ + new_gene[0] = (Gene) geqo_randint(num_gene, 1); /* choose int between 1 + * and num_gene */ - for (i=1; i<num_gene; i++) { - - /* as each point is entered into the tour, - remove it from the edge table */ + for (i = 1; i < num_gene; i++) + { - remove_gene(new_gene[i-1], edge_table[(int) new_gene[i-1]], edge_table); - - /* find destination for the newly entered point */ + /* + * as each point is entered into the tour, remove it from the edge + * table + */ - if (edge_table[new_gene[i-1]].unused_edges > 0) { - new_gene[i] = gimme_gene(edge_table[(int) new_gene[i-1]], edge_table); - } + remove_gene(new_gene[i - 1], edge_table[(int) new_gene[i - 1]], edge_table); - else { /* cope with fault */ - edge_failures++; + /* find destination for the newly entered point */ - new_gene[i] = edge_failure(new_gene, i-1, edge_table, num_gene); - } + if (edge_table[new_gene[i - 1]].unused_edges > 0) + { + new_gene[i] = gimme_gene(edge_table[(int) new_gene[i - 1]], edge_table); + } - /* mark this node as incorporated */ - edge_table[(int) new_gene[i-1]].unused_edges = -1; + else + { /* cope with fault */ + edge_failures++; - } /* for (i=1; i<num_gene; i++) */ + new_gene[i] = edge_failure(new_gene, i - 1, edge_table, num_gene); + } -return(edge_failures); + /* mark this node as incorporated */ + edge_table[(int) new_gene[i - 1]].unused_edges = -1; + + } /* for (i=1; i<num_gene; i++) */ + + return (edge_failures); } /* remove_gene-- * - * removes input gene from edge_table. - * input edge is used - * to identify deletion locations within edge table. + * removes input gene from edge_table. + * input edge is used + * to identify deletion locations within edge table. * */ static void -remove_gene (Gene gene, Edge edge, Edge *edge_table) +remove_gene(Gene gene, Edge edge, Edge * edge_table) { - int i,j; - int possess_edge; - int genes_remaining; + int i, + j; + int possess_edge; + int genes_remaining; - /* do for every gene known to have an edge to input gene - (i.e. in edge_list for input edge) */ + /* + * do for every gene known to have an edge to input gene (i.e. in + * edge_list for input edge) + */ - for (i=0; i<edge.unused_edges; i++) { - possess_edge = (int) Abs(edge.edge_list[i]); - genes_remaining = edge_table[possess_edge].unused_edges; + for (i = 0; i < edge.unused_edges; i++) + { + possess_edge = (int) Abs(edge.edge_list[i]); + genes_remaining = edge_table[possess_edge].unused_edges; - /* find the input gene in all edge_lists and delete it */ - for (j=0; j<genes_remaining; j++) { + /* find the input gene in all edge_lists and delete it */ + for (j = 0; j < genes_remaining; j++) + { - if ( (Gene) Abs(edge_table[possess_edge].edge_list[j]) == gene) { + if ((Gene) Abs(edge_table[possess_edge].edge_list[j]) == gene) + { - edge_table[possess_edge].unused_edges--; + edge_table[possess_edge].unused_edges--; - edge_table[possess_edge].edge_list[j] = - edge_table[possess_edge].edge_list[genes_remaining-1]; + edge_table[possess_edge].edge_list[j] = + edge_table[possess_edge].edge_list[genes_remaining - 1]; - break; - } + break; + } } - } + } } /* gimme_gene-- * - * priority is given to "shared" edges - * (i.e. edges which both genes possess) + * priority is given to "shared" edges + * (i.e. edges which both genes possess) * */ -static Gene -gimme_gene (Edge edge, Edge *edge_table) +static Gene +gimme_gene(Edge edge, Edge * edge_table) { - int i; - Gene friend; - int minimum_edges; - int minimum_count = -1; - int rand_decision; - - /* no point has edges to more than 4 other points - thus, this contrived minimum will be replaced */ - - minimum_edges = 5; - - /* consider candidate destination points in edge list */ - - for (i=0; i<edge.unused_edges; i++) { - friend = (Gene) edge.edge_list[i]; - - /* give priority to shared edges that are negative; - so return 'em */ - - /* negative values are caught here - so we need not worry about converting to absolute values */ - if (friend < 0) return ( (Gene) Abs(friend)); - - - /* give priority to candidates with fewest remaining unused edges; - find out what the minimum number of unused edges is (minimum_edges); - if there is more than one cadidate with the minimum number - of unused edges keep count of this number (minimum_count); */ - - /* The test for minimum_count can probably be removed at some - point but comments should probably indicate exactly why it - is guaranteed that the test will always succeed the first - time around. If it can fail then the code is in error */ - - - if (edge_table[(int) friend].unused_edges < minimum_edges) { - minimum_edges = edge_table[(int) friend].unused_edges; - minimum_count = 1; + int i; + Gene friend; + int minimum_edges; + int minimum_count = -1; + int rand_decision; + + /* + * no point has edges to more than 4 other points thus, this contrived + * minimum will be replaced + */ + + minimum_edges = 5; + + /* consider candidate destination points in edge list */ + + for (i = 0; i < edge.unused_edges; i++) + { + friend = (Gene) edge.edge_list[i]; + + /* + * give priority to shared edges that are negative; so return 'em + */ + + /* + * negative values are caught here so we need not worry about + * converting to absolute values + */ + if (friend < 0) + return ((Gene) Abs(friend)); + + + /* + * give priority to candidates with fewest remaining unused edges; + * find out what the minimum number of unused edges is + * (minimum_edges); if there is more than one cadidate with the + * minimum number of unused edges keep count of this number + * (minimum_count); + */ + + /* + * The test for minimum_count can probably be removed at some + * point but comments should probably indicate exactly why it is + * guaranteed that the test will always succeed the first time + * around. If it can fail then the code is in error + */ + + + if (edge_table[(int) friend].unused_edges < minimum_edges) + { + minimum_edges = edge_table[(int) friend].unused_edges; + minimum_count = 1; } - else - if (minimum_count == -1) - elog(WARN, "gimme_gene: Internal error - minimum_count not set"); - else - if (edge_table[(int) friend].unused_edges == minimum_edges) - minimum_count++; + else if (minimum_count == -1) + elog(WARN, "gimme_gene: Internal error - minimum_count not set"); + else if (edge_table[(int) friend].unused_edges == minimum_edges) + minimum_count++; + + } /* for (i=0; i<edge.unused_edges; i++) */ - } /* for (i=0; i<edge.unused_edges; i++) */ - - /* random decision of the possible candidates to use */ - rand_decision = (int) geqo_randint(minimum_count-1, 0); + /* random decision of the possible candidates to use */ + rand_decision = (int) geqo_randint(minimum_count - 1, 0); - - for (i=0; i<edge.unused_edges; i++) { - friend = (Gene) edge.edge_list[i]; - /* return the chosen candidate point */ - if (edge_table[(int) friend].unused_edges == minimum_edges) { - minimum_count--; + for (i = 0; i < edge.unused_edges; i++) + { + friend = (Gene) edge.edge_list[i]; - if ( minimum_count == rand_decision ) return (friend); + /* return the chosen candidate point */ + if (edge_table[(int) friend].unused_edges == minimum_edges) + { + minimum_count--; + + if (minimum_count == rand_decision) + return (friend); } } - /* ... should never be reached */ - elog(WARN,"gimme_gene: neither shared nor minimum number nor random edge found"); - return 0; /* to keep the compiler quiet */ + /* ... should never be reached */ + elog(WARN, "gimme_gene: neither shared nor minimum number nor random edge found"); + return 0; /* to keep the compiler quiet */ } /* edge_failure-- * - * routine for handling edge failure + * routine for handling edge failure * */ -static Gene -edge_failure (Gene *gene, int index, Edge *edge_table, int num_gene) +static Gene +edge_failure(Gene * gene, int index, Edge * edge_table, int num_gene) { - int i; - Gene fail_gene = gene[index]; - int remaining_edges = 0; - int four_count = 0; - int rand_decision; - - - /* how many edges remain? - how many gene with four total (initial) edges remain? */ - - for (i=1; i<=num_gene; i++) { - if ( (edge_table[i].unused_edges != -1) && (i != (int) fail_gene) ) { - remaining_edges++; - - if (edge_table[i].total_edges == 4) four_count++; - } - } + int i; + Gene fail_gene = gene[index]; + int remaining_edges = 0; + int four_count = 0; + int rand_decision; + + + /* + * how many edges remain? how many gene with four total (initial) + * edges remain? + */ + + for (i = 1; i <= num_gene; i++) + { + if ((edge_table[i].unused_edges != -1) && (i != (int) fail_gene)) + { + remaining_edges++; + + if (edge_table[i].total_edges == 4) + four_count++; + } + } - /* random decision of the gene - with remaining edges and whose total_edges == 4 */ + /* + * random decision of the gene with remaining edges and whose + * total_edges == 4 + */ - if (four_count != 0 ) { + if (four_count != 0) + { - rand_decision = (int) geqo_randint(four_count-1, 0); + rand_decision = (int) geqo_randint(four_count - 1, 0); - for (i=1; i<=num_gene; i++) { + for (i = 1; i <= num_gene; i++) + { - if ((Gene) i != fail_gene && + if ((Gene) i != fail_gene && edge_table[i].unused_edges != -1 && - edge_table[i].total_edges==4) { + edge_table[i].total_edges == 4) + { four_count--; - if (rand_decision == four_count) return ((Gene) i); - } + if (rand_decision == four_count) + return ((Gene) i); } + } - elog(DEBUG,"edge_failure(1): no edge found via random decision and total_edges == 4"); + elog(DEBUG, "edge_failure(1): no edge found via random decision and total_edges == 4"); } - else /* random decision of the gene with remaining edges */ + else +/* random decision of the gene with remaining edges */ - if (remaining_edges != 0) { + if (remaining_edges != 0) + { - rand_decision = (int) geqo_randint(remaining_edges-1, 0); + rand_decision = (int) geqo_randint(remaining_edges - 1, 0); - for (i=1; i<=num_gene; i++) { + for (i = 1; i <= num_gene; i++) + { - if ((Gene) i != fail_gene && - edge_table[i].unused_edges != -1) { + if ((Gene) i != fail_gene && + edge_table[i].unused_edges != -1) + { remaining_edges--; - if (rand_decision == remaining_edges) return (i); - } + if (rand_decision == remaining_edges) + return (i); } - - elog(DEBUG,"edge_failure(2): no edge found via random decision and remainig edges"); } - /* edge table seems to be empty; this happens sometimes on - the last point due to the fact that the first point is - removed from the table even though only one of its edges - has been determined */ + elog(DEBUG, "edge_failure(2): no edge found via random decision and remainig edges"); + } - else { /* occurs only at the last point in the tour; - simply look for the point which is not yet used */ + /* + * edge table seems to be empty; this happens sometimes on the last + * point due to the fact that the first point is removed from the + * table even though only one of its edges has been determined + */ - for (i=1; i<=num_gene; i++) - if (edge_table[i].unused_edges >= 0) - return ((Gene) i); - - elog(DEBUG,"edge_failure(3): no edge found via looking for the last ununsed point"); + else + { /* occurs only at the last point in the + * tour; simply look for the point which + * is not yet used */ + + for (i = 1; i <= num_gene; i++) + if (edge_table[i].unused_edges >= 0) + return ((Gene) i); + + elog(DEBUG, "edge_failure(3): no edge found via looking for the last ununsed point"); } /* ... should never be reached */ - elog(WARN,"edge_failure: no edge detected"); - return 0; /* to keep the compiler quiet */ + elog(WARN, "edge_failure: no edge detected"); + return 0; /* to keep the compiler quiet */ } - diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c index 7ec449f2e94..ba34d8f3e02 100644 --- a/src/backend/optimizer/geqo/geqo_eval.c +++ b/src/backend/optimizer/geqo/geqo_eval.c @@ -1,20 +1,20 @@ /*------------------------------------------------------------------------ * * geqo_eval.c-- - * Routines to evaluate query trees + * Routines to evaluate query trees * * Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_eval.c,v 1.12 1997/08/12 22:53:07 momjian Exp $ + * $Id: geqo_eval.c,v 1.13 1997/09/07 04:43:06 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ @@ -22,13 +22,13 @@ #include <math.h> #ifdef HAVE_LIMITS_H -# include <limits.h> -# ifndef MAXINT -# define MAXINT INT_MAX -# endif +#include <limits.h> +#ifndef MAXINT +#define MAXINT INT_MAX +#endif #else -# include <values.h> -#endif +#include <values.h> +#endif #include "nodes/pg_list.h" #include "nodes/relation.h" @@ -50,548 +50,598 @@ #include "optimizer/geqo_paths.h" -static List *gimme_clause_joins(Query *root, Rel *outer_rel, Rel *inner_rel); -static Rel *gimme_clauseless_join(Rel *outer_rel, Rel *inner_rel); -static Rel *init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo); -static List *new_join_tlist(List *tlist, List *other_relids, int first_resdomno); -static List *new_joininfo_list(List *joininfo_list, List *join_relids); -static void geqo_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel); -static Rel *geqo_nth(int stop, List *rels); +static List *gimme_clause_joins(Query * root, Rel * outer_rel, Rel * inner_rel); +static Rel *gimme_clauseless_join(Rel * outer_rel, Rel * inner_rel); +static Rel *init_join_rel(Rel * outer_rel, Rel * inner_rel, JInfo * joininfo); +static List *new_join_tlist(List * tlist, List * other_relids, int first_resdomno); +static List *new_joininfo_list(List * joininfo_list, List * join_relids); +static void geqo_joinrel_size(Rel * joinrel, Rel * outer_rel, Rel * inner_rel); +static Rel *geqo_nth(int stop, List * rels); -/* +/* * geqo_eval-- - * + * * Returns cost of a query tree as an individual of the population. */ Cost -geqo_eval (Query *root, Gene *tour, int num_gene) +geqo_eval(Query * root, Gene * tour, int num_gene) { - Rel *joinrel; - Cost fitness; - List *temp; + Rel *joinrel; + Cost fitness; + List *temp; /* remember root->join_relation_list_ ... */ /* because root->join_relation_list_ will be changed during the following */ - temp = listCopy(root->join_relation_list_); + temp = listCopy(root->join_relation_list_); /* joinrel is readily processed query tree -- left-sided ! */ - joinrel = gimme_tree(root, tour, 0, num_gene, NULL); + joinrel = gimme_tree(root, tour, 0, num_gene, NULL); /* compute fitness */ - fitness = (Cost) joinrel->cheapestpath->path_cost; + fitness = (Cost) joinrel->cheapestpath->path_cost; - root->join_relation_list_ = listCopy(temp); + root->join_relation_list_ = listCopy(temp); - pfree(joinrel); - freeList(temp); + pfree(joinrel); + freeList(temp); - return(fitness); + return (fitness); } -/* +/* * gimme-tree -- - * this program presumes that only LEFT-SIDED TREES are considered! - * + * this program presumes that only LEFT-SIDED TREES are considered! + * * 'outer_rel' is the preceeding join - * + * * Returns a new join relation incorporating all joins in a left-sided tree. */ -Rel * -gimme_tree (Query *root, Gene *tour, int rel_count, int num_gene, Rel *outer_rel) +Rel * +gimme_tree(Query * root, Gene * tour, int rel_count, int num_gene, Rel * outer_rel) { - Rel *inner_rel; /* current relation */ - int base_rel_index; + Rel *inner_rel; /* current relation */ + int base_rel_index; - List *new_rels = NIL; - Rel *new_rel = NULL; + List *new_rels = NIL; + Rel *new_rel = NULL; - if (rel_count < num_gene ) { /* tree not yet finished */ + if (rel_count < num_gene) + { /* tree not yet finished */ - /* tour[0] = 3; tour[1] = 1; tour[2] = 2 */ - base_rel_index = (int) tour[rel_count]; + /* tour[0] = 3; tour[1] = 1; tour[2] = 2 */ + base_rel_index = (int) tour[rel_count]; - inner_rel = (Rel *) geqo_nth(base_rel_index,root->base_relation_list_); + inner_rel = (Rel *) geqo_nth(base_rel_index, root->base_relation_list_); - if (rel_count == 0) { /* processing first join with base_rel_index = (int) tour[0] */ - rel_count++; - return gimme_tree(root, tour, rel_count, num_gene, inner_rel); + if (rel_count == 0) + { /* processing first join with + * base_rel_index = (int) tour[0] */ + rel_count++; + return gimme_tree(root, tour, rel_count, num_gene, inner_rel); } - else { /* tree main part */ - - if(!(new_rels = gimme_clause_joins(root, outer_rel,inner_rel))) { - if (BushyPlanFlag) { - new_rels = lcons(gimme_clauseless_join(outer_rel,outer_rel),NIL); /* ??? MAU */ + else + { /* tree main part */ + + if (!(new_rels = gimme_clause_joins(root, outer_rel, inner_rel))) + { + if (BushyPlanFlag) + { + new_rels = lcons(gimme_clauseless_join(outer_rel, outer_rel), NIL); /* ??? MAU */ } - else { - new_rels = lcons(gimme_clauseless_join(outer_rel,inner_rel),NIL); + else + { + new_rels = lcons(gimme_clauseless_join(outer_rel, inner_rel), NIL); } } - /* process new_rel->pathlist */ - find_all_join_paths(root, new_rels); + /* process new_rel->pathlist */ + find_all_join_paths(root, new_rels); - /* prune new_rels */ - /* MAU: is this necessary? */ - /* what's the matter if more than one new rel is left till now? */ - /* joinrels in newrels with different ordering of relids are not possible */ - if (length(new_rels) > 1) new_rels = geqo_prune_rels(new_rels); + /* prune new_rels */ + /* MAU: is this necessary? */ - if (length(new_rels) > 1) { /* should never be reached ... */ - elog(DEBUG,"gimme_tree: still %d relations left", length(new_rels)); + /* + * what's the matter if more than one new rel is left till + * now? + */ + + /* + * joinrels in newrels with different ordering of relids are + * not possible + */ + if (length(new_rels) > 1) + new_rels = geqo_prune_rels(new_rels); + + if (length(new_rels) > 1) + { /* should never be reached ... */ + elog(DEBUG, "gimme_tree: still %d relations left", length(new_rels)); } - /* get essential new relation */ - new_rel = (Rel *) lfirst(new_rels); - rel_count++; + /* get essential new relation */ + new_rel = (Rel *) lfirst(new_rels); + rel_count++; - /* process new_rel->cheapestpath, new_rel->unorderedpath */ - geqo_rel_paths(new_rel); + /* process new_rel->cheapestpath, new_rel->unorderedpath */ + geqo_rel_paths(new_rel); - /* processing of other new_rel attributes */ - if ( new_rel->size <= 0 ) - new_rel->size = compute_rel_size(new_rel); - new_rel->width = compute_rel_width(new_rel); + /* processing of other new_rel attributes */ + if (new_rel->size <= 0) + new_rel->size = compute_rel_size(new_rel); + new_rel->width = compute_rel_width(new_rel); - root->join_relation_list_ = lcons(new_rel, NIL); + root->join_relation_list_ = lcons(new_rel, NIL); - return gimme_tree(root, tour, rel_count, num_gene, new_rel); + return gimme_tree(root, tour, rel_count, num_gene, new_rel); } } - return (outer_rel); /* tree finished ... */ + return (outer_rel); /* tree finished ... */ } -/* +/* * gimme-clause-joins-- * * 'outer-rel' is the relation entry for the outer relation * 'inner-rel' is the relation entry for the inner relation - * + * * Returns a list of new join relations. */ -static List * -gimme_clause_joins(Query *root, Rel *outer_rel, Rel *inner_rel) +static List * +gimme_clause_joins(Query * root, Rel * outer_rel, Rel * inner_rel) { - List *join_list = NIL; - List *i = NIL; - List *joininfo_list = (List *) outer_rel->joininfo; - - foreach (i, joininfo_list) { - JInfo *joininfo = (JInfo*)lfirst(i); - Rel *rel = NULL; - - if(!joininfo->inactive) { - List *other_rels = (List *)joininfo->otherrels; - - if(other_rels != NIL) { - if( (length(other_rels) == 1) ) { - - if( same(other_rels, inner_rel->relids) ) { /* look if inner_rel is it...*/ - rel = init_join_rel(outer_rel, inner_rel, joininfo); + List *join_list = NIL; + List *i = NIL; + List *joininfo_list = (List *) outer_rel->joininfo; + + foreach(i, joininfo_list) + { + JInfo *joininfo = (JInfo *) lfirst(i); + Rel *rel = NULL; + + if (!joininfo->inactive) + { + List *other_rels = (List *) joininfo->otherrels; + + if (other_rels != NIL) + { + if ((length(other_rels) == 1)) + { + + if (same(other_rels, inner_rel->relids)) + { /* look if inner_rel is it... */ + rel = init_join_rel(outer_rel, inner_rel, joininfo); } } - else if (BushyPlanFlag) { /* ?!? MAU */ - rel = init_join_rel(outer_rel, get_join_rel(root, other_rels), joininfo); - } - else { - rel = NULL; - } + else if (BushyPlanFlag) + { /* ?!? MAU */ + rel = init_join_rel(outer_rel, get_join_rel(root, other_rels), joininfo); + } + else + { + rel = NULL; + } - if (rel != NULL) - join_list = lappend(join_list, rel); + if (rel != NULL) + join_list = lappend(join_list, rel); } } } - return(join_list); + return (join_list); } -/* +/* * gimme-clauseless-join-- - * Given an outer relation 'outer-rel' and an inner relation - * 'inner-rel', create a join relation between 'outer-rel' and 'inner-rel' - * + * Given an outer relation 'outer-rel' and an inner relation + * 'inner-rel', create a join relation between 'outer-rel' and 'inner-rel' + * * Returns a new join relation. */ -static Rel * -gimme_clauseless_join(Rel *outer_rel, Rel *inner_rel) +static Rel * +gimme_clauseless_join(Rel * outer_rel, Rel * inner_rel) { - return(init_join_rel(outer_rel, inner_rel, (JInfo*)NULL)); + return (init_join_rel(outer_rel, inner_rel, (JInfo *) NULL)); } -/* +/* * init-join-rel-- - * Creates and initializes a new join relation. - * + * Creates and initializes a new join relation. + * * 'outer-rel' and 'inner-rel' are relation nodes for the relations to be - * joined + * joined * 'joininfo' is the joininfo node(join clause) containing both - * 'outer-rel' and 'inner-rel', if any exists - * + * 'outer-rel' and 'inner-rel', if any exists + * * Returns the new join relation node. */ -static Rel * -init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo) +static Rel * +init_join_rel(Rel * outer_rel, Rel * inner_rel, JInfo * joininfo) { - Rel *joinrel = makeNode(Rel); - List *joinrel_joininfo_list = NIL; - List *new_outer_tlist; - List *new_inner_tlist; - - /* - * Create a new tlist by removing irrelevant elements from both - * tlists of the outer and inner join relations and then merging - * the results together. - */ - new_outer_tlist = - new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */ - inner_rel->relids, 1); - new_inner_tlist = - new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */ - outer_rel->relids, - length(new_outer_tlist) + 1); - - joinrel->relids = NIL; - joinrel->indexed = false; - joinrel->pages = 0; - joinrel->tuples = 0; - joinrel->width = 0; -/* joinrel->targetlist = NIL;*/ - joinrel->pathlist = NIL; - joinrel->unorderedpath = (Path *)NULL; - joinrel->cheapestpath = (Path *)NULL; - joinrel->pruneable = true; - joinrel->classlist = NULL; - joinrel->relam = InvalidOid; - joinrel->ordering = NULL; - joinrel->clauseinfo = NIL; - joinrel->joininfo = NULL; - joinrel->innerjoin = NIL; - joinrel->superrels = NIL; - - joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL)); - - new_outer_tlist = nconc(new_outer_tlist,new_inner_tlist); - joinrel->targetlist = new_outer_tlist; - - if (joininfo) { - joinrel->clauseinfo = joininfo->jinfoclauseinfo; - if (BushyPlanFlag) joininfo->inactive = true; - } - - joinrel_joininfo_list = - new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo), - intAppend(outer_rel->relids, inner_rel->relids)); - - joinrel->joininfo = joinrel_joininfo_list; - - geqo_joinrel_size(joinrel, outer_rel, inner_rel); - - return(joinrel); + Rel *joinrel = makeNode(Rel); + List *joinrel_joininfo_list = NIL; + List *new_outer_tlist; + List *new_inner_tlist; + + /* + * Create a new tlist by removing irrelevant elements from both tlists + * of the outer and inner join relations and then merging the results + * together. + */ + new_outer_tlist = + new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */ + inner_rel->relids, 1); + new_inner_tlist = + new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */ + outer_rel->relids, + length(new_outer_tlist) + 1); + + joinrel->relids = NIL; + joinrel->indexed = false; + joinrel->pages = 0; + joinrel->tuples = 0; + joinrel->width = 0; +/* joinrel->targetlist = NIL;*/ + joinrel->pathlist = NIL; + joinrel->unorderedpath = (Path *) NULL; + joinrel->cheapestpath = (Path *) NULL; + joinrel->pruneable = true; + joinrel->classlist = NULL; + joinrel->relam = InvalidOid; + joinrel->ordering = NULL; + joinrel->clauseinfo = NIL; + joinrel->joininfo = NULL; + joinrel->innerjoin = NIL; + joinrel->superrels = NIL; + + joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL)); + + new_outer_tlist = nconc(new_outer_tlist, new_inner_tlist); + joinrel->targetlist = new_outer_tlist; + + if (joininfo) + { + joinrel->clauseinfo = joininfo->jinfoclauseinfo; + if (BushyPlanFlag) + joininfo->inactive = true; + } + + joinrel_joininfo_list = + new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo), + intAppend(outer_rel->relids, inner_rel->relids)); + + joinrel->joininfo = joinrel_joininfo_list; + + geqo_joinrel_size(joinrel, outer_rel, inner_rel); + + return (joinrel); } -/* +/* * new-join-tlist-- - * Builds a join relations's target list by keeping those elements that - * will be in the final target list and any other elements that are still - * needed for future joins. For a target list entry to still be needed - * for future joins, its 'joinlist' field must not be empty after removal - * of all relids in 'other-relids'. - * + * Builds a join relations's target list by keeping those elements that + * will be in the final target list and any other elements that are still + * needed for future joins. For a target list entry to still be needed + * for future joins, its 'joinlist' field must not be empty after removal + * of all relids in 'other-relids'. + * * 'tlist' is the target list of one of the join relations * 'other-relids' is a list of relids contained within the other - * join relation + * join relation * 'first-resdomno' is the resdom number to use for the first created - * target list entry - * + * target list entry + * * Returns the new target list. */ -static List * -new_join_tlist(List *tlist, - List *other_relids, - int first_resdomno) +static List * +new_join_tlist(List * tlist, + List * other_relids, + int first_resdomno) { - int resdomno = first_resdomno - 1; - TargetEntry *xtl = NULL; - List *temp_node = NIL; - List *t_list = NIL; - List *i = NIL; - List *join_list = NIL; - bool in_final_tlist =false; - - - foreach(i,tlist) { - xtl= lfirst(i); - in_final_tlist = (join_list==NIL); - if( in_final_tlist) { - resdomno += 1; - temp_node = - lcons(create_tl_element(get_expr(xtl), - resdomno), - NIL); - t_list = nconc(t_list,temp_node); - } - } - - return(t_list); + int resdomno = first_resdomno - 1; + TargetEntry *xtl = NULL; + List *temp_node = NIL; + List *t_list = NIL; + List *i = NIL; + List *join_list = NIL; + bool in_final_tlist = false; + + + foreach(i, tlist) + { + xtl = lfirst(i); + in_final_tlist = (join_list == NIL); + if (in_final_tlist) + { + resdomno += 1; + temp_node = + lcons(create_tl_element(get_expr(xtl), + resdomno), + NIL); + t_list = nconc(t_list, temp_node); + } + } + + return (t_list); } -/* +/* * new-joininfo-list-- - * Builds a join relation's joininfo list by checking for join clauses - * which still need to used in future joins involving this relation. A - * join clause is still needed if there are still relations in the clause - * not contained in the list of relations comprising this join relation. - * New joininfo nodes are only created and added to - * 'current-joininfo-list' if a node for a particular join hasn't already - * been created. + * Builds a join relation's joininfo list by checking for join clauses + * which still need to used in future joins involving this relation. A + * join clause is still needed if there are still relations in the clause + * not contained in the list of relations comprising this join relation. + * New joininfo nodes are only created and added to + * 'current-joininfo-list' if a node for a particular join hasn't already + * been created. * - * 'current-joininfo-list' contains a list of those joininfo nodes that - * have already been built + * 'current-joininfo-list' contains a list of those joininfo nodes that + * have already been built * 'joininfo-list' is the list of join clauses involving this relation - * 'join-relids' is a list of relids corresponding to the relations - * currently being joined - * + * 'join-relids' is a list of relids corresponding to the relations + * currently being joined + * * Returns a list of joininfo nodes, new and old. */ -static List * -new_joininfo_list(List *joininfo_list, List *join_relids) +static List * +new_joininfo_list(List * joininfo_list, List * join_relids) { - List *current_joininfo_list = NIL; - List *new_otherrels = NIL; - JInfo *other_joininfo = (JInfo*)NULL; - List *xjoininfo = NIL; - - foreach (xjoininfo, joininfo_list) { - List *or; - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - - new_otherrels = joininfo->otherrels; - foreach (or, new_otherrels) - { - if ( intMember (lfirsti(or), join_relids) ) - new_otherrels = lremove ((void*)lfirst(or), new_otherrels); - } - joininfo->otherrels = new_otherrels; - if ( new_otherrels != NIL ) + List *current_joininfo_list = NIL; + List *new_otherrels = NIL; + JInfo *other_joininfo = (JInfo *) NULL; + List *xjoininfo = NIL; + + foreach(xjoininfo, joininfo_list) { - other_joininfo = joininfo_member(new_otherrels, - current_joininfo_list); - if(other_joininfo) { - other_joininfo->jinfoclauseinfo = - (List*)LispUnion(joininfo->jinfoclauseinfo, - other_joininfo->jinfoclauseinfo); - }else { - other_joininfo = makeNode(JInfo); - - other_joininfo->otherrels = - joininfo->otherrels; - other_joininfo->jinfoclauseinfo = - joininfo->jinfoclauseinfo; - other_joininfo->mergesortable = - joininfo->mergesortable; - other_joininfo->hashjoinable = - joininfo->hashjoinable; - other_joininfo->inactive = false; - - current_joininfo_list = lcons(other_joininfo, - current_joininfo_list); - } + List *or; + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + new_otherrels = joininfo->otherrels; + foreach(or, new_otherrels) + { + if (intMember(lfirsti(or), join_relids)) + new_otherrels = lremove((void *) lfirst(or), new_otherrels); + } + joininfo->otherrels = new_otherrels; + if (new_otherrels != NIL) + { + other_joininfo = joininfo_member(new_otherrels, + current_joininfo_list); + if (other_joininfo) + { + other_joininfo->jinfoclauseinfo = + (List *) LispUnion(joininfo->jinfoclauseinfo, + other_joininfo->jinfoclauseinfo); + } + else + { + other_joininfo = makeNode(JInfo); + + other_joininfo->otherrels = + joininfo->otherrels; + other_joininfo->jinfoclauseinfo = + joininfo->jinfoclauseinfo; + other_joininfo->mergesortable = + joininfo->mergesortable; + other_joininfo->hashjoinable = + joininfo->hashjoinable; + other_joininfo->inactive = false; + + current_joininfo_list = lcons(other_joininfo, + current_joininfo_list); + } + } } - } - return(current_joininfo_list); + return (current_joininfo_list); } #ifdef NOTUSED /* * add-new-joininfos-- - * For each new join relation, create new joininfos that - * use the join relation as inner relation, and add - * the new joininfos to those rel nodes that still - * have joins with the join relation. + * For each new join relation, create new joininfos that + * use the join relation as inner relation, and add + * the new joininfos to those rel nodes that still + * have joins with the join relation. * * 'joinrels' is a list of join relations. * * Modifies the joininfo field of appropriate rel nodes. */ static void -geqo_add_new_joininfos(Query *root, List *joinrels, List *outerrels) +geqo_add_new_joininfos(Query * root, List * joinrels, List * outerrels) { - List *xjoinrel = NIL; - List *xrelid = NIL; - List *xrel = NIL; - List *xjoininfo = NIL; - - Rel *rel; - List *relids; - - List *super_rels; - List *xsuper_rel = NIL; - JInfo *new_joininfo; - - foreach(xjoinrel, joinrels) { - Rel *joinrel = (Rel *)lfirst(xjoinrel); - foreach(xrelid, joinrel->relids) { - /* length(joinrel->relids) should always be greater that 1, because of *JOIN* */ - /* ! BUG BUG ! - Relid relid = (Relid)lfirst(xrelid); - Rel *rel = get_join_rel(root, relid); - */ - - /* - if ( (root->join_relation_list_) != NIL ) { - rel = get_join_rel(root, xrelid); - } - else { - rel = get_base_rel(root, lfirsti(xrelid)); - } - */ - - /* NOTE: STILL BUGGY FOR CLAUSE-JOINS: */ - /* - relids = lconsi(lfirsti(xrelid), NIL); - rel = rel_member(relids, outerrels); - */ + List *xjoinrel = NIL; + List *xrelid = NIL; + List *xrel = NIL; + List *xjoininfo = NIL; + + Rel *rel; + List *relids; + + List *super_rels; + List *xsuper_rel = NIL; + JInfo *new_joininfo; + + foreach(xjoinrel, joinrels) + { + Rel *joinrel = (Rel *) lfirst(xjoinrel); + + foreach(xrelid, joinrel->relids) + { + + /* + * length(joinrel->relids) should always be greater that 1, + * because of *JOIN* + */ + + /* + * ! BUG BUG ! Relid relid = (Relid)lfirst(xrelid); Rel *rel = + * get_join_rel(root, relid); + */ - relids = lconsi(lfirsti(xrelid), NIL); - rel = rel_member(relids, root->base_relation_list_); + /* + * if ( (root->join_relation_list_) != NIL ) { rel = + * get_join_rel(root, xrelid); } else { rel = + * get_base_rel(root, lfirsti(xrelid)); } + */ - add_superrels(rel,joinrel); + /* NOTE: STILL BUGGY FOR CLAUSE-JOINS: */ + + /* + * relids = lconsi(lfirsti(xrelid), NIL); rel = + * rel_member(relids, outerrels); + */ + + relids = lconsi(lfirsti(xrelid), NIL); + rel = rel_member(relids, root->base_relation_list_); + + add_superrels(rel, joinrel); + } } - } - foreach(xjoinrel, joinrels) { - Rel *joinrel = (Rel *)lfirst(xjoinrel); - - foreach(xjoininfo, joinrel->joininfo) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - List *other_rels = joininfo->otherrels; - List *clause_info = joininfo->jinfoclauseinfo; - bool mergesortable = joininfo->mergesortable; - bool hashjoinable = joininfo->hashjoinable; - - foreach(xrelid, other_rels) { - /* ! BUG BUG ! - Relid relid = (Relid)lfirst(xrelid); - Rel *rel = get_join_rel(root, relid); - */ - - /* - if ( (root->join_relation_list_) != NIL ) { - rel = get_join_rel(root, xrelid); - } - else { - rel = get_base_rel(root, lfirsti(xrelid)); - } - */ - - /* NOTE: STILL BUGGY FOR CLAUSE-JOINS: */ - /* - relids = lconsi(lfirsti(xrelid), NIL); - rel = rel_member(relids, outerrels); - */ - - relids = lconsi(lfirsti(xrelid), NIL); - rel = rel_member(relids, root->base_relation_list_); - - super_rels = rel->superrels; - new_joininfo = makeNode(JInfo); - - new_joininfo->otherrels = joinrel->relids; - new_joininfo->jinfoclauseinfo = clause_info; - new_joininfo->mergesortable = mergesortable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->inactive = false; - rel->joininfo = - lappend(rel->joininfo, new_joininfo); - - foreach(xsuper_rel, super_rels) { - Rel *super_rel = (Rel *)lfirst(xsuper_rel); - - if( nonoverlap_rels(super_rel,joinrel) ) { - List *new_relids = super_rel->relids; - JInfo *other_joininfo = - joininfo_member(new_relids, - joinrel->joininfo); - - if (other_joininfo) { - other_joininfo->jinfoclauseinfo = - (List*)LispUnion(clause_info, - other_joininfo->jinfoclauseinfo); - } else { - JInfo *new_joininfo = makeNode(JInfo); - - new_joininfo->otherrels = new_relids; - new_joininfo->jinfoclauseinfo = clause_info; - new_joininfo->mergesortable = mergesortable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->inactive = false; - joinrel->joininfo = - lappend(joinrel->joininfo, - new_joininfo); + foreach(xjoinrel, joinrels) + { + Rel *joinrel = (Rel *) lfirst(xjoinrel); + + foreach(xjoininfo, joinrel->joininfo) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + List *other_rels = joininfo->otherrels; + List *clause_info = joininfo->jinfoclauseinfo; + bool mergesortable = joininfo->mergesortable; + bool hashjoinable = joininfo->hashjoinable; + + foreach(xrelid, other_rels) + { + + /* + * ! BUG BUG ! Relid relid = (Relid)lfirst(xrelid); Rel + * *rel = get_join_rel(root, relid); + */ + + /* + * if ( (root->join_relation_list_) != NIL ) { rel = + * get_join_rel(root, xrelid); } else { rel = + * get_base_rel(root, lfirsti(xrelid)); } + */ + + /* NOTE: STILL BUGGY FOR CLAUSE-JOINS: */ + + /* + * relids = lconsi(lfirsti(xrelid), NIL); rel = + * rel_member(relids, outerrels); + */ + + relids = lconsi(lfirsti(xrelid), NIL); + rel = rel_member(relids, root->base_relation_list_); + + super_rels = rel->superrels; + new_joininfo = makeNode(JInfo); + + new_joininfo->otherrels = joinrel->relids; + new_joininfo->jinfoclauseinfo = clause_info; + new_joininfo->mergesortable = mergesortable; + new_joininfo->hashjoinable = hashjoinable; + new_joininfo->inactive = false; + rel->joininfo = + lappend(rel->joininfo, new_joininfo); + + foreach(xsuper_rel, super_rels) + { + Rel *super_rel = (Rel *) lfirst(xsuper_rel); + + if (nonoverlap_rels(super_rel, joinrel)) + { + List *new_relids = super_rel->relids; + JInfo *other_joininfo = + joininfo_member(new_relids, + joinrel->joininfo); + + if (other_joininfo) + { + other_joininfo->jinfoclauseinfo = + (List *) LispUnion(clause_info, + other_joininfo->jinfoclauseinfo); + } + else + { + JInfo *new_joininfo = makeNode(JInfo); + + new_joininfo->otherrels = new_relids; + new_joininfo->jinfoclauseinfo = clause_info; + new_joininfo->mergesortable = mergesortable; + new_joininfo->hashjoinable = hashjoinable; + new_joininfo->inactive = false; + joinrel->joininfo = + lappend(joinrel->joininfo, + new_joininfo); + } + } + } } - } } - } } - } - foreach(xrel, outerrels) { - rel = (Rel *)lfirst(xrel); - rel->superrels = NIL; - } + foreach(xrel, outerrels) + { + rel = (Rel *) lfirst(xrel); + rel->superrels = NIL; + } } /* * final-join-rels-- - * Find the join relation that includes all the original - * relations, i.e. the final join result. + * Find the join relation that includes all the original + * relations, i.e. the final join result. * * 'join-rel-list' is a list of join relations. * * Returns the list of final join relations. */ -static List * -geqo_final_join_rels(List *join_rel_list) +static List * +geqo_final_join_rels(List * join_rel_list) { - List *xrel = NIL; - List *temp = NIL; - List *t_list = NIL; - - /* - * find the relations that has no further joins, - * i.e., its joininfos all have otherrels nil. - */ - foreach(xrel,join_rel_list) { - Rel *rel = (Rel *)lfirst(xrel); - List *xjoininfo = NIL; - bool final = true; - - foreach (xjoininfo, rel->joininfo) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - - if (joininfo->otherrels != NIL) { - final = false; - break; - } - } - if (final) { - temp = lcons(rel, NIL); - t_list = nconc(t_list, temp); + List *xrel = NIL; + List *temp = NIL; + List *t_list = NIL; + + /* + * find the relations that has no further joins, i.e., its joininfos + * all have otherrels nil. + */ + foreach(xrel, join_rel_list) + { + Rel *rel = (Rel *) lfirst(xrel); + List *xjoininfo = NIL; + bool final = true; + + foreach(xjoininfo, rel->joininfo) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + if (joininfo->otherrels != NIL) + { + final = false; + break; + } + } + if (final) + { + temp = lcons(rel, NIL); + t_list = nconc(t_list, temp); + } } - } - return(t_list); + return (t_list); } /* * add_superrels-- - * add rel to the temporary property list superrels. + * add rel to the temporary property list superrels. * * 'rel' a rel node * 'super-rel' rel node of a join relation that includes rel @@ -599,65 +649,72 @@ geqo_final_join_rels(List *join_rel_list) * Modifies the superrels field of rel */ static void -add_superrels(Rel *rel, Rel *super_rel) +add_superrels(Rel * rel, Rel * super_rel) { - rel->superrels = lappend(rel->superrels, super_rel); + rel->superrels = lappend(rel->superrels, super_rel); } /* * nonoverlap-rels-- - * test if two join relations overlap, i.e., includes the same - * relation. + * test if two join relations overlap, i.e., includes the same + * relation. * * 'rel1' and 'rel2' are two join relations * * Returns non-nil if rel1 and rel2 do not overlap. */ -static bool -nonoverlap_rels(Rel *rel1, Rel *rel2) +static bool +nonoverlap_rels(Rel * rel1, Rel * rel2) { - return(nonoverlap_sets(rel1->relids, rel2->relids)); + return (nonoverlap_sets(rel1->relids, rel2->relids)); } -static bool -nonoverlap_sets(List *s1, List *s2) +static bool +nonoverlap_sets(List * s1, List * s2) { - List *x = NIL; - - foreach(x,s1) { - int e = lfirsti(x); - if(intMember(e,s2)) - return(false); - } - return(true); + List *x = NIL; + + foreach(x, s1) + { + int e = lfirsti(x); + + if (intMember(e, s2)) + return (false); + } + return (true); } -#endif /* NOTUSED */ + +#endif /* NOTUSED */ /* * geqo_joinrel_size-- - * compute estimate for join relation tuples, even for - * long join queries; so get logarithm of size when MAXINT overflow; + * compute estimate for join relation tuples, even for + * long join queries; so get logarithm of size when MAXINT overflow; */ static void -geqo_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel) +geqo_joinrel_size(Rel * joinrel, Rel * outer_rel, Rel * inner_rel) { - Cost temp; - int ntuples; - + Cost temp; + int ntuples; + temp = (Cost) inner_rel->tuples * (Cost) outer_rel->tuples; /* cartesian product */ - if (joinrel->clauseinfo) { + if (joinrel->clauseinfo) + { temp = temp * product_selec(joinrel->clauseinfo); - } - - if (temp >= (MAXINT -1)) { - ntuples = ceil( geqo_log((double)temp, (double) GEQO_LOG_BASE) ); - } - else { - ntuples = ceil((double)temp); - } + } - if (ntuples < 1) ntuples = 1; /* make the best case 1 instead of 0 */ + if (temp >= (MAXINT - 1)) + { + ntuples = ceil(geqo_log((double) temp, (double) GEQO_LOG_BASE)); + } + else + { + ntuples = ceil((double) temp); + } + + if (ntuples < 1) + ntuples = 1; /* make the best case 1 instead of 0 */ joinrel->tuples = ntuples; } @@ -665,19 +722,21 @@ geqo_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel) double geqo_log(double x, double b) { - return(log(x)/log(b)); + return (log(x) / log(b)); } -static Rel * -geqo_nth(int stop, List *rels) +static Rel * +geqo_nth(int stop, List * rels) { - List *r; - int i=1; + List *r; + int i = 1; - foreach(r, rels) { - if (i == stop) return lfirst(r); + foreach(r, rels) + { + if (i == stop) + return lfirst(r); i++; - } - elog(WARN,"geqo_nth: Internal error - ran off end of list"); - return NULL; /* to keep compiler happy */ + } + elog(WARN, "geqo_nth: Internal error - ran off end of list"); + return NULL; /* to keep compiler happy */ } diff --git a/src/backend/optimizer/geqo/geqo_main.c b/src/backend/optimizer/geqo/geqo_main.c index 4b450002885..eab939c03e6 100644 --- a/src/backend/optimizer/geqo/geqo_main.c +++ b/src/backend/optimizer/geqo/geqo_main.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------ * * geqo_main.c-- - * solution of the query optimization problem - * by means of a Genetic Algorithm (GA) + * solution of the query optimization problem + * by means of a Genetic Algorithm (GA) * * Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_main.c,v 1.3 1997/03/14 16:02:51 scrappy Exp $ + * $Id: geqo_main.c,v 1.4 1997/09/07 04:43:09 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ @@ -48,228 +48,241 @@ /* define edge recombination crossover [ERX] per default */ #if !defined(ERX) && \ - !defined(PMX) && \ - !defined(CX) && \ - !defined(PX) && \ - !defined(OX1) && \ - !defined(OX2) + !defined(PMX) && \ + !defined(CX) && \ + !defined(PX) && \ + !defined(OX1) && \ + !defined(OX2) #define ERX #endif /* * geqo-- - * solution of the query optimization problem - * similar to a constrained Traveling Salesman Problem (TSP) + * solution of the query optimization problem + * similar to a constrained Traveling Salesman Problem (TSP) */ -Rel * -geqo(Query *root) +Rel * +geqo(Query * root) { - int generation; - Chromosome *momma; - Chromosome *daddy; - Chromosome *kid; + int generation; + Chromosome *momma; + Chromosome *daddy; + Chromosome *kid; #if defined(ERX) - Edge *edge_table; /* list of edges */ - int edge_failures=0; - float difference; -#endif + Edge *edge_table; /* list of edges */ + int edge_failures = 0; + float difference; + +#endif #if defined(CX) || defined(PX) || defined(OX1) || defined(OX2) - City *city_table; /* list of cities */ + City *city_table; /* list of cities */ + #endif #if defined(CX) - int cycle_diffs=0; - int mutations=0; + int cycle_diffs = 0; + int mutations = 0; + #endif - int number_of_rels; + int number_of_rels; - Pool *pool; - int pool_size, number_generations, status_interval; + Pool *pool; + int pool_size, + number_generations, + status_interval; - Gene *best_tour; - Rel *best_rel; -/* Plan *best_plan; */ + Gene *best_tour; + Rel *best_rel; + +/* Plan *best_plan; */ /* set tour size */ - number_of_rels = length(root->base_relation_list_); + number_of_rels = length(root->base_relation_list_); /* set GA parameters */ - geqo_params(number_of_rels) ; /* out of "$PGDATA/pg_geqo" file */ - pool_size = PoolSize; - number_generations = Generations; - status_interval = 10; + geqo_params(number_of_rels);/* out of "$PGDATA/pg_geqo" file */ + pool_size = PoolSize; + number_generations = Generations; + status_interval = 10; /* seed random number generator */ - srandom(RandomSeed); + srandom(RandomSeed); /* allocate genetic pool memory */ - pool = alloc_pool(pool_size, number_of_rels); + pool = alloc_pool(pool_size, number_of_rels); /* random initialization of the pool */ - random_init_pool (root, pool, 0, pool->size); + random_init_pool(root, pool, 0, pool->size); /* sort the pool according to cheapest path as fitness */ - sort_pool (pool); /* we have to do it only one time, since all kids replace the worst individuals in future (-> geqo_pool.c:spread_chromo ) */ + sort_pool(pool); /* we have to do it only one time, since + * all kids replace the worst individuals + * in future (-> geqo_pool.c:spread_chromo + * ) */ /* allocate chromosome momma and daddy memory */ - momma = alloc_chromo(pool->string_length); - daddy = alloc_chromo(pool->string_length); + momma = alloc_chromo(pool->string_length); + daddy = alloc_chromo(pool->string_length); #if defined (ERX) - elog(DEBUG,"geqo_main: using edge recombination crossover [ERX]"); + elog(DEBUG, "geqo_main: using edge recombination crossover [ERX]"); /* allocate edge table memory */ - edge_table = alloc_edge_table(pool->string_length); + edge_table = alloc_edge_table(pool->string_length); #elif defined(PMX) - elog(DEBUG,"geqo_main: using partially matched crossover [PMX]"); + elog(DEBUG, "geqo_main: using partially matched crossover [PMX]"); /* allocate chromosome kid memory */ - kid = alloc_chromo(pool->string_length); + kid = alloc_chromo(pool->string_length); #elif defined(CX) - elog(DEBUG,"geqo_main: using cycle crossover [CX]"); + elog(DEBUG, "geqo_main: using cycle crossover [CX]"); /* allocate city table memory */ - kid = alloc_chromo(pool->string_length); - city_table = alloc_city_table(pool->string_length); + kid = alloc_chromo(pool->string_length); + city_table = alloc_city_table(pool->string_length); #elif defined(PX) - elog(DEBUG,"geqo_main: using position crossover [PX]"); + elog(DEBUG, "geqo_main: using position crossover [PX]"); /* allocate city table memory */ - kid = alloc_chromo(pool->string_length); - city_table = alloc_city_table(pool->string_length); + kid = alloc_chromo(pool->string_length); + city_table = alloc_city_table(pool->string_length); #elif defined(OX1) - elog(DEBUG,"geqo_main: using order crossover [OX1]"); + elog(DEBUG, "geqo_main: using order crossover [OX1]"); /* allocate city table memory */ - kid = alloc_chromo(pool->string_length); - city_table = alloc_city_table(pool->string_length); + kid = alloc_chromo(pool->string_length); + city_table = alloc_city_table(pool->string_length); #elif defined(OX2) - elog(DEBUG,"geqo_main: using order crossover [OX2]"); + elog(DEBUG, "geqo_main: using order crossover [OX2]"); /* allocate city table memory */ - kid = alloc_chromo(pool->string_length); - city_table = alloc_city_table(pool->string_length); + kid = alloc_chromo(pool->string_length); + city_table = alloc_city_table(pool->string_length); #endif /* my pain main part: */ /* iterative optimization */ - for (generation = 0; generation < number_generations; generation++) { + for (generation = 0; generation < number_generations; generation++) + { - /* SELECTION */ - geqo_selection(momma, daddy, pool, SelectionBias); /* using linear bias function */ + /* SELECTION */ + geqo_selection(momma, daddy, pool, SelectionBias); /* using linear bias + * function */ #if defined (ERX) - /* EDGE RECOMBINATION CROSSOVER */ - difference = gimme_edge_table(momma->string, daddy->string, pool->string_length, edge_table); + /* EDGE RECOMBINATION CROSSOVER */ + difference = gimme_edge_table(momma->string, daddy->string, pool->string_length, edge_table); - /* let the kid grow in momma's womb (storage) for nine months ;-) */ - /* sleep(23328000) -- har har har */ - kid = momma; + /* let the kid grow in momma's womb (storage) for nine months ;-) */ + /* sleep(23328000) -- har har har */ + kid = momma; - /* are there any edge failures ? */ - edge_failures += gimme_tour(edge_table, kid->string, pool->string_length); + /* are there any edge failures ? */ + edge_failures += gimme_tour(edge_table, kid->string, pool->string_length); #elif defined(PMX) - /* PARTIALLY MATCHED CROSSOVER */ - pmx(momma->string, daddy->string, kid->string, pool->string_length); + /* PARTIALLY MATCHED CROSSOVER */ + pmx(momma->string, daddy->string, kid->string, pool->string_length); #elif defined(CX) - /* CYCLE CROSSOVER */ - cycle_diffs = - cx(momma->string, daddy->string, kid->string, pool->string_length, city_table); - /* mutate the child */ - if (cycle_diffs == 0) { - mutations++; - geqo_mutation (kid->string, pool->string_length); - } + /* CYCLE CROSSOVER */ + cycle_diffs = + cx(momma->string, daddy->string, kid->string, pool->string_length, city_table); + /* mutate the child */ + if (cycle_diffs == 0) + { + mutations++; + geqo_mutation(kid->string, pool->string_length); + } #elif defined(PX) - /* POSITION CROSSOVER */ - px(momma->string, daddy->string, kid->string, pool->string_length, city_table); + /* POSITION CROSSOVER */ + px(momma->string, daddy->string, kid->string, pool->string_length, city_table); #elif defined(OX1) - /* ORDER CROSSOVER */ - ox1(momma->string, daddy->string, kid->string, pool->string_length, city_table); + /* ORDER CROSSOVER */ + ox1(momma->string, daddy->string, kid->string, pool->string_length, city_table); #elif defined(OX2) - /* ORDER CROSSOVER */ - ox2(momma->string, daddy->string, kid->string, pool->string_length, city_table); + /* ORDER CROSSOVER */ + ox2(momma->string, daddy->string, kid->string, pool->string_length, city_table); #endif - /* EVALUATE FITNESS */ - kid->worth = geqo_eval (root, kid->string, pool->string_length); + /* EVALUATE FITNESS */ + kid->worth = geqo_eval(root, kid->string, pool->string_length); - /* push the kid into the wilderness of life according to its worth */ - spread_chromo (kid, pool); + /* push the kid into the wilderness of life according to its worth */ + spread_chromo(kid, pool); #ifdef GEQO_DEBUG - if (status_interval && !(generation % status_interval)) - print_gen (stdout, pool, generation); + if (status_interval && !(generation % status_interval)) + print_gen(stdout, pool, generation); #endif - } /* end of iterative optimization */ + } /* end of iterative optimization */ #if defined(ERX) && defined(GEQO_DEBUG) -if (edge_failures != 0) - fprintf (stdout, "\nFailures: %d Avg: %d\n", edge_failures, (int) generation/edge_failures); + if (edge_failures != 0) + fprintf(stdout, "\nFailures: %d Avg: %d\n", edge_failures, (int) generation / edge_failures); -else fprintf (stdout, "No edge failures detected.\n"); + else + fprintf(stdout, "No edge failures detected.\n"); #endif #if defined(CX) && defined(GEQO_DEBUG) -if (mutations != 0) - fprintf (stdout, "\nMutations: %d Generations: %d\n", mutations, generation); + if (mutations != 0) + fprintf(stdout, "\nMutations: %d Generations: %d\n", mutations, generation); -else fprintf (stdout, "No mutations processed.\n"); + else + fprintf(stdout, "No mutations processed.\n"); #endif - + #ifdef GEQO_DEBUG -fprintf (stdout, "\n"); -print_pool (stdout, pool, 0, pool_size-1); + fprintf(stdout, "\n"); + print_pool(stdout, pool, 0, pool_size - 1); #endif /* got the cheapest query tree processed by geqo; first element of the population indicates the best query tree */ -best_tour = (Gene *) pool->data[0].string; + best_tour = (Gene *) pool->data[0].string; /* root->join_relation_list_ will be modified during this ! */ -best_rel = (Rel *) gimme_tree(root, best_tour, 0, pool->string_length, NULL); + best_rel = (Rel *) gimme_tree(root, best_tour, 0, pool->string_length, NULL); /* DBG: show the query plan print_plan(best_plan, root); DBG */ /* ... free memory stuff */ -free_chromo(momma); -free_chromo(daddy); + free_chromo(momma); + free_chromo(daddy); #if defined (ERX) -free_edge_table(edge_table); + free_edge_table(edge_table); #elif defined(PMX) -free_chromo(kid); + free_chromo(kid); #elif defined(CX) -free_chromo(kid); -free_city_table(city_table); + free_chromo(kid); + free_city_table(city_table); #elif defined(PX) -free_chromo(kid); -free_city_table(city_table); + free_chromo(kid); + free_city_table(city_table); #elif defined(OX1) -free_chromo(kid); -free_city_table(city_table); + free_chromo(kid); + free_city_table(city_table); #elif defined(OX2) -free_chromo(kid); -free_city_table(city_table); + free_chromo(kid); + free_city_table(city_table); #endif -free_pool(pool); + free_pool(pool); -return(best_rel); + return (best_rel); } - diff --git a/src/backend/optimizer/geqo/geqo_misc.c b/src/backend/optimizer/geqo/geqo_misc.c index 48ae78bdcda..67e810d87ca 100644 --- a/src/backend/optimizer/geqo/geqo_misc.c +++ b/src/backend/optimizer/geqo/geqo_misc.c @@ -1,20 +1,20 @@ /*------------------------------------------------------------------------ * * geqo_misc.c-- - * misc. printout and debug stuff + * misc. printout and debug stuff * * Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_misc.c,v 1.2 1997/02/19 14:52:01 scrappy Exp $ + * $Id: geqo_misc.c,v 1.3 1997/09/07 04:43:10 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ @@ -41,91 +41,97 @@ #include "optimizer/geqo_recombination.h" #include "optimizer/geqo_misc.h" -static float avg_pool (Pool *pool); +static float avg_pool(Pool * pool); /* avg_pool-- * */ static float -avg_pool (Pool *pool) +avg_pool(Pool * pool) { - int i; - double cumulative = 0.0; - - if (pool->size==0) - elog(WARN,"avg_pool: pool_size of zero"); - - for (i=0; i<pool->size; i++) - cumulative = cumulative + pool->data[i].worth; - - return ((float) cumulative/pool->size); + int i; + double cumulative = 0.0; + + if (pool->size == 0) + elog(WARN, "avg_pool: pool_size of zero"); + + for (i = 0; i < pool->size; i++) + cumulative = cumulative + pool->data[i].worth; + + return ((float) cumulative / pool->size); } /* print_pool-- */ void -print_pool (FILE *fp, Pool *pool, int start, int stop) +print_pool(FILE * fp, Pool * pool, int start, int stop) { - int i, j; + int i, + j; - /* be extra careful that start and stop are valid inputs */ + /* be extra careful that start and stop are valid inputs */ - if (start < 0) start = 0; - if (stop > pool->size) stop = pool->size; + if (start < 0) + start = 0; + if (stop > pool->size) + stop = pool->size; - if (start+stop > pool->size) { - start = 0; - stop = pool->size; + if (start + stop > pool->size) + { + start = 0; + stop = pool->size; } - for (i=start; i<stop; i++) { - fprintf (fp, "%d)\t", i); - for (j=0; j<pool->string_length; j++) - fprintf (fp, "%d ", pool->data[i].string[j]); - fprintf (fp, "%f\n", pool->data[i].worth); + for (i = start; i < stop; i++) + { + fprintf(fp, "%d)\t", i); + for (j = 0; j < pool->string_length; j++) + fprintf(fp, "%d ", pool->data[i].string[j]); + fprintf(fp, "%f\n", pool->data[i].worth); } } /* print_gen-- * - * printout for chromosome: best, worst, mean, average + * printout for chromosome: best, worst, mean, average * */ void -print_gen(FILE *fp, Pool *pool, int generation) +print_gen(FILE * fp, Pool * pool, int generation) { - int lowest; - - /* Get index to lowest ranking gene in poplulation. */ - /* Use 2nd to last since last is buffer. */ - lowest = pool->size > 1 ? pool->size-2 : 0; - - fprintf (fp, - "%5d | Bst: %f Wst: %f Mean: %f Avg: %f\n", - generation, - pool->data[0].worth, - pool->data[lowest].worth, - pool->data[pool->size/2].worth, - avg_pool(pool)); + int lowest; + + /* Get index to lowest ranking gene in poplulation. */ + /* Use 2nd to last since last is buffer. */ + lowest = pool->size > 1 ? pool->size - 2 : 0; + + fprintf(fp, + "%5d | Bst: %f Wst: %f Mean: %f Avg: %f\n", + generation, + pool->data[0].worth, + pool->data[lowest].worth, + pool->data[pool->size / 2].worth, + avg_pool(pool)); } void -print_edge_table (FILE *fp, Edge *edge_table, int num_gene) +print_edge_table(FILE * fp, Edge * edge_table, int num_gene) { - int i,j; - - fprintf (fp, "\nEDGE TABLE\n"); - - for (i=1; i<=num_gene; i++) - { - fprintf (fp, "%d :", i); - for (j=0; j<edge_table[i].unused_edges; j++) - fprintf (fp, " %d", edge_table[i].edge_list[j]); - fprintf (fp, "\n"); - } - - fprintf (fp, "\n"); + int i, + j; + + fprintf(fp, "\nEDGE TABLE\n"); + + for (i = 1; i <= num_gene; i++) + { + fprintf(fp, "%d :", i); + for (j = 0; j < edge_table[i].unused_edges; j++) + fprintf(fp, " %d", edge_table[i].edge_list[j]); + fprintf(fp, "\n"); + } + + fprintf(fp, "\n"); } /************************************************************* @@ -133,116 +139,147 @@ print_edge_table (FILE *fp, Edge *edge_table, int num_gene) *************************************************************/ void -geqo_print_joinclauses(Query *root, List *clauses) +geqo_print_joinclauses(Query * root, List * clauses) { - List *l; - extern void print_expr(Node *expr, List *rtable); /* in print.c */ + List *l; + extern void print_expr(Node * expr, List * rtable); /* in print.c */ - foreach(l, clauses) { - CInfo *c = lfirst(l); + foreach(l, clauses) + { + CInfo *c = lfirst(l); - print_expr((Node*)c->clause, root->rtable); - if (lnext(l)) printf(" "); - } + print_expr((Node *) c->clause, root->rtable); + if (lnext(l)) + printf(" "); + } } void -geqo_print_path(Query *root, Path *path, int indent) +geqo_print_path(Query * root, Path * path, int indent) { - char *ptype = NULL; - JoinPath *jp; - bool join = false; - int i; - - for(i=0; i < indent; i++) - printf("\t"); - - switch(nodeTag(path)) { - case T_Path: - ptype = "SeqScan"; join=false; break; - case T_IndexPath: - ptype = "IdxScan"; join=false; break; - case T_JoinPath: - ptype = "Nestloop"; join=true; break; - case T_MergePath: - ptype = "MergeJoin"; join=true; break; - case T_HashPath: - ptype = "HashJoin"; join=true; break; - default: - break; - } - if (join) { - int size = path->parent->size; - jp = (JoinPath*)path; - printf("%s size=%d cost=%f\n", ptype, size, path->path_cost); - switch(nodeTag(path)) { + char *ptype = NULL; + JoinPath *jp; + bool join = false; + int i; + + for (i = 0; i < indent; i++) + printf("\t"); + + switch (nodeTag(path)) + { + case T_Path: + ptype = "SeqScan"; + join = false; + break; + case T_IndexPath: + ptype = "IdxScan"; + join = false; + break; + case T_JoinPath: + ptype = "Nestloop"; + join = true; + break; case T_MergePath: + ptype = "MergeJoin"; + join = true; + break; case T_HashPath: - for(i=0; i < indent+1; i++) - printf("\t"); - printf(" clauses=("); - geqo_print_joinclauses(root, - ((JoinPath*)path)->pathclauseinfo); - printf(")\n"); - - if (nodeTag(path)==T_MergePath) { - MergePath *mp = (MergePath*)path; - if (mp->outersortkeys || mp->innersortkeys) { - for(i=0; i < indent+1; i++) - printf("\t"); - printf(" sortouter=%d sortinner=%d\n", - ((mp->outersortkeys)?1:0), - ((mp->innersortkeys)?1:0)); - } - } - break; + ptype = "HashJoin"; + join = true; + break; default: - break; + break; } - geqo_print_path(root, jp->outerjoinpath, indent+1); - geqo_print_path(root, jp->innerjoinpath, indent+1); - } else { - int size = path->parent->size; - int relid = lfirsti(path->parent->relids); - printf("%s(%d) size=%d cost=%f", - ptype, relid, size, path->path_cost); - - if (nodeTag(path)==T_IndexPath) { - List *k, *l; - - printf(" keys="); - foreach (k, path->keys) { - printf("("); - foreach (l, lfirst(k)) { - Var *var = lfirst(l); - printf("%d.%d", var->varnoold, var->varoattno); - if (lnext(l)) printf(", "); + if (join) + { + int size = path->parent->size; + + jp = (JoinPath *) path; + printf("%s size=%d cost=%f\n", ptype, size, path->path_cost); + switch (nodeTag(path)) + { + case T_MergePath: + case T_HashPath: + for (i = 0; i < indent + 1; i++) + printf("\t"); + printf(" clauses=("); + geqo_print_joinclauses(root, + ((JoinPath *) path)->pathclauseinfo); + printf(")\n"); + + if (nodeTag(path) == T_MergePath) + { + MergePath *mp = (MergePath *) path; + + if (mp->outersortkeys || mp->innersortkeys) + { + for (i = 0; i < indent + 1; i++) + printf("\t"); + printf(" sortouter=%d sortinner=%d\n", + ((mp->outersortkeys) ? 1 : 0), + ((mp->innersortkeys) ? 1 : 0)); + } + } + break; + default: + break; } - printf(")"); - if (lnext(k)) printf(", "); - } + geqo_print_path(root, jp->outerjoinpath, indent + 1); + geqo_print_path(root, jp->innerjoinpath, indent + 1); + } + else + { + int size = path->parent->size; + int relid = lfirsti(path->parent->relids); + + printf("%s(%d) size=%d cost=%f", + ptype, relid, size, path->path_cost); + + if (nodeTag(path) == T_IndexPath) + { + List *k, + *l; + + printf(" keys="); + foreach(k, path->keys) + { + printf("("); + foreach(l, lfirst(k)) + { + Var *var = lfirst(l); + + printf("%d.%d", var->varnoold, var->varoattno); + if (lnext(l)) + printf(", "); + } + printf(")"); + if (lnext(k)) + printf(", "); + } + } + printf("\n"); } - printf("\n"); - } } -void -geqo_print_rel(Query *root, Rel *rel) +void +geqo_print_rel(Query * root, Rel * rel) { - List *l; - - printf("______________________________\n"); - printf("("); - foreach(l, rel->relids) { - printf("%d ", lfirsti(l)); - } - printf("): size=%d width=%d\n", rel->size, rel->width); - - printf("\tpath list:\n"); - foreach (l, rel->pathlist) { - geqo_print_path(root, lfirst(l), 1); - } - - printf("\tcheapest path:\n"); - geqo_print_path(root, rel->cheapestpath, 1); + List *l; + + printf("______________________________\n"); + printf("("); + foreach(l, rel->relids) + { + printf("%d ", lfirsti(l)); + } + printf("): size=%d width=%d\n", rel->size, rel->width); + + printf("\tpath list:\n"); + foreach(l, rel->pathlist) + { + geqo_print_path(root, lfirst(l), 1); + } + + printf("\tcheapest path:\n"); + geqo_print_path(root, rel->cheapestpath, 1); } diff --git a/src/backend/optimizer/geqo/geqo_mutation.c b/src/backend/optimizer/geqo/geqo_mutation.c index 9d544564210..a5a43e6e2b9 100644 --- a/src/backend/optimizer/geqo/geqo_mutation.c +++ b/src/backend/optimizer/geqo/geqo_mutation.c @@ -2,33 +2,33 @@ * * geqo_mutation.c-- * -* TSP mutation routines +* TSP mutation routines * -* $Id: geqo_mutation.c,v 1.1 1997/02/19 12:57:13 scrappy Exp $ +* $Id: geqo_mutation.c,v 1.2 1997/09/07 04:43:13 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* this is adopted from Genitor : */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ #include "postgres.h" @@ -50,27 +50,28 @@ #include "optimizer/geqo_random.h" #include "optimizer/geqo_mutation.h" - void - geqo_mutation (Gene *tour, int num_gene) - { - int swap1; - int swap2; - int num_swaps = geqo_randint (num_gene/3, 0); - Gene temp; +void +geqo_mutation(Gene * tour, int num_gene) +{ + int swap1; + int swap2; + int num_swaps = geqo_randint(num_gene / 3, 0); + Gene temp; - while (num_swaps > 0) { - swap1 = geqo_randint (num_gene-1, 0); - swap2 = geqo_randint (num_gene-1, 0); + while (num_swaps > 0) + { + swap1 = geqo_randint(num_gene - 1, 0); + swap2 = geqo_randint(num_gene - 1, 0); - while (swap1 == swap2) - swap2 = geqo_randint (num_gene-1, 0); + while (swap1 == swap2) + swap2 = geqo_randint(num_gene - 1, 0); - temp = tour[swap1]; - tour[swap1] = tour[swap2]; - tour[swap2] = temp; + temp = tour[swap1]; + tour[swap1] = tour[swap2]; + tour[swap2] = temp; - num_swaps -= 1; - } + num_swaps -= 1; + } } diff --git a/src/backend/optimizer/geqo/geqo_ox1.c b/src/backend/optimizer/geqo/geqo_ox1.c index 329554f9aae..b88b8950673 100644 --- a/src/backend/optimizer/geqo/geqo_ox1.c +++ b/src/backend/optimizer/geqo/geqo_ox1.c @@ -2,35 +2,35 @@ * * geqo_ox1.c-- * -* order crossover [OX] routines; -* OX1 operator according to Davis -* (Proc Int'l Joint Conf on AI) +* order crossover [OX] routines; +* OX1 operator according to Davis +* (Proc Int'l Joint Conf on AI) * -* $Id: geqo_ox1.c,v 1.1 1997/03/14 16:02:58 scrappy Exp $ +* $Id: geqo_ox1.c,v 1.2 1997/09/07 04:43:14 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* the ox algorithm is adopted from Genitor : */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ #include "postgres.h" @@ -56,48 +56,52 @@ /* ox1-- * - * position crossover + * position crossover */ void -ox1(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table) +ox1(Gene * tour1, Gene * tour2, Gene * offspring, int num_gene, City * city_table) { - int left, right, k, p, temp; - - /* initialize city table */ - for (k = 1; k <= num_gene; k++) - city_table[k].used = 0; - - /* select portion to copy from tour1 */ - left = geqo_randint (num_gene - 1, 0); - right = geqo_randint (num_gene - 1, 0); - - if (left > right) - { - temp = left; - left = right; - right = temp; - } - - /* copy portion from tour1 to offspring */ - for (k = left; k <= right; k++) - { - offspring[k] = tour1[k]; - city_table[(int) tour1[k]].used = 1; - } - - k = (right + 1) % num_gene; /* index into offspring */ - p = k; /* index into tour2 */ - - /* copy stuff from tour2 to offspring */ - while (k != left) - { - if (!city_table[(int) tour2[p]].used) - { - offspring[k] = tour2[p]; - k = (k + 1) % num_gene; - city_table[(int) tour2[p]].used = 1; - } - p = (p + 1) % num_gene; /* increment tour2-index */ - } - - } + int left, + right, + k, + p, + temp; + + /* initialize city table */ + for (k = 1; k <= num_gene; k++) + city_table[k].used = 0; + + /* select portion to copy from tour1 */ + left = geqo_randint(num_gene - 1, 0); + right = geqo_randint(num_gene - 1, 0); + + if (left > right) + { + temp = left; + left = right; + right = temp; + } + + /* copy portion from tour1 to offspring */ + for (k = left; k <= right; k++) + { + offspring[k] = tour1[k]; + city_table[(int) tour1[k]].used = 1; + } + + k = (right + 1) % num_gene; /* index into offspring */ + p = k; /* index into tour2 */ + + /* copy stuff from tour2 to offspring */ + while (k != left) + { + if (!city_table[(int) tour2[p]].used) + { + offspring[k] = tour2[p]; + k = (k + 1) % num_gene; + city_table[(int) tour2[p]].used = 1; + } + p = (p + 1) % num_gene; /* increment tour2-index */ + } + +} diff --git a/src/backend/optimizer/geqo/geqo_ox2.c b/src/backend/optimizer/geqo/geqo_ox2.c index 2afcece01ff..ef09925b4fa 100644 --- a/src/backend/optimizer/geqo/geqo_ox2.c +++ b/src/backend/optimizer/geqo/geqo_ox2.c @@ -2,35 +2,35 @@ * * geqo_ox2.c-- * -* order crossover [OX] routines; -* OX2 operator according to Syswerda -* (The Genetic Algorithms Handbook, ed L Davis) +* order crossover [OX] routines; +* OX2 operator according to Syswerda +* (The Genetic Algorithms Handbook, ed L Davis) * -* $Id: geqo_ox2.c,v 1.1 1997/03/14 16:03:02 scrappy Exp $ +* $Id: geqo_ox2.c,v 1.2 1997/09/07 04:43:15 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* the ox algorithm is adopted from Genitor : */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ #include "postgres.h" @@ -56,58 +56,70 @@ /* ox2-- * - * position crossover + * position crossover */ void -ox2(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table) +ox2(Gene * tour1, Gene * tour2, Gene * offspring, int num_gene, City * city_table) { - int k, j, count, pos, select, num_positions; - - /* initialize city table */ - for (k = 1; k <= num_gene; k++) { - city_table[k].used = 0; - city_table[k-1].select_list = -1; - } - - /* determine the number of positions to be inherited from tour1 */ - num_positions = geqo_randint (2*num_gene/3, num_gene/3); - - /* make a list of selected cities */ - for (k=0; k<num_positions; k++) { - pos = geqo_randint (num_gene - 1, 0); - city_table[pos].select_list = (int) tour1[pos]; - city_table[(int) tour1[pos]].used = 1; /* mark used */ - } - - - count = 0; - k = 0; - - /* consolidate the select list to adjacent positions */ - while (count < num_positions) { - if (city_table[k].select_list == -1) { - j = k + 1; - while ((city_table[j].select_list == -1) && (j < num_gene)) - j++; - - city_table[k].select_list = city_table[j].select_list; - city_table[j].select_list = -1; - count ++; - } - else - count ++; - k++; - } - - select = 0; - - for (k=0; k<num_gene; k++) { - if (city_table[(int) tour2[k]].used) { - offspring[k] = (Gene) city_table[select].select_list; - select ++; /* next city in the select list */ - } - else /* city isn't used yet, so inherit from tour2 */ - offspring[k] = tour2[k]; - } + int k, + j, + count, + pos, + select, + num_positions; + + /* initialize city table */ + for (k = 1; k <= num_gene; k++) + { + city_table[k].used = 0; + city_table[k - 1].select_list = -1; + } + + /* determine the number of positions to be inherited from tour1 */ + num_positions = geqo_randint(2 * num_gene / 3, num_gene / 3); + + /* make a list of selected cities */ + for (k = 0; k < num_positions; k++) + { + pos = geqo_randint(num_gene - 1, 0); + city_table[pos].select_list = (int) tour1[pos]; + city_table[(int) tour1[pos]].used = 1; /* mark used */ + } + + + count = 0; + k = 0; + + /* consolidate the select list to adjacent positions */ + while (count < num_positions) + { + if (city_table[k].select_list == -1) + { + j = k + 1; + while ((city_table[j].select_list == -1) && (j < num_gene)) + j++; + + city_table[k].select_list = city_table[j].select_list; + city_table[j].select_list = -1; + count++; + } + else + count++; + k++; + } + + select = 0; + + for (k = 0; k < num_gene; k++) + { + if (city_table[(int) tour2[k]].used) + { + offspring[k] = (Gene) city_table[select].select_list; + select++; /* next city in the select list */ + } + else +/* city isn't used yet, so inherit from tour2 */ + offspring[k] = tour2[k]; + } } diff --git a/src/backend/optimizer/geqo/geqo_params.c b/src/backend/optimizer/geqo/geqo_params.c index 52c57c45378..45f7dfd5ddc 100644 --- a/src/backend/optimizer/geqo/geqo_params.c +++ b/src/backend/optimizer/geqo/geqo_params.c @@ -1,20 +1,20 @@ /*------------------------------------------------------------------------ * * geqo_params.c-- -* routines for determining necessary genetic optimization parameters +* routines for determining necessary genetic optimization parameters * * Copyright (c) 1994, Regents of the University of California * -* $Id: geqo_params.c,v 1.5 1997/08/18 02:14:41 momjian Exp $ +* $Id: geqo_params.c,v 1.6 1997/09/07 04:43:16 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ @@ -45,48 +45,48 @@ #include "storage/fd.h" -#define POOL_TAG "Pool_Size" -#define TRIAL_TAG "Generations" -#define RAND_TAG "Random_Seed" -#define BIAS_TAG "Selection_Bias" +#define POOL_TAG "Pool_Size" +#define TRIAL_TAG "Generations" +#define RAND_TAG "Random_Seed" +#define BIAS_TAG "Selection_Bias" -#define EFFORT_TAG "Effort" /* optimization effort and */ -#define LOW "low" /* corresponding tags */ -#define MEDIUM "medium" -#define HIGH "high" +#define EFFORT_TAG "Effort"/* optimization effort and */ +#define LOW "low" /* corresponding tags */ +#define MEDIUM "medium" +#define HIGH "high" -#define MAX_TOKEN 80 /* Maximum size of one token in the * - * configuration file */ +#define MAX_TOKEN 80 /* Maximum size of one token in the * + * configuration file */ -static int gimme_pool_size(int string_length); -static int gimme_number_generations(int pool_size, int effort); -static int next_token(FILE *, char *, int); +static int gimme_pool_size(int string_length); +static int gimme_number_generations(int pool_size, int effort); +static int next_token(FILE *, char *, int); /* * geqo_param-- - * get ga parameters out of "$PGDATA/pg_geqo" file. + * get ga parameters out of "$PGDATA/pg_geqo" file. */ void geqo_params(int string_length) { - int i; + int i; - char buf[MAX_TOKEN]; - FILE *file; + char buf[MAX_TOKEN]; + FILE *file; - char *conf_file; + char *conf_file; /* these static variables are used to signal that a value has been set */ - int pool_size = 0; - int number_trials = 0; - int random_seed = 0; - int selection_bias = 0; - int effort = 0; + int pool_size = 0; + int number_trials = 0; + int random_seed = 0; + int selection_bias = 0; + int effort = 0; /* put together the full pathname to the config file */ conf_file = - (char *) palloc((strlen(DataDir)+strlen(GEQO_FILE)+2)*sizeof(char)); + (char *) palloc((strlen(DataDir) + strlen(GEQO_FILE) + 2) * sizeof(char)); sprintf(conf_file, "%s/%s", DataDir, GEQO_FILE); @@ -94,99 +94,109 @@ geqo_params(int string_length) file = AllocateFile(conf_file, "r"); if (file) { + /* * empty and comment line stuff */ while ((i = next_token(file, buf, sizeof(buf))) != EOF) { - /* If only token on the line, ignore */ - if (i == '\n') continue; - - /* Comment -- read until end of line then next line */ - if (buf[0] == '#') - { - while (next_token(file, buf, sizeof(buf)) == 0) ; - continue; - } + /* If only token on the line, ignore */ + if (i == '\n') + continue; + + /* Comment -- read until end of line then next line */ + if (buf[0] == '#') + { + while (next_token(file, buf, sizeof(buf)) == 0); + continue; + } /* * get ga parameters by parsing */ - + /*------------------------------------------------- pool size */ - if ( strcmp(buf, POOL_TAG) == 0 ) + if (strcmp(buf, POOL_TAG) == 0) { i = next_token(file, buf, sizeof(buf)); /* get next token */ - - if (i != EOF) /* only ignore if we got no text at all */ + + if (i != EOF) /* only ignore if we got no text at all */ { - if (sscanf (buf, "%d", &PoolSize) == 1) pool_size = 1; + if (sscanf(buf, "%d", &PoolSize) == 1) + pool_size = 1; } - + } - + /*------------------------------------------------- number of trials */ - else if ( strcmp(buf, TRIAL_TAG) == 0 ) + else if (strcmp(buf, TRIAL_TAG) == 0) { i = next_token(file, buf, sizeof(buf)); - + if (i != EOF) { - if (sscanf (buf, "%d", &Generations) == 1) number_trials = 1; + if (sscanf(buf, "%d", &Generations) == 1) + number_trials = 1; } - + } - + /*------------------------------------------------- optimization effort */ - else if ( strcmp(buf, EFFORT_TAG) == 0 ) + else if (strcmp(buf, EFFORT_TAG) == 0) { i = next_token(file, buf, sizeof(buf)); - + if (i != EOF) { - if (strcmp (buf, LOW) == 0) effort = LOW_EFFORT; - else if (strcmp (buf, MEDIUM) == 0) effort = MEDIUM_EFFORT; - else if (strcmp (buf, HIGH) == 0) effort = HIGH_EFFORT; + if (strcmp(buf, LOW) == 0) + effort = LOW_EFFORT; + else if (strcmp(buf, MEDIUM) == 0) + effort = MEDIUM_EFFORT; + else if (strcmp(buf, HIGH) == 0) + effort = HIGH_EFFORT; } - + } - + /*------------------------------------------- random seed */ - else if ( strcmp(buf, RAND_TAG) == 0 ) - { + else if (strcmp(buf, RAND_TAG) == 0) + { i = next_token(file, buf, sizeof(buf)); - + if (i != EOF) { - if (sscanf (buf, "%ld", &RandomSeed) == 1) random_seed = 1; + if (sscanf(buf, "%ld", &RandomSeed) == 1) + random_seed = 1; } - + } - + /*------------------------------------------- selection bias */ - else if ( strcmp(buf, BIAS_TAG) == 0 ) + else if (strcmp(buf, BIAS_TAG) == 0) { i = next_token(file, buf, sizeof(buf)); - + if (i != EOF) { - if (sscanf (buf, "%lf", &SelectionBias) == 1) selection_bias = 1; + if (sscanf(buf, "%lf", &SelectionBias) == 1) + selection_bias = 1; } - + } - + /* unrecognized tags */ else { if (i != EOF) { } - - elog(DEBUG,"geqo_params: unknown parameter type \"%s\"\nin file \'%s\'", buf, conf_file); + + elog(DEBUG, "geqo_params: unknown parameter type \"%s\"\nin file \'%s\'", buf, conf_file); /* if not at end-of-line, keep reading til we are */ - while (i == 0) i = next_token(file, buf, sizeof(buf)); - } + while (i == 0) + i = next_token(file, buf, sizeof(buf)); + } } FreeFile(file); @@ -194,9 +204,9 @@ geqo_params(int string_length) pfree(conf_file); } - else + else { - elog(DEBUG,"geqo_params: ga parameter file\n\'%s\'\ndoes not exist or permissions are not setup correctly", conf_file); + elog(DEBUG, "geqo_params: ga parameter file\n\'%s\'\ndoes not exist or permissions are not setup correctly", conf_file); } /* @@ -204,49 +214,49 @@ geqo_params(int string_length) */ /**************** PoolSize: essential ****************/ - if ( !(pool_size) ) + if (!(pool_size)) { PoolSize = gimme_pool_size(string_length); - elog(DEBUG,"geqo_params: no pool size specified;\nusing computed value of %d", PoolSize); + elog(DEBUG, "geqo_params: no pool size specified;\nusing computed value of %d", PoolSize); } - - + + /**************** Effort: essential ****************/ - if ( !(effort) ) + if (!(effort)) { if (PoolSize == MAX_POOL) effort = HIGH_EFFORT; else effort = MEDIUM_EFFORT; - - elog(DEBUG,"geqo_params: no optimization effort specified;\nusing value of %d", effort); + + elog(DEBUG, "geqo_params: no optimization effort specified;\nusing value of %d", effort); } /**************** Generations: essential ****************/ - if ( !(number_trials) ) + if (!(number_trials)) { Generations = gimme_number_generations(PoolSize, effort); - - elog(DEBUG,"geqo_params: no number of trials specified;\nusing computed value of %d", Generations); + + elog(DEBUG, "geqo_params: no number of trials specified;\nusing computed value of %d", Generations); } /* RandomSeed: */ - if ( !(random_seed) ) + if (!(random_seed)) { RandomSeed = (long) time(NULL); - elog(DEBUG,"geqo_params: no random seed specified;\nusing computed value of %ld", RandomSeed); + elog(DEBUG, "geqo_params: no random seed specified;\nusing computed value of %ld", RandomSeed); } /* SelectionBias: */ - if ( !(selection_bias) ) + if (!(selection_bias)) { SelectionBias = SELECTION_BIAS; - elog(DEBUG,"geqo_params: no selection bias specified;\nusing default value of %f", SelectionBias); + elog(DEBUG, "geqo_params: no selection bias specified;\nusing default value of %f", SelectionBias); } } @@ -255,73 +265,79 @@ geqo_params(int string_length) /* * Grab one token out of fp. Defined as the next string of non-whitespace * in the file. After we get the token, continue reading until EOF, end of - * line or the next token. If it's the last token on the line, return '\n' + * line or the next token. If it's the last token on the line, return '\n' * for the value. If we get EOF before reading a token, return EOF. In all * other cases return 0. */ -static int -next_token(FILE *fp, char *buf, int bufsz) +static int +next_token(FILE * fp, char *buf, int bufsz) { - int c; - char *eb = buf+(bufsz-1); + int c; + char *eb = buf + (bufsz - 1); - /* Discard inital whitespace */ - while (isspace(c = getc(fp))) ; + /* Discard inital whitespace */ + while (isspace(c = getc(fp))); - /* EOF seen before any token so return EOF */ - if (c == EOF) return -1; + /* EOF seen before any token so return EOF */ + if (c == EOF) + return -1; - /* Form a token in buf */ - do { - if (buf < eb) *buf++ = c; - c = getc(fp); - } while (!isspace(c) && c != EOF); - *buf = '\0'; + /* Form a token in buf */ + do + { + if (buf < eb) + *buf++ = c; + c = getc(fp); + } while (!isspace(c) && c != EOF); + *buf = '\0'; - /* Discard trailing tabs and spaces */ - while (c == ' ' || c == '\t') c = getc(fp); + /* Discard trailing tabs and spaces */ + while (c == ' ' || c == '\t') + c = getc(fp); - /* Put back the char that was non-whitespace (putting back EOF is ok) */ - ungetc(c, fp); + /* Put back the char that was non-whitespace (putting back EOF is ok) */ + ungetc(c, fp); - /* If we ended with a newline, return that, otherwise return 0 */ - return (c == '\n' ? '\n' : 0); + /* If we ended with a newline, return that, otherwise return 0 */ + return (c == '\n' ? '\n' : 0); } /* gimme_pool_size-- - * compute good estimation for pool size - * according to number of involved rels in a query + * compute good estimation for pool size + * according to number of involved rels in a query */ -static int +static int gimme_pool_size(int string_length) { - double exponent; - double size; + double exponent; + double size; exponent = (double) string_length + 1.0; - size = pow (2.0, exponent); + size = pow(2.0, exponent); - if (size < MIN_POOL) { + if (size < MIN_POOL) + { return (MIN_POOL); - } - else if (size > MAX_POOL) { + } + else if (size > MAX_POOL) + { return (MAX_POOL); - } - else - return ( (int) ceil(size) ); + } + else + return ((int) ceil(size)); } /* gimme_number_generations-- - * compute good estimation for number of generations size - * for convergence + * compute good estimation for number of generations size + * for convergence */ -static int +static int gimme_number_generations(int pool_size, int effort) { - int number_gens; + int number_gens; - number_gens = (int) ceil ( geqo_log((double) pool_size, 2.0) ); + number_gens = (int) ceil(geqo_log((double) pool_size, 2.0)); return (effort * number_gens); } diff --git a/src/backend/optimizer/geqo/geqo_paths.c b/src/backend/optimizer/geqo/geqo_paths.c index a22be406f5a..d98855d2887 100644 --- a/src/backend/optimizer/geqo/geqo_paths.c +++ b/src/backend/optimizer/geqo/geqo_paths.c @@ -1,11 +1,11 @@ /*------------------------------------------------------------------------- * * geqo_paths.c-- - * Routines to process redundant paths and relations + * Routines to process redundant paths and relations * * Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_paths.c,v 1.4 1997/06/11 02:44:12 vadim Exp $ + * $Id: geqo_paths.c,v 1.5 1997/09/07 04:43:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,120 +28,128 @@ #include "optimizer/geqo_paths.h" -static List *geqo_prune_rel(Rel *rel, List *other_rels); -static Path *set_paths(Rel *rel, Path *unorderedpath); +static List *geqo_prune_rel(Rel * rel, List * other_rels); +static Path *set_paths(Rel * rel, Path * unorderedpath); -/* +/* * geqo-prune-rels-- - * Removes any redundant relation entries from a list of rel nodes - * 'rel-list'. - * - * Returns the resulting list. - * + * Removes any redundant relation entries from a list of rel nodes + * 'rel-list'. + * + * Returns the resulting list. + * */ -List *geqo_prune_rels(List *rel_list) +List * +geqo_prune_rels(List * rel_list) { - List *temp_list = NIL; - - if (rel_list != NIL) { - temp_list = lcons(lfirst(rel_list), - geqo_prune_rels(geqo_prune_rel((Rel*)lfirst(rel_list), - lnext(rel_list)))); - } - return(temp_list); + List *temp_list = NIL; + + if (rel_list != NIL) + { + temp_list = lcons(lfirst(rel_list), + geqo_prune_rels(geqo_prune_rel((Rel *) lfirst(rel_list), + lnext(rel_list)))); + } + return (temp_list); } -/* +/* * geqo-prune-rel-- - * Prunes those relations from 'other-rels' that are redundant with - * 'rel'. A relation is redundant if it is built up of the same - * relations as 'rel'. Paths for the redundant relation are merged into - * the pathlist of 'rel'. - * + * Prunes those relations from 'other-rels' that are redundant with + * 'rel'. A relation is redundant if it is built up of the same + * relations as 'rel'. Paths for the redundant relation are merged into + * the pathlist of 'rel'. + * * Returns a list of non-redundant relations, and sets the pathlist field * of 'rel' appropriately. - * + * */ -static List * -geqo_prune_rel(Rel *rel, List *other_rels) +static List * +geqo_prune_rel(Rel * rel, List * other_rels) { - List *i = NIL; - List *t_list = NIL; - List *temp_node = NIL; - Rel *other_rel = (Rel *)NULL; - - foreach(i, other_rels) { - other_rel = (Rel*)lfirst(i); - if(same(rel->relids, other_rel->relids)) { - rel->pathlist = add_pathlist(rel, - rel->pathlist, - other_rel->pathlist); - t_list = nconc(t_list, NIL); /* XXX is this right ? */ - } else { - temp_node = lcons(other_rel, NIL); - t_list = nconc(t_list,temp_node); - } - } - return(t_list); + List *i = NIL; + List *t_list = NIL; + List *temp_node = NIL; + Rel *other_rel = (Rel *) NULL; + + foreach(i, other_rels) + { + other_rel = (Rel *) lfirst(i); + if (same(rel->relids, other_rel->relids)) + { + rel->pathlist = add_pathlist(rel, + rel->pathlist, + other_rel->pathlist); + t_list = nconc(t_list, NIL); /* XXX is this right ? */ + } + else + { + temp_node = lcons(other_rel, NIL); + t_list = nconc(t_list, temp_node); + } + } + return (t_list); } -/* +/* * geqo-rel-paths-- - * For a relation 'rel' (which corresponds to a join - * relation), set pointers to the unordered path and cheapest paths - * (if the unordered path isn't the cheapest, it is pruned), and - * reset the relation's size field to reflect the join. - * + * For a relation 'rel' (which corresponds to a join + * relation), set pointers to the unordered path and cheapest paths + * (if the unordered path isn't the cheapest, it is pruned), and + * reset the relation's size field to reflect the join. + * * Returns nothing of interest. - * + * */ void -geqo_rel_paths(Rel *rel) +geqo_rel_paths(Rel * rel) { - List *y = NIL; - Path *path = (Path*)NULL; - JoinPath *cheapest = (JoinPath*)NULL; - - rel->size = 0; - foreach(y, rel->pathlist) - { - path = (Path*)lfirst(y); - - if(!path->p_ordering.ord.sortop) + List *y = NIL; + Path *path = (Path *) NULL; + JoinPath *cheapest = (JoinPath *) NULL; + + rel->size = 0; + foreach(y, rel->pathlist) + { + path = (Path *) lfirst(y); + + if (!path->p_ordering.ord.sortop) break; - } + } - cheapest = (JoinPath*)set_paths(rel, path); - if ( IsA_JoinPath (cheapest) ) - rel->size = compute_joinrel_size(cheapest); + cheapest = (JoinPath *) set_paths(rel, path); + if (IsA_JoinPath(cheapest)) + rel->size = compute_joinrel_size(cheapest); } -/* +/* * set-path-- - * Compares the unordered path for a relation with the cheapest path. If - * the unordered path is not cheapest, it is pruned. - * - * Resets the pointers in 'rel' for unordered and cheapest paths. - * + * Compares the unordered path for a relation with the cheapest path. If + * the unordered path is not cheapest, it is pruned. + * + * Resets the pointers in 'rel' for unordered and cheapest paths. + * * Returns the cheapest path. - * + * */ -static Path * -set_paths(Rel *rel, Path *unorderedpath) +static Path * +set_paths(Rel * rel, Path * unorderedpath) { - Path *cheapest = set_cheapest(rel, rel->pathlist); - - /* don't prune if not pruneable -- JMH, 11/23/92 */ - if(unorderedpath != cheapest - && rel->pruneable) { - - rel->unorderedpath = (Path *)NULL; - rel->pathlist = lremove(unorderedpath, rel->pathlist); - } else { - rel->unorderedpath = (Path *)unorderedpath; - } - - return(cheapest); + Path *cheapest = set_cheapest(rel, rel->pathlist); + + /* don't prune if not pruneable -- JMH, 11/23/92 */ + if (unorderedpath != cheapest + && rel->pruneable) + { + + rel->unorderedpath = (Path *) NULL; + rel->pathlist = lremove(unorderedpath, rel->pathlist); + } + else + { + rel->unorderedpath = (Path *) unorderedpath; + } + + return (cheapest); } - diff --git a/src/backend/optimizer/geqo/geqo_pmx.c b/src/backend/optimizer/geqo/geqo_pmx.c index c6e699cfa9f..c9187fec54b 100644 --- a/src/backend/optimizer/geqo/geqo_pmx.c +++ b/src/backend/optimizer/geqo/geqo_pmx.c @@ -2,35 +2,35 @@ * * geqo_pmx.c-- * -* partially matched crossover [PMX] routines; -* PMX operator according to Goldberg & Lingle -* (Proc Int'l Conf on GA's) +* partially matched crossover [PMX] routines; +* PMX operator according to Goldberg & Lingle +* (Proc Int'l Conf on GA's) * -* $Id: geqo_pmx.c,v 1.1 1997/02/19 12:57:28 scrappy Exp $ +* $Id: geqo_pmx.c,v 1.2 1997/09/07 04:43:18 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* the pmx algorithm is adopted from Genitor : */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ #include "postgres.h" @@ -56,155 +56,182 @@ /* pmx-- * - * partially matched crossover + * partially matched crossover */ void -pmx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene) +pmx(Gene * tour1, Gene * tour2, Gene * offspring, int num_gene) { - int *failed = (int *) palloc ((num_gene+1)*sizeof(int)); - int *from = (int *) palloc ((num_gene+1)*sizeof(int)); - int *indx = (int *) palloc ((num_gene+1)*sizeof(int)); - int *check_list = (int *) palloc ((num_gene+1)*sizeof(int)); + int *failed = (int *) palloc((num_gene + 1) * sizeof(int)); + int *from = (int *) palloc((num_gene + 1) * sizeof(int)); + int *indx = (int *) palloc((num_gene + 1) * sizeof(int)); + int *check_list = (int *) palloc((num_gene + 1) * sizeof(int)); + + int left, + right, + temp, + i, + j, + k; + int mx_fail, + found, + mx_hold; - int left, right, temp, i, j, k; - int mx_fail, found, mx_hold; - /* no mutation so start up the pmx replacement algorithm */ /* initialize failed[], from[], check_list[] */ - for (k = 0; k < num_gene; k++) { - failed[k] = -1; - from[k] = -1; - check_list[k+1] = 0; - } - + for (k = 0; k < num_gene; k++) + { + failed[k] = -1; + from[k] = -1; + check_list[k + 1] = 0; + } + /* locate crossover points */ - left = geqo_randint(num_gene-1, 0); - right = geqo_randint(num_gene-1, 0); + left = geqo_randint(num_gene - 1, 0); + right = geqo_randint(num_gene - 1, 0); - if (left > right) { - temp = left; - left = right; - right = temp; - } + if (left > right) + { + temp = left; + left = right; + right = temp; + } /* copy tour2 into offspring */ - for (k = 0; k < num_gene; k++) { - offspring[k] = tour2[k]; - from[k] = DAD; - check_list[tour2[k]]++; - } - + for (k = 0; k < num_gene; k++) + { + offspring[k] = tour2[k]; + from[k] = DAD; + check_list[tour2[k]]++; + } + /* copy tour1 into offspring */ - for (k = left; k <= right; k++) { - check_list[offspring[k]]--; - offspring[k] = tour1[k]; - from[k] = MOM; - check_list[tour1[k]]++; - } + for (k = left; k <= right; k++) + { + check_list[offspring[k]]--; + offspring[k] = tour1[k]; + from[k] = MOM; + check_list[tour1[k]]++; + } /* pmx main part */ - mx_fail = 0; + mx_fail = 0; /* STEP 1 */ - for (k = left; k <= right; k++) { /* for all elements in the tour1-2 */ - - if (tour1[k] == tour2[k]) found = 1; /* find match in tour2 */ + for (k = left; k <= right; k++) + { /* for all elements in the tour1-2 */ - else { - found = 0; /* substitute elements */ + if (tour1[k] == tour2[k]) + found = 1; /* find match in tour2 */ - j = 0; - while ( !(found) && (j < num_gene) ) { - if ( (offspring[j] == tour1[k]) && (from[j] == DAD) ) { + else + { + found = 0; /* substitute elements */ - check_list[offspring[j]]--; - offspring[j] = tour2[k]; - found = 1; - check_list[tour2[k]]++; - } + j = 0; + while (!(found) && (j < num_gene)) + { + if ((offspring[j] == tour1[k]) && (from[j] == DAD)) + { - j++; - } + check_list[offspring[j]]--; + offspring[j] = tour2[k]; + found = 1; + check_list[tour2[k]]++; + } - } + j++; + } - if ( !(found) ) { /* failed to replace gene */ - failed[mx_fail] = (int) tour1[k]; - indx[mx_fail] = k; - mx_fail++; - } - - } /* ... for */ - - -/* STEP 2 */ + } - /* see if any genes could not be replaced */ - if (mx_fail > 0) { - mx_hold = mx_fail; + if (!(found)) + { /* failed to replace gene */ + failed[mx_fail] = (int) tour1[k]; + indx[mx_fail] = k; + mx_fail++; + } - for (k = 0; k < mx_hold; k++) { - found = 0; + } /* ... for */ - j = 0; - while ( !(found) && (j < num_gene) ) { - if ( (failed[k] == (int) offspring[j]) && (from[j] == DAD) ) { - check_list[offspring[j]]--; - offspring[j] = tour2[indx[k]]; - check_list[tour2[indx[k]]]++; - - found = 1; - failed[k] = -1; - mx_fail--; - } +/* STEP 2 */ - j++; - } + /* see if any genes could not be replaced */ + if (mx_fail > 0) + { + mx_hold = mx_fail; - } /* ... for */ + for (k = 0; k < mx_hold; k++) + { + found = 0; - } /* ... if */ - + j = 0; + while (!(found) && (j < num_gene)) + { -/* STEP 3 */ + if ((failed[k] == (int) offspring[j]) && (from[j] == DAD)) + { + check_list[offspring[j]]--; + offspring[j] = tour2[indx[k]]; + check_list[tour2[indx[k]]]++; - for (k = 1; k <= num_gene; k++) { + found = 1; + failed[k] = -1; + mx_fail--; + } - if (check_list[k] > 1) { - i = 0; + j++; + } - while (i < num_gene) { - if ( (offspring[i] == (Gene) k) && (from[i] == DAD) ) { - j = 1; + } /* ... for */ - while (j <= num_gene) { - if (check_list[j] == 0) { - offspring[i] = (Gene) j; - check_list[k]--; - check_list[j]++; - i = num_gene + 1; - j = i; - } + } /* ... if */ - j++; - } - } /* ... if */ - - i++; - } /* end while */ +/* STEP 3 */ - } - } /* ... for */ - - pfree(failed); - pfree(from); - pfree(indx); - pfree(check_list); + for (k = 1; k <= num_gene; k++) + { + + if (check_list[k] > 1) + { + i = 0; + + while (i < num_gene) + { + if ((offspring[i] == (Gene) k) && (from[i] == DAD)) + { + j = 1; + + while (j <= num_gene) + { + if (check_list[j] == 0) + { + offspring[i] = (Gene) j; + check_list[k]--; + check_list[j]++; + i = num_gene + 1; + j = i; + } + + j++; + } + + } /* ... if */ + + i++; + } /* end while */ + + } + } /* ... for */ + + pfree(failed); + pfree(from); + pfree(indx); + pfree(check_list); } diff --git a/src/backend/optimizer/geqo/geqo_pool.c b/src/backend/optimizer/geqo/geqo_pool.c index 98a1a6e2a06..89c945d4ef4 100644 --- a/src/backend/optimizer/geqo/geqo_pool.c +++ b/src/backend/optimizer/geqo/geqo_pool.c @@ -1,20 +1,20 @@ /*------------------------------------------------------------------------ * * geqo_pool.c-- - * Genetic Algorithm (GA) pool stuff + * Genetic Algorithm (GA) pool stuff * * Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_pool.c,v 1.1 1997/02/19 12:57:31 scrappy Exp $ + * $Id: geqo_pool.c,v 1.2 1997/09/07 04:43:19 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ @@ -44,205 +44,220 @@ #include "optimizer/geqo_recombination.h" -static int compare(void *arg1, void *arg2); +static int compare(void *arg1, void *arg2); /* * alloc-pool-- - * allocates memory for GA pool + * allocates memory for GA pool */ -Pool * +Pool * alloc_pool(int pool_size, int string_length) { - Pool *new_pool; - Chromosome *chromo; - int i; - - /* pool */ - new_pool = (Pool *) palloc (sizeof(Pool)); - new_pool->size = (int) pool_size; - new_pool->string_length = (int) string_length; - - /* all chromosome */ - new_pool->data = (Chromosome *) palloc (pool_size * sizeof(Chromosome)); - - /* all gene */ - chromo = (Chromosome *) new_pool->data; /* vector of all chromos */ - for (i=0; i<pool_size; i++) { - chromo[i].string = palloc((string_length+1)*sizeof(Gene)); + Pool *new_pool; + Chromosome *chromo; + int i; + + /* pool */ + new_pool = (Pool *) palloc(sizeof(Pool)); + new_pool->size = (int) pool_size; + new_pool->string_length = (int) string_length; + + /* all chromosome */ + new_pool->data = (Chromosome *) palloc(pool_size * sizeof(Chromosome)); + + /* all gene */ + chromo = (Chromosome *) new_pool->data; /* vector of all chromos */ + for (i = 0; i < pool_size; i++) + { + chromo[i].string = palloc((string_length + 1) * sizeof(Gene)); } - return (new_pool); + return (new_pool); } /* * free-pool-- - * deallocates memory for GA pool + * deallocates memory for GA pool */ void -free_pool (Pool *pool) +free_pool(Pool * pool) { - Chromosome *chromo; - int i; + Chromosome *chromo; + int i; - /* all gene */ - chromo = (Chromosome *) pool->data; /* vector of all chromos */ - for (i=0; i<pool->size; i++) pfree(chromo[i].string); + /* all gene */ + chromo = (Chromosome *) pool->data; /* vector of all chromos */ + for (i = 0; i < pool->size; i++) + pfree(chromo[i].string); - /* all chromosome */ - pfree (pool->data); + /* all chromosome */ + pfree(pool->data); - /* pool */ - pfree (pool); + /* pool */ + pfree(pool); } /* * random-init-pool-- - * initialize genetic pool + * initialize genetic pool */ void -random_init_pool (Query *root, Pool *pool, int strt, int stp) +random_init_pool(Query * root, Pool * pool, int strt, int stp) { - Chromosome *chromo = (Chromosome *) pool->data; - int i; + Chromosome *chromo = (Chromosome *) pool->data; + int i; - for (i=strt; i<stp; i++) { - init_tour(chromo[i].string, pool->string_length); /* from "geqo_recombination.c" */ + for (i = strt; i < stp; i++) + { + init_tour(chromo[i].string, pool->string_length); /* from + * "geqo_recombination.c" + * */ - pool->data[i].worth = - geqo_eval(root, chromo[i].string, pool->string_length); /* "from geqo_eval.c" */ + pool->data[i].worth = + geqo_eval(root, chromo[i].string, pool->string_length); /* "from geqo_eval.c" */ } } /* * sort-pool-- - * sorts input pool according to worth, from smallest to largest + * sorts input pool according to worth, from smallest to largest * - * maybe you have to change compare() for different ordering ... + * maybe you have to change compare() for different ordering ... */ void -sort_pool(Pool *pool) -{ - pg_qsort(pool->data, pool->size, sizeof(Chromosome), compare); +sort_pool(Pool * pool) +{ + pg_qsort(pool->data, pool->size, sizeof(Chromosome), compare); } /* * compare-- - * static input function for pg_sort + * static input function for pg_sort * - * return values for sort from smallest to largest are prooved! - * don't change them! + * return values for sort from smallest to largest are prooved! + * don't change them! */ static int compare(void *arg1, void *arg2) { - Chromosome chromo1 = *(Chromosome *) arg1; - Chromosome chromo2 = *(Chromosome *) arg2; - - if (chromo1.worth == chromo2.worth) - return(0); - else if (chromo1.worth > chromo2.worth) - return(1); - else - return(-1); + Chromosome chromo1 = *(Chromosome *) arg1; + Chromosome chromo2 = *(Chromosome *) arg2; + + if (chromo1.worth == chromo2.worth) + return (0); + else if (chromo1.worth > chromo2.worth) + return (1); + else + return (-1); } /* alloc_chromo-- - * allocates a chromosome and string space + * allocates a chromosome and string space */ -Chromosome * -alloc_chromo (int string_length) +Chromosome * +alloc_chromo(int string_length) { - Chromosome *chromo; + Chromosome *chromo; + + chromo = (Chromosome *) palloc(sizeof(Chromosome)); + chromo->string = (Gene *) palloc((string_length + 1) * sizeof(Gene)); - chromo = (Chromosome *) palloc (sizeof(Chromosome)); - chromo->string = (Gene *) palloc ((string_length+1)*sizeof(Gene)); - - return (chromo); + return (chromo); } /* free_chromo-- - * deallocates a chromosome and string space + * deallocates a chromosome and string space */ void -free_chromo (Chromosome *chromo) +free_chromo(Chromosome * chromo) { - pfree(chromo->string); - pfree(chromo); + pfree(chromo->string); + pfree(chromo); } /* spread_chromo-- - * inserts a new chromosome into the pool, displacing worst gene in pool - * assumes best->worst = smallest->largest + * inserts a new chromosome into the pool, displacing worst gene in pool + * assumes best->worst = smallest->largest */ void -spread_chromo (Chromosome *chromo, Pool *pool) +spread_chromo(Chromosome * chromo, Pool * pool) { - int top, mid, bot; - int i, index; - Chromosome swap_chromo, tmp_chromo; - - /* new chromo is so bad we can't use it */ - if (chromo->worth > pool->data[pool->size-1].worth) return; - - /* do a binary search to find the index of the new chromo */ - - top = 0; - mid = pool->size/2; - bot = pool->size-1; - index = -1; - - while (index == -1) { - /* these 4 cases find a new location */ - - if (chromo->worth <= pool->data[top].worth) - index = top; - else - if (chromo->worth == pool->data[mid].worth) - index = mid; - else - if (chromo->worth == pool->data[bot].worth) - index = bot; - else - if (bot-top <=1) - index = bot; - - - /* these 2 cases move the search indices since - a new location has not yet been found. */ - - else - if (chromo->worth < pool->data[mid].worth) { - bot = mid; - mid = top + ( (bot-top)/2 ); - } - else { /* (chromo->worth > pool->data[mid].worth) */ - top = mid; - mid = top + ( (bot-top)/2 ); - } - } /* ... while */ - - /* now we have index for chromo */ - - /* move every gene from index on down - one position to make room for chromo */ - - /* copy new gene into pool storage; - always replace worst gene in pool */ - - geqo_copy (&pool->data[pool->size-1], chromo, pool->string_length); - - swap_chromo.string = pool->data[pool->size-1].string; - swap_chromo.worth = pool->data[pool->size-1].worth; - - for (i=index; i<pool->size; i++) { - tmp_chromo.string = pool->data[i].string; - tmp_chromo.worth = pool->data[i].worth; - - pool->data[i].string = swap_chromo.string; - pool->data[i].worth = swap_chromo.worth; - - swap_chromo.string = tmp_chromo.string; - swap_chromo.worth = tmp_chromo.worth; - } + int top, + mid, + bot; + int i, + index; + Chromosome swap_chromo, + tmp_chromo; + + /* new chromo is so bad we can't use it */ + if (chromo->worth > pool->data[pool->size - 1].worth) + return; + + /* do a binary search to find the index of the new chromo */ + + top = 0; + mid = pool->size / 2; + bot = pool->size - 1; + index = -1; + + while (index == -1) + { + /* these 4 cases find a new location */ + + if (chromo->worth <= pool->data[top].worth) + index = top; + else if (chromo->worth == pool->data[mid].worth) + index = mid; + else if (chromo->worth == pool->data[bot].worth) + index = bot; + else if (bot - top <= 1) + index = bot; + + + /* + * these 2 cases move the search indices since a new location has + * not yet been found. + */ + + else if (chromo->worth < pool->data[mid].worth) + { + bot = mid; + mid = top + ((bot - top) / 2); + } + else + { /* (chromo->worth > pool->data[mid].worth) */ + top = mid; + mid = top + ((bot - top) / 2); + } + } /* ... while */ + + /* now we have index for chromo */ + + /* + * move every gene from index on down one position to make room for + * chromo + */ + + /* + * copy new gene into pool storage; always replace worst gene in pool + */ + + geqo_copy(&pool->data[pool->size - 1], chromo, pool->string_length); + + swap_chromo.string = pool->data[pool->size - 1].string; + swap_chromo.worth = pool->data[pool->size - 1].worth; + + for (i = index; i < pool->size; i++) + { + tmp_chromo.string = pool->data[i].string; + tmp_chromo.worth = pool->data[i].worth; + + pool->data[i].string = swap_chromo.string; + pool->data[i].worth = swap_chromo.worth; + + swap_chromo.string = tmp_chromo.string; + swap_chromo.worth = tmp_chromo.worth; + } } diff --git a/src/backend/optimizer/geqo/geqo_px.c b/src/backend/optimizer/geqo/geqo_px.c index f060561b516..71aa2415b55 100644 --- a/src/backend/optimizer/geqo/geqo_px.c +++ b/src/backend/optimizer/geqo/geqo_px.c @@ -2,35 +2,35 @@ * * geqo_px.c-- * -* position crossover [PX] routines; -* PX operator according to Syswerda -* (The Genetic Algorithms Handbook, L Davis, ed) +* position crossover [PX] routines; +* PX operator according to Syswerda +* (The Genetic Algorithms Handbook, L Davis, ed) * -* $Id: geqo_px.c,v 1.1 1997/02/19 12:57:37 scrappy Exp $ +* $Id: geqo_px.c,v 1.2 1997/09/07 04:43:20 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* the px algorithm is adopted from Genitor : */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ #include "postgres.h" @@ -56,61 +56,70 @@ /* px-- * - * position crossover + * position crossover */ void -px(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table) +px(Gene * tour1, Gene * tour2, Gene * offspring, int num_gene, City * city_table) { - int num_positions; - int i, pos, tour2_index, offspring_index; + int num_positions; + int i, + pos, + tour2_index, + offspring_index; - /* initialize city table */ - for (i=1; i<=num_gene; i++) { - city_table[i].used = 0; - } + /* initialize city table */ + for (i = 1; i <= num_gene; i++) + { + city_table[i].used = 0; + } - /* choose random positions that will be inherited directly from parent */ - num_positions = geqo_randint (2*num_gene/3, num_gene/3); + /* choose random positions that will be inherited directly from parent */ + num_positions = geqo_randint(2 * num_gene / 3, num_gene / 3); - /* choose random position */ - for (i=0; i<num_positions; i++) { - pos = geqo_randint (num_gene - 1, 0); + /* choose random position */ + for (i = 0; i < num_positions; i++) + { + pos = geqo_randint(num_gene - 1, 0); - offspring[pos] = tour1[pos]; /* transfer cities to child */ - city_table[(int) tour1[pos]].used = 1; /* mark city used */ - } + offspring[pos] = tour1[pos]; /* transfer cities to child */ + city_table[(int) tour1[pos]].used = 1; /* mark city used */ + } - tour2_index = 0; - offspring_index = 0; + tour2_index = 0; + offspring_index = 0; - /* px main part */ + /* px main part */ - while (offspring_index < num_gene) { + while (offspring_index < num_gene) + { - /* next position in offspring filled */ - if (!city_table[(int) tour1[offspring_index]].used) { + /* next position in offspring filled */ + if (!city_table[(int) tour1[offspring_index]].used) + { - /* next city in tour1 not used */ - if (!city_table[(int) tour2[tour2_index]].used) { + /* next city in tour1 not used */ + if (!city_table[(int) tour2[tour2_index]].used) + { - /* inherit from tour1 */ - offspring[offspring_index] = tour2[tour2_index]; + /* inherit from tour1 */ + offspring[offspring_index] = tour2[tour2_index]; - tour2_index++; - offspring_index++; - } - else { /* next city in tour2 has been used */ - tour2_index++; - } + tour2_index++; + offspring_index++; + } + else + { /* next city in tour2 has been used */ + tour2_index++; + } - } - else { /* next position in offspring is filled */ - offspring_index++; - } + } + else + { /* next position in offspring is filled */ + offspring_index++; + } - } - - } + } +} diff --git a/src/backend/optimizer/geqo/geqo_recombination.c b/src/backend/optimizer/geqo/geqo_recombination.c index df175dcb866..53803079819 100644 --- a/src/backend/optimizer/geqo/geqo_recombination.c +++ b/src/backend/optimizer/geqo/geqo_recombination.c @@ -1,18 +1,18 @@ /*------------------------------------------------------------------------ * * geqo_recombination.c-- -* misc recombination procedures +* misc recombination procedures * -* $Id: geqo_recombination.c,v 1.1 1997/02/19 12:57:42 scrappy Exp $ +* $Id: geqo_recombination.c,v 1.2 1997/09/07 04:43:21 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ @@ -42,65 +42,70 @@ /* * init_tour-- * - * Randomly generates a legal "traveling salesman" tour - * (i.e. where each point is visited only once.) - * Essentially, this routine fills an array with all possible - * points on the tour and randomly chooses the 'next' city from - * this array. When a city is chosen, the array is shortened - * and the procedure repeated. + * Randomly generates a legal "traveling salesman" tour + * (i.e. where each point is visited only once.) + * Essentially, this routine fills an array with all possible + * points on the tour and randomly chooses the 'next' city from + * this array. When a city is chosen, the array is shortened + * and the procedure repeated. * */ void -init_tour(Gene *tour, int num_gene) +init_tour(Gene * tour, int num_gene) { -Gene *tmp; -int remainder; -int next, i; + Gene *tmp; + int remainder; + int next, + i; -tmp = (Gene *) palloc (num_gene*sizeof(Gene)); - -for(i = 0; i < num_gene; i++) { - tmp[i] = (Gene) i+1; /* builds tours "1 - 2 - 3" etc. */ - } + tmp = (Gene *) palloc(num_gene * sizeof(Gene)); -remainder = num_gene - 1; + for (i = 0; i < num_gene; i++) + { + tmp[i] = (Gene) i + 1; /* builds tours "1 - 2 - 3" etc. */ + } -for(i = 0; i < num_gene; i++) { - next = (int) geqo_randint(remainder, 0); /* choose city between 0 and remainder */ - tour[i] = tmp[next]; - tmp[next] = tmp[remainder]; - remainder--; - } + remainder = num_gene - 1; -pfree(tmp); -} + for (i = 0; i < num_gene; i++) + { + next = (int) geqo_randint(remainder, 0); /* choose city between 0 + * and remainder */ + tour[i] = tmp[next]; + tmp[next] = tmp[remainder]; + remainder--; + } + + pfree(tmp); +} /* alloc_city_table-- * - * allocate memory for city table + * allocate memory for city table * */ -City * +City * alloc_city_table(int num_gene) { - City *city_table; + City *city_table; - /* palloc one extra location so that nodes numbered - 1..n can be indexed directly; 0 will not be used */ + /* + * palloc one extra location so that nodes numbered 1..n can be + * indexed directly; 0 will not be used + */ - city_table = (City *) palloc ((num_gene+1)*sizeof(City)); + city_table = (City *) palloc((num_gene + 1) * sizeof(City)); - return (city_table); - } + return (city_table); +} /* free_city_table-- * - * deallocate memory of city table + * deallocate memory of city table * */ - void - free_city_table(City *city_table) - { - pfree(city_table); - } - +void +free_city_table(City * city_table) +{ + pfree(city_table); +} diff --git a/src/backend/optimizer/geqo/geqo_selection.c b/src/backend/optimizer/geqo/geqo_selection.c index 0c6502003bd..820de485fe4 100644 --- a/src/backend/optimizer/geqo/geqo_selection.c +++ b/src/backend/optimizer/geqo/geqo_selection.c @@ -1,36 +1,36 @@ /*------------------------------------------------------------------------- * * geqo_selection.c-- - * linear selection scheme for the genetic query optimizer + * linear selection scheme for the genetic query optimizer * * Copyright (c) 1994, Regents of the University of California * - * $Id: geqo_selection.c,v 1.1 1997/02/19 12:57:46 scrappy Exp $ + * $Id: geqo_selection.c,v 1.2 1997/09/07 04:43:24 momjian Exp $ * *------------------------------------------------------------------------- */ /* contributed by: =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= - * Martin Utesch * Institute of Automatic Control * - = = University of Mining and Technology = - * [email protected] * Freiberg, Germany * + * Martin Utesch * Institute of Automatic Control * + = = University of Mining and Technology = + * [email protected] * Freiberg, Germany * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= */ /* this is adopted from D. Whitley's Genitor algorithm */ /*************************************************************/ -/* */ -/* Copyright (c) 1990 */ -/* Darrell L. Whitley */ -/* Computer Science Department */ -/* Colorado State University */ -/* */ -/* Permission is hereby granted to copy all or any part of */ -/* this program for free distribution. The author's name */ -/* and this copyright notice must be included in any copy. */ -/* */ +/* */ +/* Copyright (c) 1990 */ +/* Darrell L. Whitley */ +/* Computer Science Department */ +/* Colorado State University */ +/* */ +/* Permission is hereby granted to copy all or any part of */ +/* this program for free distribution. The author's name */ +/* and this copyright notice must be included in any copy. */ +/* */ /*************************************************************/ #include <math.h> @@ -55,49 +55,51 @@ #include "optimizer/geqo_copy.h" #include "optimizer/geqo_random.h" -static int linear(int max, double bias); +static int linear(int max, double bias); /* geqo_selection-- * - * according to bias described by input parameters, - * second genes are selected from the pool + * according to bias described by input parameters, + * second genes are selected from the pool */ void -geqo_selection (Chromosome *momma, Chromosome *daddy, Pool *pool, double bias) +geqo_selection(Chromosome * momma, Chromosome * daddy, Pool * pool, double bias) { - int first, second; - - first = (int) linear(pool->size, bias); - second = (int) linear(pool->size, bias); - - if (pool->size > 1) { - while(first==second) - second = (int) linear(pool->size, bias); - } - - geqo_copy (momma, &pool->data[first], pool->string_length); - geqo_copy (daddy, &pool->data[second], pool->string_length); + int first, + second; + + first = (int) linear(pool->size, bias); + second = (int) linear(pool->size, bias); + + if (pool->size > 1) + { + while (first == second) + second = (int) linear(pool->size, bias); + } + + geqo_copy(momma, &pool->data[first], pool->string_length); + geqo_copy(daddy, &pool->data[second], pool->string_length); } /* linear-- - * generates random integer between 0 and input max number - * using input linear bias + * generates random integer between 0 and input max number + * using input linear bias * - * probability distribution function is: f(x) = bias - 2(bias - 1)x - * bias = (prob of first rule) / (prob of middle rule) + * probability distribution function is: f(x) = bias - 2(bias - 1)x + * bias = (prob of first rule) / (prob of middle rule) * */ static int -linear(int pool_size, double bias) /* bias is y-intercept of linear distribution */ +linear(int pool_size, double bias) /* bias is y-intercept of linear + * distribution */ { - double index; /* index between 0 and pop_size */ - double max = (double) pool_size; + double index; /* index between 0 and pop_size */ + double max = (double) pool_size; - index = - max*( bias - sqrt ( (bias*bias) - 4.0*(bias-1.0)*geqo_rand() ) ) - / 2.0 / (bias-1.0); + index = + max * (bias - sqrt((bias * bias) - 4.0 * (bias - 1.0) * geqo_rand())) + / 2.0 / (bias - 1.0); - return((int) index); + return ((int) index); } - diff --git a/src/backend/optimizer/geqo/minspantree.c b/src/backend/optimizer/geqo/minspantree.c index 4e4c2ad11b4..1fcc2569478 100644 --- a/src/backend/optimizer/geqo/minspantree.c +++ b/src/backend/optimizer/geqo/minspantree.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------ * * minspantree.c-- -* routine to sort a join graph which is including cycles +* routine to sort a join graph which is including cycles * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION -* $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/Attic/minspantree.c,v 1.1 1997/02/19 12:57:50 scrappy Exp $ +* $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/Attic/minspantree.c,v 1.2 1997/09/07 04:43:25 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -33,166 +33,181 @@ /* * minspantree-- - * The function minspantree computes the minimum spanning tree - * for a given number of nodes and a given distance function. - * For each pair of nodes found to be connected, a given - * function is called. Nodes are denoted by the integer numbers - * 1 .. number_of_joins, where number_of_joins is the number of nodes. + * The function minspantree computes the minimum spanning tree + * for a given number of nodes and a given distance function. + * For each pair of nodes found to be connected, a given + * function is called. Nodes are denoted by the integer numbers + * 1 .. number_of_joins, where number_of_joins is the number of nodes. */ void -minspantree(Query *root, List *join_rels, Rel *garel) +minspantree(Query * root, List * join_rels, Rel * garel) { - int number_of_rels = length(root->base_relation_list_); - int number_of_joins = length(join_rels); - int *connectto; - /* connectto[i] = 0, if node i is already connected */ - /* to the tree, otherwise connectto[i] is the node */ - /* nearest to i, which is already connected. */ - - Cost *disttoconnect; /* disttoconnect[i]: distance between i and connectto[i] */ - - Cost dist, /* temporary */ - mindist; /* minimal distance between connected and unconnected node */ - - Cost mstlength = 0.0; /* the total length of the minimum spanning tree */ - - int count; - int n, /* newly attached node */ - nextn, /* next node to be attached */ - tempn; - - int i, id1, id2; - List *r = NIL; - Rel *joinrel = NULL; - Rel **tmprel_array; - - - /* allocate memory for matrix tmprel_array[x][y] */ - tmprel_array = (Rel **) palloc((number_of_rels+1)*sizeof(Rel *)); - for (i=0; i<=number_of_rels; i++) - (tmprel_array[i] = (Rel *) palloc ((number_of_rels+1)*sizeof(Rel))); - - /* read relations of join-relations into tmprel_array */ - - foreach(r, join_rels) { - joinrel = (Rel *)lfirst(r); - id1 = (int)lfirst(joinrel->relids); - id2 = (int)lsecond(joinrel->relids); - - if (id1 > id2) { - tmprel_array[id2][id1] = *(Rel *)joinrel; - } - else { - tmprel_array[id1][id2] = *(Rel *)joinrel; /* ever reached? */ - } - } - - /* Trivial special cases handled first */ - /* garel is global in "tsp.h" */ - - if (number_of_joins <= 2) - { - i=1; - foreach(r, join_rels) { - garel[i] = *(Rel *)lfirst(r); - i++; - } - } - - - else if (number_of_joins == 3) - { - Rel *rel12 = (Rel *) &tmprel_array[1][2]; - Rel *rel13 = (Rel *) &tmprel_array[1][3]; - Rel *rel23 = (Rel *) &tmprel_array[2][3]; - if (rel12->cheapestpath->path_cost > rel13->cheapestpath->path_cost) - { - garel[1] = tmprel_array[1][3]; - if (rel12->cheapestpath->path_cost > rel23->cheapestpath->path_cost) + int number_of_rels = length(root->base_relation_list_); + int number_of_joins = length(join_rels); + int *connectto; + + /* connectto[i] = 0, if node i is already connected */ + /* to the tree, otherwise connectto[i] is the node */ + /* nearest to i, which is already connected. */ + + Cost *disttoconnect; /* disttoconnect[i]: distance + * between i and connectto[i] */ + + Cost dist, /* temporary */ + mindist; /* minimal distance between connected and + * unconnected node */ + + Cost mstlength = 0.0; /* the total length of the minimum + * spanning tree */ + + int count; + int n, /* newly attached node */ + nextn, /* next node to be attached */ + tempn; + + int i, + id1, + id2; + List *r = NIL; + Rel *joinrel = NULL; + Rel **tmprel_array; + + + /* allocate memory for matrix tmprel_array[x][y] */ + tmprel_array = (Rel **) palloc((number_of_rels + 1) * sizeof(Rel *)); + for (i = 0; i <= number_of_rels; i++) + (tmprel_array[i] = (Rel *) palloc((number_of_rels + 1) * sizeof(Rel))); + + /* read relations of join-relations into tmprel_array */ + + foreach(r, join_rels) { - garel[2] = tmprel_array[2][3]; + joinrel = (Rel *) lfirst(r); + id1 = (int) lfirst(joinrel->relids); + id2 = (int) lsecond(joinrel->relids); + + if (id1 > id2) + { + tmprel_array[id2][id1] = *(Rel *) joinrel; + } + else + { + tmprel_array[id1][id2] = *(Rel *) joinrel; /* ever reached? */ + } } - else + + /* Trivial special cases handled first */ + /* garel is global in "tsp.h" */ + + if (number_of_joins <= 2) { - garel[2] = tmprel_array[1][2]; + i = 1; + foreach(r, join_rels) + { + garel[i] = *(Rel *) lfirst(r); + i++; + } } - } - else - { - garel[1] = tmprel_array[1][2]; - if (rel13->cheapestpath->path_cost > rel23->cheapestpath->path_cost) + + + else if (number_of_joins == 3) { - garel[2] = tmprel_array[2][3]; + Rel *rel12 = (Rel *) & tmprel_array[1][2]; + Rel *rel13 = (Rel *) & tmprel_array[1][3]; + Rel *rel23 = (Rel *) & tmprel_array[2][3]; + + if (rel12->cheapestpath->path_cost > rel13->cheapestpath->path_cost) + { + garel[1] = tmprel_array[1][3]; + if (rel12->cheapestpath->path_cost > rel23->cheapestpath->path_cost) + { + garel[2] = tmprel_array[2][3]; + } + else + { + garel[2] = tmprel_array[1][2]; + } + } + else + { + garel[1] = tmprel_array[1][2]; + if (rel13->cheapestpath->path_cost > rel23->cheapestpath->path_cost) + { + garel[2] = tmprel_array[2][3]; + } + else + { + garel[2] = tmprel_array[1][3]; + } + } } + + + /* now the general case */ else { - garel[2] = tmprel_array[1][3]; - } - } - } - - - /* now the general case */ - else - { - connectto = (int *) palloc((number_of_rels+1)*sizeof(int)); - disttoconnect = (Cost *) palloc((number_of_rels+1)*sizeof(Cost)); - - nextn = 2; - for (tempn = 2; tempn <= number_of_rels; tempn++ ) - { - connectto[tempn] = 1; - disttoconnect[tempn] = (Cost) MAXFLOAT; - } - - joinrel = NULL; - n = 1; - i = 1; - for (count = 2; count <= number_of_rels; count++ ) - { - connectto[n] = 0; - mindist = (Cost) MAXFLOAT; - for (tempn = 2; tempn <= number_of_rels; tempn++ ) - { - if (connectto[tempn] != 0) - { - if (n > tempn) { - joinrel = (Rel *) &tmprel_array[tempn][n]; - } - else { - joinrel = (Rel *) &tmprel_array[n][tempn]; - } - dist = joinrel->cheapestpath->path_cost; - - if (dist < disttoconnect[tempn]) - { - disttoconnect[tempn] = dist; - connectto[tempn] = n; - } - if (disttoconnect[tempn] < mindist) - { - mindist = disttoconnect[tempn]; - nextn = tempn; + connectto = (int *) palloc((number_of_rels + 1) * sizeof(int)); + disttoconnect = (Cost *) palloc((number_of_rels + 1) * sizeof(Cost)); + + nextn = 2; + for (tempn = 2; tempn <= number_of_rels; tempn++) + { + connectto[tempn] = 1; + disttoconnect[tempn] = (Cost) MAXFLOAT; + } + + joinrel = NULL; + n = 1; + i = 1; + for (count = 2; count <= number_of_rels; count++) + { + connectto[n] = 0; + mindist = (Cost) MAXFLOAT; + for (tempn = 2; tempn <= number_of_rels; tempn++) + { + if (connectto[tempn] != 0) + { + if (n > tempn) + { + joinrel = (Rel *) & tmprel_array[tempn][n]; + } + else + { + joinrel = (Rel *) & tmprel_array[n][tempn]; + } + dist = joinrel->cheapestpath->path_cost; + + if (dist < disttoconnect[tempn]) + { + disttoconnect[tempn] = dist; + connectto[tempn] = n; + } + if (disttoconnect[tempn] < mindist) + { + mindist = disttoconnect[tempn]; + nextn = tempn; + } + } + } + n = nextn; + if (n > connectto[n]) + { + garel[i] = tmprel_array[connectto[n]][n]; + } + else + { + garel[i] = tmprel_array[n][connectto[n]]; + } + i++; + } + + pfree(connectto); + pfree(disttoconnect); + } - } - } - n = nextn; - if (n > connectto[n]) { - garel[i] = tmprel_array[connectto[n]][n]; - } - else { - garel[i] = tmprel_array[n][connectto[n]]; - } - i++; - } - - pfree(connectto); - pfree(disttoconnect); - - } - - for (i=0; i<=number_of_rels; i++) pfree(tmprel_array[i]); - pfree(tmprel_array); -} + for (i = 0; i <= number_of_rels; i++) + pfree(tmprel_array[i]); + pfree(tmprel_array); +} diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index d27b31cfbd7..7c4576d6f02 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * allpaths.c-- - * Routines to find possible search paths for processing a query + * Routines to find possible search paths for processing a query * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.10 1997/06/10 07:55:45 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.11 1997/09/07 04:43:27 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,226 +34,245 @@ #include "optimizer/geqo.h" #ifdef GEQO -bool _use_geqo_ = true; +bool _use_geqo_ = true; + #else -bool _use_geqo_ = false; +bool _use_geqo_ = false; + #endif -int32 _use_geqo_rels_ = GEQO_RELS; +int32 _use_geqo_rels_ = GEQO_RELS; -static void find_rel_paths(Query *root, List *rels); -static List *find_join_paths(Query *root, List *outer_rels, int levels_left); +static void find_rel_paths(Query * root, List * rels); +static List *find_join_paths(Query * root, List * outer_rels, int levels_left); -/* +/* * find-paths-- - * Finds all possible access paths for executing a query, returning the - * top level list of relation entries. - * + * Finds all possible access paths for executing a query, returning the + * top level list of relation entries. + * * 'rels' is the list of single relation entries appearing in the query */ -List * -find_paths(Query *root, List *rels) +List * +find_paths(Query * root, List * rels) { - int levels_left; - - /* - * Set the number of join (not nesting) levels yet to be processed. - */ - levels_left = length(rels); - - if (levels_left <= 0) - return NIL; - - /* - * Find the base relation paths. - */ - find_rel_paths(root, rels); - - if (levels_left <= 1) { + int levels_left; + /* - * Unsorted single relation, no more processing is required. + * Set the number of join (not nesting) levels yet to be processed. */ - return (rels); - }else { - /* - * this means that joins or sorts are required. - * set selectivities of clauses that have not been set - * by an index. + levels_left = length(rels); + + if (levels_left <= 0) + return NIL; + + /* + * Find the base relation paths. */ - set_rest_relselec(root, rels); + find_rel_paths(root, rels); + + if (levels_left <= 1) + { - return(find_join_paths(root, rels, levels_left-1)); - } + /* + * Unsorted single relation, no more processing is required. + */ + return (rels); + } + else + { + + /* + * this means that joins or sorts are required. set selectivities + * of clauses that have not been set by an index. + */ + set_rest_relselec(root, rels); + + return (find_join_paths(root, rels, levels_left - 1)); + } } -/* +/* * find-rel-paths-- - * Finds all paths available for scanning each relation entry in - * 'rels'. Sequential scan and any available indices are considered - * if possible(indices are not considered for lower nesting levels). - * All unique paths are attached to the relation's 'pathlist' field. - * - * MODIFIES: rels + * Finds all paths available for scanning each relation entry in + * 'rels'. Sequential scan and any available indices are considered + * if possible(indices are not considered for lower nesting levels). + * All unique paths are attached to the relation's 'pathlist' field. + * + * MODIFIES: rels */ static void -find_rel_paths(Query *root, List *rels) +find_rel_paths(Query * root, List * rels) { - List *temp; - Rel *rel; - List *lastpath; - - foreach(temp, rels) { - List *sequential_scan_list; - List *rel_index_scan_list; - List *or_index_scan_list; - - rel = (Rel *)lfirst(temp); - sequential_scan_list = lcons(create_seqscan_path(rel), - NIL); - - rel_index_scan_list = - find_index_paths(root, - rel, - find_relation_indices(root,rel), - rel->clauseinfo, - rel->joininfo); - - or_index_scan_list = - create_or_index_paths(root, rel, rel->clauseinfo); - - rel->pathlist = add_pathlist(rel, - sequential_scan_list, - append(rel_index_scan_list, - or_index_scan_list)); - - /* The unordered path is always the last in the list. - * If it is not the cheapest path, prune it. - */ - lastpath = rel->pathlist; - while(lnext(lastpath)!=NIL) - lastpath=lnext(lastpath); - prune_rel_path(rel, (Path*)lfirst(lastpath)); - /* - * if there is a qualification of sequential scan the selec. - * value is not set -- so set it explicitly -- Sunita - */ - set_rest_selec(root, rel->clauseinfo); - rel->size = compute_rel_size(rel); - rel->width = compute_rel_width(rel); - } - return; + List *temp; + Rel *rel; + List *lastpath; + + foreach(temp, rels) + { + List *sequential_scan_list; + List *rel_index_scan_list; + List *or_index_scan_list; + + rel = (Rel *) lfirst(temp); + sequential_scan_list = lcons(create_seqscan_path(rel), + NIL); + + rel_index_scan_list = + find_index_paths(root, + rel, + find_relation_indices(root, rel), + rel->clauseinfo, + rel->joininfo); + + or_index_scan_list = + create_or_index_paths(root, rel, rel->clauseinfo); + + rel->pathlist = add_pathlist(rel, + sequential_scan_list, + append(rel_index_scan_list, + or_index_scan_list)); + + /* + * The unordered path is always the last in the list. If it is not + * the cheapest path, prune it. + */ + lastpath = rel->pathlist; + while (lnext(lastpath) != NIL) + lastpath = lnext(lastpath); + prune_rel_path(rel, (Path *) lfirst(lastpath)); + + /* + * if there is a qualification of sequential scan the selec. value + * is not set -- so set it explicitly -- Sunita + */ + set_rest_selec(root, rel->clauseinfo); + rel->size = compute_rel_size(rel); + rel->width = compute_rel_width(rel); + } + return; } -/* +/* * find-join-paths-- - * Find all possible joinpaths for a query by successively finding ways - * to join single relations into join relations. + * Find all possible joinpaths for a query by successively finding ways + * to join single relations into join relations. * - * if BushyPlanFlag is set, bushy tree plans will be generated: - * Find all possible joinpaths(bushy trees) for a query by systematically - * finding ways to join relations(both original and derived) together. - * - * 'outer-rels' is the current list of relations for which join paths - * are to be found, i.e., he current list of relations that - * have already been derived. + * if BushyPlanFlag is set, bushy tree plans will be generated: + * Find all possible joinpaths(bushy trees) for a query by systematically + * finding ways to join relations(both original and derived) together. + * + * 'outer-rels' is the current list of relations for which join paths + * are to be found, i.e., he current list of relations that + * have already been derived. * 'levels-left' is the current join level being processed, where '1' is - * the "last" level - * + * the "last" level + * * Returns the final level of join relations, i.e., the relation that is * the result of joining all the original relations togehter. */ -static List * -find_join_paths(Query *root, List *outer_rels, int levels_left) +static List * +find_join_paths(Query * root, List * outer_rels, int levels_left) { - List *x; - List *new_rels; - Rel *rel; - - /******************************************* - * genetic query optimizer entry point * - * <[email protected]> * - *******************************************/ - - if ( (_use_geqo_) && length(root->base_relation_list_) >= _use_geqo_rels_ ) - return lcons(geqo(root), NIL); /* returns *one* Rel, so lcons it */ - - /******************************************* - * rest will be deprecated in case of GEQO * - *******************************************/ - - /* - * Determine all possible pairs of relations to be joined at this level. - * Determine paths for joining these relation pairs and modify 'new-rels' - * accordingly, then eliminate redundant join relations. - */ - new_rels = find_join_rels(root, outer_rels); - - find_all_join_paths(root, new_rels); - - new_rels = prune_joinrels(new_rels); - -#if 0 - /* - ** for each expensive predicate in each path in each distinct rel, - ** consider doing pullup -- JMH - */ - if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF) - foreach(x, new_rels) - xfunc_trypullup((Rel*)lfirst(x)); -#endif + List *x; + List *new_rels; + Rel *rel; - prune_rel_paths(new_rels); + /******************************************* + * genetic query optimizer entry point * + * <[email protected]> * + *******************************************/ + + if ((_use_geqo_) && length(root->base_relation_list_) >= _use_geqo_rels_) + return lcons(geqo(root), NIL); /* returns *one* Rel, so lcons it */ + + /******************************************* + * rest will be deprecated in case of GEQO * + *******************************************/ - if(BushyPlanFlag) { /* - * In case of bushy trees - * if there is still a join between a join relation and another - * relation, add a new joininfo that involves the join relation - * to the joininfo list of the other relation + * Determine all possible pairs of relations to be joined at this + * level. Determine paths for joining these relation pairs and modify + * 'new-rels' accordingly, then eliminate redundant join relations. */ - add_new_joininfos(root, new_rels,outer_rels); - } + new_rels = find_join_rels(root, outer_rels); + + find_all_join_paths(root, new_rels); + + new_rels = prune_joinrels(new_rels); - foreach(x, new_rels) { - rel = (Rel*)lfirst(x); - if ( rel->size <= 0 ) - rel->size = compute_rel_size(rel); - rel->width = compute_rel_width(rel); +#if 0 + + /* + * * for each expensive predicate in each path in each distinct rel, * + * consider doing pullup -- JMH + */ + if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF) + foreach(x, new_rels) + xfunc_trypullup((Rel *) lfirst(x)); +#endif + + prune_rel_paths(new_rels); + + if (BushyPlanFlag) + { + + /* + * In case of bushy trees if there is still a join between a join + * relation and another relation, add a new joininfo that involves + * the join relation to the joininfo list of the other relation + */ + add_new_joininfos(root, new_rels, outer_rels); + } + + foreach(x, new_rels) + { + rel = (Rel *) lfirst(x); + if (rel->size <= 0) + rel->size = compute_rel_size(rel); + rel->width = compute_rel_width(rel); /*#define OPTIMIZER_DEBUG*/ #ifdef OPTIMIZER_DEBUG - printf("levels left: %d\n", levels_left); - debug_print_rel(root, rel); -#endif - } - - if(BushyPlanFlag) { - /* - * prune rels that have been completely incorporated into - * new join rels - */ - outer_rels = prune_oldrels(outer_rels); - /* - * merge join rels if then contain the same list of base rels - */ - outer_rels = merge_joinrels(new_rels,outer_rels); - root->join_relation_list_ = outer_rels; - } - else { - root->join_relation_list_ = new_rels; - } - - if(levels_left == 1) { - if(BushyPlanFlag) - return(final_join_rels(outer_rels)); + printf("levels left: %d\n", levels_left); + debug_print_rel(root, rel); +#endif + } + + if (BushyPlanFlag) + { + + /* + * prune rels that have been completely incorporated into new join + * rels + */ + outer_rels = prune_oldrels(outer_rels); + + /* + * merge join rels if then contain the same list of base rels + */ + outer_rels = merge_joinrels(new_rels, outer_rels); + root->join_relation_list_ = outer_rels; + } else - return(new_rels); - } else { - if(BushyPlanFlag) - return(find_join_paths(root, outer_rels, levels_left - 1)); + { + root->join_relation_list_ = new_rels; + } + + if (levels_left == 1) + { + if (BushyPlanFlag) + return (final_join_rels(outer_rels)); + else + return (new_rels); + } else - return(find_join_paths(root, new_rels, levels_left - 1)); - } + { + if (BushyPlanFlag) + return (find_join_paths(root, outer_rels, levels_left - 1)); + else + return (find_join_paths(root, new_rels, levels_left - 1)); + } } /***************************************************************************** @@ -262,115 +281,147 @@ find_join_paths(Query *root, List *outer_rels, int levels_left) #ifdef OPTIMIZER_DEBUG static void -print_joinclauses(Query *root, List *clauses) +print_joinclauses(Query * root, List * clauses) { - List *l; - extern void print_expr(Node *expr, List *rtable); /* in print.c */ + List *l; + extern void print_expr(Node * expr, List * rtable); /* in print.c */ - foreach(l, clauses) { - CInfo *c = lfirst(l); + foreach(l, clauses) + { + CInfo *c = lfirst(l); - print_expr((Node*)c->clause, root->rtable); - if (lnext(l)) printf(" "); - } + print_expr((Node *) c->clause, root->rtable); + if (lnext(l)) + printf(" "); + } } static void -print_path(Query *root, Path *path, int indent) +print_path(Query * root, Path * path, int indent) { - char *ptype = NULL; - JoinPath *jp; - bool join = false; - int i; - - for(i=0; i < indent; i++) - printf("\t"); - - switch(nodeTag(path)) { - case T_Path: - ptype = "SeqScan"; join=false; break; - case T_IndexPath: - ptype = "IdxScan"; join=false; break; - case T_JoinPath: - ptype = "Nestloop"; join=true; break; - case T_MergePath: - ptype = "MergeJoin"; join=true; break; - case T_HashPath: - ptype = "HashJoin"; join=true; break; - default: - break; - } - if (join) { - int size = path->parent->size; - jp = (JoinPath*)path; - printf("%s size=%d cost=%f\n", ptype, size, path->path_cost); - switch(nodeTag(path)) { + char *ptype = NULL; + JoinPath *jp; + bool join = false; + int i; + + for (i = 0; i < indent; i++) + printf("\t"); + + switch (nodeTag(path)) + { + case T_Path: + ptype = "SeqScan"; + join = false; + break; + case T_IndexPath: + ptype = "IdxScan"; + join = false; + break; + case T_JoinPath: + ptype = "Nestloop"; + join = true; + break; case T_MergePath: + ptype = "MergeJoin"; + join = true; + break; case T_HashPath: - for(i=0; i < indent+1; i++) - printf("\t"); - printf(" clauses=("); - print_joinclauses(root, - ((JoinPath*)path)->pathclauseinfo); - printf(")\n"); - - if (nodeTag(path)==T_MergePath) { - MergePath *mp = (MergePath*)path; - if (mp->outersortkeys || mp->innersortkeys) { - for(i=0; i < indent+1; i++) - printf("\t"); - printf(" sortouter=%d sortinner=%d\n", - ((mp->outersortkeys)?1:0), - ((mp->innersortkeys)?1:0)); - } - } - break; + ptype = "HashJoin"; + join = true; + break; default: - break; + break; } - print_path(root, jp->outerjoinpath, indent+1); - print_path(root, jp->innerjoinpath, indent+1); - } else { - int size = path->parent->size; - int relid = lfirsti(path->parent->relids); - printf("%s(%d) size=%d cost=%f", - ptype, relid, size, path->path_cost); - - if (nodeTag(path)==T_IndexPath) { - List *k, *l; - - printf(" keys="); - foreach (k, path->keys) { - printf("("); - foreach (l, lfirst(k)) { - Var *var = lfirst(l); - printf("%d.%d", var->varnoold, var->varoattno); - if (lnext(l)) printf(", "); + if (join) + { + int size = path->parent->size; + + jp = (JoinPath *) path; + printf("%s size=%d cost=%f\n", ptype, size, path->path_cost); + switch (nodeTag(path)) + { + case T_MergePath: + case T_HashPath: + for (i = 0; i < indent + 1; i++) + printf("\t"); + printf(" clauses=("); + print_joinclauses(root, + ((JoinPath *) path)->pathclauseinfo); + printf(")\n"); + + if (nodeTag(path) == T_MergePath) + { + MergePath *mp = (MergePath *) path; + + if (mp->outersortkeys || mp->innersortkeys) + { + for (i = 0; i < indent + 1; i++) + printf("\t"); + printf(" sortouter=%d sortinner=%d\n", + ((mp->outersortkeys) ? 1 : 0), + ((mp->innersortkeys) ? 1 : 0)); + } + } + break; + default: + break; } - printf(")"); - if (lnext(k)) printf(", "); - } + print_path(root, jp->outerjoinpath, indent + 1); + print_path(root, jp->innerjoinpath, indent + 1); + } + else + { + int size = path->parent->size; + int relid = lfirsti(path->parent->relids); + + printf("%s(%d) size=%d cost=%f", + ptype, relid, size, path->path_cost); + + if (nodeTag(path) == T_IndexPath) + { + List *k, + *l; + + printf(" keys="); + foreach(k, path->keys) + { + printf("("); + foreach(l, lfirst(k)) + { + Var *var = lfirst(l); + + printf("%d.%d", var->varnoold, var->varoattno); + if (lnext(l)) + printf(", "); + } + printf(")"); + if (lnext(k)) + printf(", "); + } + } + printf("\n"); } - printf("\n"); - } } -static void -debug_print_rel(Query *root, Rel *rel) +static void +debug_print_rel(Query * root, Rel * rel) { - List *l; - - printf("("); - foreach(l, rel->relids) { - printf("%d ", lfirsti(l)); - } - printf("): size=%d width=%d\n", rel->size, rel->width); - - printf("\tpath list:\n"); - foreach (l, rel->pathlist) { - print_path(root, lfirst(l), 1); - } - printf("\tcheapest path:\n"); - print_path(root, rel->cheapestpath, 1); + List *l; + + printf("("); + foreach(l, rel->relids) + { + printf("%d ", lfirsti(l)); + } + printf("): size=%d width=%d\n", rel->size, rel->width); + + printf("\tpath list:\n"); + foreach(l, rel->pathlist) + { + print_path(root, lfirst(l), 1); + } + printf("\tcheapest path:\n"); + print_path(root, rel->cheapestpath, 1); } -#endif /* OPTIMIZER_DEBUG */ + +#endif /* OPTIMIZER_DEBUG */ diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 634e1130794..0ce580754e3 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * clausesel.c-- - * Routines to compute and set clause selectivities + * Routines to compute and set clause selectivities * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.2 1997/09/07 04:43:31 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,7 +23,7 @@ #include "optimizer/cost.h" #include "optimizer/plancat.h" -#include "parser/parsetree.h" /* for getrelid() */ +#include "parser/parsetree.h" /* for getrelid() */ #include "catalog/pg_proc.h" #include "catalog/pg_operator.h" @@ -31,301 +31,353 @@ #include "utils/elog.h" #include "utils/lsyscache.h" -static Cost compute_selec(Query *root, List *clauses, List *or_selectivities); +static Cost compute_selec(Query * root, List * clauses, List * or_selectivities); /**************************************************************************** - * ROUTINES TO SET CLAUSE SELECTIVITIES + * ROUTINES TO SET CLAUSE SELECTIVITIES ****************************************************************************/ -/* +/* * set_clause_selectivities - - * Sets the selectivity field for each of clause in 'clauseinfo-list' - * to 'new-selectivity'. If the selectivity has already been set, reset - * it only if the new one is better. - * + * Sets the selectivity field for each of clause in 'clauseinfo-list' + * to 'new-selectivity'. If the selectivity has already been set, reset + * it only if the new one is better. + * * Returns nothing of interest. * */ void -set_clause_selectivities(List *clauseinfo_list, Cost new_selectivity) +set_clause_selectivities(List * clauseinfo_list, Cost new_selectivity) { - List *temp; - CInfo *clausenode; - Cost cost_clause; - - foreach (temp,clauseinfo_list) { - clausenode = (CInfo*)lfirst(temp); - cost_clause = clausenode->selectivity; - if ( FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) { - clausenode->selectivity = new_selectivity; + List *temp; + CInfo *clausenode; + Cost cost_clause; + + foreach(temp, clauseinfo_list) + { + clausenode = (CInfo *) lfirst(temp); + cost_clause = clausenode->selectivity; + if (FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) + { + clausenode->selectivity = new_selectivity; + } } - } } -/* +/* * product_selec - - * Multiplies the selectivities of each clause in 'clauseinfo-list'. - * + * Multiplies the selectivities of each clause in 'clauseinfo-list'. + * * Returns a flonum corresponding to the selectivity of 'clauseinfo-list'. */ Cost -product_selec(List *clauseinfo_list) +product_selec(List * clauseinfo_list) { - Cost result = 1.0; - if (clauseinfo_list!=NIL) { - List *xclausenode = NIL; - Cost temp; - - foreach(xclausenode,clauseinfo_list) { - temp = ((CInfo *)lfirst(xclausenode))->selectivity; - result = result * temp; + Cost result = 1.0; + + if (clauseinfo_list != NIL) + { + List *xclausenode = NIL; + Cost temp; + + foreach(xclausenode, clauseinfo_list) + { + temp = ((CInfo *) lfirst(xclausenode))->selectivity; + result = result * temp; + } } - } - return(result); + return (result); } -/* +/* * set_rest_relselec - - * Scans through clauses on each relation and assigns a selectivity to - * those clauses that haven't been assigned a selectivity by an index. - * + * Scans through clauses on each relation and assigns a selectivity to + * those clauses that haven't been assigned a selectivity by an index. + * * Returns nothing of interest. * MODIFIES: selectivities of the various rel's clauseinfo - * slots. + * slots. */ void -set_rest_relselec(Query *root, List *rel_list) +set_rest_relselec(Query * root, List * rel_list) { - Rel *rel; - List *x; + Rel *rel; + List *x; - foreach (x,rel_list) { - rel = (Rel*)lfirst(x); - set_rest_selec(root, rel->clauseinfo); - } + foreach(x, rel_list) + { + rel = (Rel *) lfirst(x); + set_rest_selec(root, rel->clauseinfo); + } } -/* +/* * set_rest_selec - - * Sets the selectivity fields for those clauses within a single - * relation's 'clauseinfo-list' that haven't already been set. - * + * Sets the selectivity fields for those clauses within a single + * relation's 'clauseinfo-list' that haven't already been set. + * * Returns nothing of interest. - * + * */ void -set_rest_selec(Query *root, List *clauseinfo_list) +set_rest_selec(Query * root, List * clauseinfo_list) { - List *temp = NIL; - CInfo *clausenode = (CInfo*)NULL; - Cost cost_clause; - - foreach (temp,clauseinfo_list) { - clausenode = (CInfo*)lfirst(temp); - cost_clause = clausenode->selectivity; + List *temp = NIL; + CInfo *clausenode = (CInfo *) NULL; + Cost cost_clause; - /* - * Check to see if the selectivity of this clause or any 'or' - * subclauses (if any) haven't been set yet. - */ - if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) { - clausenode->selectivity = - compute_clause_selec(root, - (Node*)clausenode->clause, - lcons(makeFloat(cost_clause), NIL)); + foreach(temp, clauseinfo_list) + { + clausenode = (CInfo *) lfirst(temp); + cost_clause = clausenode->selectivity; + + /* + * Check to see if the selectivity of this clause or any 'or' + * subclauses (if any) haven't been set yet. + */ + if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) + { + clausenode->selectivity = + compute_clause_selec(root, + (Node *) clausenode->clause, + lcons(makeFloat(cost_clause), NIL)); + } } - } } /**************************************************************************** - * ROUTINES TO COMPUTE SELECTIVITIES + * ROUTINES TO COMPUTE SELECTIVITIES ****************************************************************************/ -/* +/* * compute_clause_selec - - * Given a clause, this routine will compute the selectivity of the - * clause by calling 'compute_selec' with the appropriate parameters - * and possibly use that return value to compute the real selectivity - * of a clause. - * + * Given a clause, this routine will compute the selectivity of the + * clause by calling 'compute_selec' with the appropriate parameters + * and possibly use that return value to compute the real selectivity + * of a clause. + * * 'or-selectivities' are selectivities that have already been assigned - * to subclauses of an 'or' clause. - * + * to subclauses of an 'or' clause. + * * Returns a flonum corresponding to the clause selectivity. - * + * */ Cost -compute_clause_selec(Query *root, Node *clause, List *or_selectivities) +compute_clause_selec(Query * root, Node * clause, List * or_selectivities) { - if (!is_opclause (clause)) { - /* if it's not an operator clause, then it is a boolean clause -jolly*/ - /* - * Boolean variables get a selectivity of 1/2. - */ - return(0.1); - } else if (not_clause (clause)) { - /* - * 'not' gets "1.0 - selectivity-of-inner-clause". - */ - return (1.000000 - compute_selec(root, - lcons(get_notclausearg((Expr*)clause), - NIL), - or_selectivities)); - } else if (or_clause(clause)) { - /* - * Both 'or' and 'and' clauses are evaluated as described in - * (compute_selec). - */ - return (compute_selec(root, - ((Expr*)clause)->args, or_selectivities)); - } else { - return(compute_selec(root, - lcons(clause,NIL),or_selectivities)); - } + if (!is_opclause(clause)) + { + + /* + * if it's not an operator clause, then it is a boolean clause + * -jolly + */ + + /* + * Boolean variables get a selectivity of 1/2. + */ + return (0.1); + } + else if (not_clause(clause)) + { + + /* + * 'not' gets "1.0 - selectivity-of-inner-clause". + */ + return (1.000000 - compute_selec(root, + lcons(get_notclausearg((Expr *) clause), + NIL), + or_selectivities)); + } + else if (or_clause(clause)) + { + + /* + * Both 'or' and 'and' clauses are evaluated as described in + * (compute_selec). + */ + return (compute_selec(root, + ((Expr *) clause)->args, or_selectivities)); + } + else + { + return (compute_selec(root, + lcons(clause, NIL), or_selectivities)); + } } -/* - * compute_selec - - * Computes the selectivity of a clause. - * - * If there is more than one clause in the argument 'clauses', then the - * desired selectivity is that of an 'or' clause. Selectivities for an - * 'or' clause such as (OR a b) are computed by finding the selectivity - * of a (s1) and b (s2) and computing s1+s2 - s1*s2. - * - * In addition, if the clause is an 'or' clause, individual selectivities - * may have already been assigned by indices to subclauses. These values - * are contained in the list 'or-selectivities'. - * +/* + * compute_selec - + * Computes the selectivity of a clause. + * + * If there is more than one clause in the argument 'clauses', then the + * desired selectivity is that of an 'or' clause. Selectivities for an + * 'or' clause such as (OR a b) are computed by finding the selectivity + * of a (s1) and b (s2) and computing s1+s2 - s1*s2. + * + * In addition, if the clause is an 'or' clause, individual selectivities + * may have already been assigned by indices to subclauses. These values + * are contained in the list 'or-selectivities'. + * * Returns the clause selectivity as a flonum. - * + * */ -static Cost -compute_selec(Query *root, List *clauses, List *or_selectivities) +static Cost +compute_selec(Query * root, List * clauses, List * or_selectivities) { - Cost s1 = 0; - List *clause = lfirst(clauses); - - if (clauses==NULL) { - s1 = 1.0; - } else if (IsA(clause,Param)) { - /* XXX How're we handling this before?? -ay */ - s1 = 1.0; - } else if (IsA(clause,Const)) { - s1 = ((bool) ((Const*) clause)->constvalue) ? 1.0 : 0.0; - } else if (IsA(clause,Var)) { - Oid relid = getrelid(((Var*)clause)->varno, - root->rtable); + Cost s1 = 0; + List *clause = lfirst(clauses); - /* - * we have a bool Var. This is exactly equivalent to the clause: - * reln.attribute = 't' - * so we compute the selectivity as if that is what we have. The - * magic #define constants are a hack. I didn't want to have to - * do system cache look ups to find out all of that info. - */ + if (clauses == NULL) + { + s1 = 1.0; + } + else if (IsA(clause, Param)) + { + /* XXX How're we handling this before?? -ay */ + s1 = 1.0; + } + else if (IsA(clause, Const)) + { + s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0; + } + else if (IsA(clause, Var)) + { + Oid relid = getrelid(((Var *) clause)->varno, + root->rtable); - s1 = restriction_selectivity(EqualSelectivityProcedure, - BooleanEqualOperator, - relid, - ((Var*)clause)->varoattno, - "t", - _SELEC_CONSTANT_RIGHT_); - } else if (or_selectivities) { - /* If s1 has already been assigned by an index, use that value. */ - List *this_sel = lfirst(or_selectivities); - - s1 = floatVal(this_sel); - } else if (is_funcclause((Node*)clause)) { - /* this isn't an Oper, it's a Func!! */ - /* - ** This is not an operator, so we guess at the selectivity. - ** THIS IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE - ** ABLE TO HAVE SELECTIVITIES THEMSELVES. - ** -- JMH 7/9/92 - */ - s1 = 0.1; - } else if (NumRelids((Node*) clause) == 1) { - /* ...otherwise, calculate s1 from 'clauses'. - * The clause is not a join clause, since there is - * only one relid in the clause. The clause - * selectivity will be based on the operator - * selectivity and operand values. - */ - Oid opno = ((Oper*)((Expr*)clause)->oper)->opno; - RegProcedure oprrest = get_oprrest(opno); - Oid relid; - int relidx; - AttrNumber attno; - Datum constval; - int flag; - - get_relattval((Node*)clause, &relidx, &attno, &constval, &flag); - relid = getrelid(relidx, root->rtable); - - /* if the oprrest procedure is missing for whatever reason, - use a selectivity of 0.5*/ - if (!oprrest) - s1 = (Cost) (0.5); + /* + * we have a bool Var. This is exactly equivalent to the clause: + * reln.attribute = 't' so we compute the selectivity as if that + * is what we have. The magic #define constants are a hack. I + * didn't want to have to do system cache look ups to find out all + * of that info. + */ + + s1 = restriction_selectivity(EqualSelectivityProcedure, + BooleanEqualOperator, + relid, + ((Var *) clause)->varoattno, + "t", + _SELEC_CONSTANT_RIGHT_); + } + else if (or_selectivities) + { + /* If s1 has already been assigned by an index, use that value. */ + List *this_sel = lfirst(or_selectivities); + + s1 = floatVal(this_sel); + } + else if (is_funcclause((Node *) clause)) + { + /* this isn't an Oper, it's a Func!! */ + + /* + * * This is not an operator, so we guess at the selectivity. * + * THIS IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE * ABLE + * TO HAVE SELECTIVITIES THEMSELVES. * -- JMH 7/9/92 + */ + s1 = 0.1; + } + else if (NumRelids((Node *) clause) == 1) + { + + /* + * ...otherwise, calculate s1 from 'clauses'. The clause is not a + * join clause, since there is only one relid in the clause. The + * clause selectivity will be based on the operator selectivity + * and operand values. + */ + Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno; + RegProcedure oprrest = get_oprrest(opno); + Oid relid; + int relidx; + AttrNumber attno; + Datum constval; + int flag; + + get_relattval((Node *) clause, &relidx, &attno, &constval, &flag); + relid = getrelid(relidx, root->rtable); + + /* + * if the oprrest procedure is missing for whatever reason, use a + * selectivity of 0.5 + */ + if (!oprrest) + s1 = (Cost) (0.5); + else if (attno == InvalidAttrNumber) + { + + /* + * attno can be Invalid if the clause had a function in it, + * i.e. WHERE myFunc(f) = 10 + */ + /* this should be FIXED somehow to use function selectivity */ + s1 = (Cost) (0.5); + } + else + s1 = (Cost) restriction_selectivity(oprrest, + opno, + relid, + attno, + (char *) constval, + flag); + + } else - if (attno == InvalidAttrNumber) { - /* attno can be Invalid if the clause had a function in it, - i.e. WHERE myFunc(f) = 10 */ - /* this should be FIXED somehow to use function selectivity */ - s1 = (Cost) (0.5); - } else - s1 = (Cost) restriction_selectivity(oprrest, - opno, - relid, - attno, - (char *)constval, - flag); - - } else { - /* The clause must be a join clause. The clause - * selectivity will be based on the relations to be - * scanned and the attributes they are to be joined - * on. + { + + /* + * The clause must be a join clause. The clause selectivity will + * be based on the relations to be scanned and the attributes they + * are to be joined on. + */ + Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno; + RegProcedure oprjoin = get_oprjoin(opno); + int relid1, + relid2; + AttrNumber attno1, + attno2; + + get_rels_atts((Node *) clause, &relid1, &attno1, &relid2, &attno2); + relid1 = getrelid(relid1, root->rtable); + relid2 = getrelid(relid2, root->rtable); + + /* + * if the oprjoin procedure is missing for whatever reason, use a + * selectivity of 0.5 + */ + if (!oprjoin) + s1 = (Cost) (0.5); + else + s1 = (Cost) join_selectivity(oprjoin, + opno, + relid1, + attno1, + relid2, + attno2); + } + + /* + * A null clause list eliminates no tuples, so return a selectivity of + * 1.0. If there is only one clause, the selectivity is not that of + * an 'or' clause, but rather that of the single clause. */ - Oid opno = ((Oper*)((Expr*)clause)->oper)->opno; - RegProcedure oprjoin = get_oprjoin (opno); - int relid1, relid2; - AttrNumber attno1, attno2; - - get_rels_atts((Node*)clause, &relid1, &attno1, &relid2, &attno2); - relid1 = getrelid(relid1, root->rtable); - relid2 = getrelid(relid2, root->rtable); - - /* if the oprjoin procedure is missing for whatever reason, - use a selectivity of 0.5*/ - if (!oprjoin) - s1 = (Cost) (0.5); - else - s1 = (Cost) join_selectivity(oprjoin, - opno, - relid1, - attno1, - relid2, - attno2); - } - - /* A null clause list eliminates no tuples, so return a selectivity - * of 1.0. If there is only one clause, the selectivity is not - * that of an 'or' clause, but rather that of the single clause. - */ - - if (length (clauses) < 2) { - return(s1); - } else { - /* Compute selectivity of the 'or'ed subclauses. */ - /* Added check for taking lnext(NIL). -- JMH 3/9/92 */ - Cost s2; - - if (or_selectivities != NIL) - s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities)); + + if (length(clauses) < 2) + { + return (s1); + } else - s2 = compute_selec(root, lnext(clauses), NIL); - return(s1 + s2 - s1 * s2); - } -} + { + /* Compute selectivity of the 'or'ed subclauses. */ + /* Added check for taking lnext(NIL). -- JMH 3/9/92 */ + Cost s2; + if (or_selectivities != NIL) + s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities)); + else + s2 = compute_selec(root, lnext(clauses), NIL); + return (s1 + s2 - s1 * s2); + } +} diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 35453fb3870..2873e62c48c 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * costsize.c-- - * Routines to compute (and set) relation sizes and path costs + * Routines to compute (and set) relation sizes and path costs * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.16 1997/08/19 21:31:48 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.17 1997/09/07 04:43:33 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -17,15 +17,15 @@ #include <math.h> #ifdef HAVE_LIMITS_H -# include <limits.h> -# ifndef MAXINT -# define MAXINT INT_MAX -# endif +#include <limits.h> +#ifndef MAXINT +#define MAXINT INT_MAX +#endif #else -# ifdef HAVE_VALUES_H -# include <values.h> -# endif -#endif +#ifdef HAVE_VALUES_H +#include <values.h> +#endif +#endif #include <utils/lsyscache.h> #include "nodes/relation.h" @@ -35,77 +35,81 @@ #include "optimizer/keys.h" #include "optimizer/tlist.h" -#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "storage/bufmgr.h" /* for BLCKSZ */ -extern int NBuffers; +extern int NBuffers; -static int compute_attribute_width(TargetEntry *tlistentry); -static double base_log(double x, double b); -static int compute_targetlist_width(List *targetlist); +static int compute_attribute_width(TargetEntry * tlistentry); +static double base_log(double x, double b); +static int compute_targetlist_width(List * targetlist); -int _disable_cost_ = 30000000; - -bool _enable_seqscan_ = true; -bool _enable_indexscan_ = true; -bool _enable_sort_ = true; -bool _enable_hash_ = true; -bool _enable_nestloop_ = true; -bool _enable_mergesort_ = true; -bool _enable_hashjoin_ = true; +int _disable_cost_ = 30000000; -Cost _cpu_page_wight_ = _CPU_PAGE_WEIGHT_; -Cost _cpu_index_page_wight_ = _CPU_INDEX_PAGE_WEIGHT_; +bool _enable_seqscan_ = true; +bool _enable_indexscan_ = true; +bool _enable_sort_ = true; +bool _enable_hash_ = true; +bool _enable_nestloop_ = true; +bool _enable_mergesort_ = true; +bool _enable_hashjoin_ = true; -/* +Cost _cpu_page_wight_ = _CPU_PAGE_WEIGHT_; +Cost _cpu_index_page_wight_ = _CPU_INDEX_PAGE_WEIGHT_; + +/* * cost_seqscan-- - * Determines and returns the cost of scanning a relation sequentially. - * If the relation is a temporary to be materialized from a query - * embedded within a data field (determined by 'relid' containing an - * attribute reference), then a predetermined constant is returned (we - * have NO IDEA how big the result of a POSTQUEL procedure is going to - * be). - * - * disk = p - * cpu = *CPU-PAGE-WEIGHT* * t - * + * Determines and returns the cost of scanning a relation sequentially. + * If the relation is a temporary to be materialized from a query + * embedded within a data field (determined by 'relid' containing an + * attribute reference), then a predetermined constant is returned (we + * have NO IDEA how big the result of a POSTQUEL procedure is going to + * be). + * + * disk = p + * cpu = *CPU-PAGE-WEIGHT* * t + * * 'relid' is the relid of the relation to be scanned * 'relpages' is the number of pages in the relation to be scanned - * (as determined from the system catalogs) + * (as determined from the system catalogs) * 'reltuples' is the number of tuples in the relation to be scanned - * + * * Returns a flonum. - * + * */ Cost cost_seqscan(int relid, int relpages, int reltuples) { - Cost temp = 0; + Cost temp = 0; - if ( !_enable_seqscan_ ) - temp += _disable_cost_; + if (!_enable_seqscan_) + temp += _disable_cost_; - if (relid < 0) { - /* - * cost of sequentially scanning a materialized temporary relation - */ - temp += _TEMP_SCAN_COST_; - } else { - temp += relpages; - temp += _cpu_page_wight_ * reltuples; - } - Assert(temp >= 0); - return(temp); + if (relid < 0) + { + + /* + * cost of sequentially scanning a materialized temporary relation + */ + temp += _TEMP_SCAN_COST_; + } + else + { + temp += relpages; + temp += _cpu_page_wight_ * reltuples; + } + Assert(temp >= 0); + return (temp); } -/* +/* * cost_index-- - * Determines and returns the cost of scanning a relation using an index. - * - * disk = expected-index-pages + expected-data-pages - * cpu = *CPU-PAGE-WEIGHT* * - * (expected-index-tuples + expected-data-tuples) - * + * Determines and returns the cost of scanning a relation using an index. + * + * disk = expected-index-pages + expected-data-pages + * cpu = *CPU-PAGE-WEIGHT* * + * (expected-index-tuples + expected-data-tuples) + * * 'indexid' is the index OID * 'expected-indexpages' is the number of index pages examined in the scan * 'selec' is the selectivity of the index @@ -113,100 +117,102 @@ cost_seqscan(int relid, int relpages, int reltuples) * 'reltuples' is the number of tuples in the main relation * 'indexpages' is the number of pages in the index relation * 'indextuples' is the number of tuples in the index relation - * + * * Returns a flonum. - * + * */ Cost cost_index(Oid indexid, - int expected_indexpages, - Cost selec, - int relpages, - int reltuples, - int indexpages, - int indextuples, - bool is_injoin) + int expected_indexpages, + Cost selec, + int relpages, + int reltuples, + int indexpages, + int indextuples, + bool is_injoin) { - Cost temp; - double temp2; + Cost temp; + double temp2; + + temp = (Cost) 0; - temp = (Cost) 0; + if (!_enable_indexscan_ && !is_injoin) + temp += _disable_cost_; - if (!_enable_indexscan_ && !is_injoin) - temp += _disable_cost_; + /* expected index relation pages */ + temp += expected_indexpages; - /* expected index relation pages */ - temp += expected_indexpages; + /* expected base relation pages */ + temp2 = (reltuples == 0) ? (double) 0 : (double) relpages / reltuples; + temp2 = temp2 * (double) selec *indextuples; - /* expected base relation pages */ - temp2 = ( reltuples == 0 ) ? (double)0 : (double)relpages/reltuples; - temp2 = temp2 * (double)selec * indextuples; - temp += Min (relpages, (int)ceil (temp2)); + temp += Min(relpages, (int) ceil(temp2)); - /* per index tuples */ - temp = temp + (_cpu_index_page_wight_ * selec * indextuples); + /* per index tuples */ + temp = temp + (_cpu_index_page_wight_ * selec * indextuples); - /* per heap tuples */ - temp = temp + (_cpu_page_wight_ * selec * reltuples); + /* per heap tuples */ + temp = temp + (_cpu_page_wight_ * selec * reltuples); - Assert(temp >= 0); - return(temp); + Assert(temp >= 0); + return (temp); } -/* +/* * cost_sort-- - * Determines and returns the cost of sorting a relation by considering - * 1. the cost of doing an external sort: XXX this is probably too low - * disk = (p lg p) - * cpu = *CPU-PAGE-WEIGHT* * (t lg t) - * 2. the cost of reading the sort result into memory (another seqscan) - * unless 'noread' is set - * + * Determines and returns the cost of sorting a relation by considering + * 1. the cost of doing an external sort: XXX this is probably too low + * disk = (p lg p) + * cpu = *CPU-PAGE-WEIGHT* * (t lg t) + * 2. the cost of reading the sort result into memory (another seqscan) + * unless 'noread' is set + * * 'keys' is a list of sort keys * 'tuples' is the number of tuples in the relation * 'width' is the average tuple width in bytes * 'noread' is a flag indicating that the sort result can remain on disk - * (i.e., the sort result is the result relation) - * + * (i.e., the sort result is the result relation) + * * Returns a flonum. - * + * */ Cost -cost_sort(List *keys, int tuples, int width, bool noread) +cost_sort(List * keys, int tuples, int width, bool noread) { - Cost temp = 0; - int npages = page_size (tuples,width); - Cost pages = (Cost)npages; - Cost numTuples = tuples; - - if ( !_enable_sort_ ) - temp += _disable_cost_ ; - if (tuples == 0 || keys==NULL) + Cost temp = 0; + int npages = page_size(tuples, width); + Cost pages = (Cost) npages; + Cost numTuples = tuples; + + if (!_enable_sort_) + temp += _disable_cost_; + if (tuples == 0 || keys == NULL) { - Assert(temp >= 0); - return(temp); + Assert(temp >= 0); + return (temp); } - temp += pages * base_log((double)pages, (double)2.0); + temp += pages * base_log((double) pages, (double) 2.0); - /* - * could be base_log(pages, NBuffers), but we are only doing 2-way merges - */ - temp += _cpu_page_wight_ * - numTuples * base_log((double)pages,(double)2.0); + /* + * could be base_log(pages, NBuffers), but we are only doing 2-way + * merges + */ + temp += _cpu_page_wight_ * + numTuples * base_log((double) pages, (double) 2.0); - if( !noread ) - temp = temp + cost_seqscan(_TEMP_RELATION_ID_, npages, tuples); - Assert(temp >= 0); + if (!noread) + temp = temp + cost_seqscan(_TEMP_RELATION_ID_, npages, tuples); + Assert(temp >= 0); - return(temp); + return (temp); } -/* +/* * cost_result-- - * Determines and returns the cost of writing a relation of 'tuples' - * tuples of 'width' bytes out to a result relation. - * + * Determines and returns the cost of writing a relation of 'tuples' + * tuples of 'width' bytes out to a result relation. + * * Returns a flonum. * */ @@ -214,257 +220,273 @@ cost_sort(List *keys, int tuples, int width, bool noread) Cost cost_result(int tuples, int width) { - Cost temp =0; - temp = temp + page_size(tuples,width); - temp = temp + _cpu_page_wight_ * tuples; - Assert(temp >= 0); - return(temp); + Cost temp = 0; + + temp = temp + page_size(tuples, width); + temp = temp + _cpu_page_wight_ * tuples; + Assert(temp >= 0); + return (temp); } + #endif -/* +/* * cost_nestloop-- - * Determines and returns the cost of joining two relations using the - * nested loop algorithm. - * + * Determines and returns the cost of joining two relations using the + * nested loop algorithm. + * * 'outercost' is the (disk+cpu) cost of scanning the outer relation * 'innercost' is the (disk+cpu) cost of scanning the inner relation * 'outertuples' is the number of tuples in the outer relation - * + * * Returns a flonum. * */ Cost cost_nestloop(Cost outercost, - Cost innercost, - int outertuples, - int innertuples, - int outerpages, - bool is_indexjoin) + Cost innercost, + int outertuples, + int innertuples, + int outerpages, + bool is_indexjoin) { - Cost temp =0; + Cost temp = 0; - if ( !_enable_nestloop_ ) - temp += _disable_cost_; - temp += outercost; - temp += outertuples * innercost; - Assert(temp >= 0); + if (!_enable_nestloop_) + temp += _disable_cost_; + temp += outercost; + temp += outertuples * innercost; + Assert(temp >= 0); - return(temp); + return (temp); } -/* +/* * cost_mergesort-- - * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the - * outer and inner relations - * 'outersortkeys' and 'innersortkeys' are lists of the keys to be used - * to sort the outer and inner relations - * 'outertuples' and 'innertuples' are the number of tuples in the outer - * and inner relations - * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) - * of the tuples of the outer and inner relations - * + * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the + * outer and inner relations + * 'outersortkeys' and 'innersortkeys' are lists of the keys to be used + * to sort the outer and inner relations + * 'outertuples' and 'innertuples' are the number of tuples in the outer + * and inner relations + * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) + * of the tuples of the outer and inner relations + * * Returns a flonum. - * + * */ Cost cost_mergesort(Cost outercost, - Cost innercost, - List *outersortkeys, - List *innersortkeys, - int outersize, - int innersize, - int outerwidth, - int innerwidth) + Cost innercost, + List * outersortkeys, + List * innersortkeys, + int outersize, + int innersize, + int outerwidth, + int innerwidth) { - Cost temp = 0; - - if ( !_enable_mergesort_ ) - temp += _disable_cost_; - - temp += outercost; - temp += innercost; - temp += cost_sort(outersortkeys,outersize,outerwidth,false); - temp += cost_sort(innersortkeys,innersize,innerwidth,false); - temp += _cpu_page_wight_ * (outersize + innersize); - Assert(temp >= 0); - - return(temp); + Cost temp = 0; + + if (!_enable_mergesort_) + temp += _disable_cost_; + + temp += outercost; + temp += innercost; + temp += cost_sort(outersortkeys, outersize, outerwidth, false); + temp += cost_sort(innersortkeys, innersize, innerwidth, false); + temp += _cpu_page_wight_ * (outersize + innersize); + Assert(temp >= 0); + + return (temp); } -/* - * cost_hashjoin-- XXX HASH - * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the - * outer and inner relations - * 'outerkeys' and 'innerkeys' are lists of the keys to be used - * to hash the outer and inner relations - * 'outersize' and 'innersize' are the number of tuples in the outer - * and inner relations - * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) - * of the tuples of the outer and inner relations - * +/* + * cost_hashjoin-- XXX HASH + * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the + * outer and inner relations + * 'outerkeys' and 'innerkeys' are lists of the keys to be used + * to hash the outer and inner relations + * 'outersize' and 'innersize' are the number of tuples in the outer + * and inner relations + * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) + * of the tuples of the outer and inner relations + * * Returns a flonum. */ Cost cost_hashjoin(Cost outercost, - Cost innercost, - List *outerkeys, - List *innerkeys, - int outersize, - int innersize, - int outerwidth, - int innerwidth) + Cost innercost, + List * outerkeys, + List * innerkeys, + int outersize, + int innersize, + int outerwidth, + int innerwidth) { - Cost temp = 0; - int outerpages = page_size (outersize,outerwidth); - int innerpages = page_size (innersize,innerwidth); - int nrun = ceil((double)outerpages/(double)NBuffers); - - if (outerpages < innerpages) - return _disable_cost_; - if ( !_enable_hashjoin_ ) - temp += _disable_cost_; - /* - temp += outercost + (nrun + 1) * innercost; - * - * the innercost shouldn't be used it. Instead the - * cost of hashing the innerpath should be used - * - * ASSUME innercost is 1 for now -- a horrible hack - * - jolly - temp += outercost + (nrun + 1); - * - * But we must add innercost to result. - vadim 04/24/97 - */ - temp += outercost + innercost + (nrun + 1); - - temp += _cpu_page_wight_ * (outersize + nrun * innersize); - Assert(temp >= 0); - - return(temp); + Cost temp = 0; + int outerpages = page_size(outersize, outerwidth); + int innerpages = page_size(innersize, innerwidth); + int nrun = ceil((double) outerpages / (double) NBuffers); + + if (outerpages < innerpages) + return _disable_cost_; + if (!_enable_hashjoin_) + temp += _disable_cost_; + + /* + * temp += outercost + (nrun + 1) * innercost; + * + * the innercost shouldn't be used it. Instead the cost of hashing the + * innerpath should be used + * + * ASSUME innercost is 1 for now -- a horrible hack - jolly temp += + * outercost + (nrun + 1); + * + * But we must add innercost to result. - vadim 04/24/97 + */ + temp += outercost + innercost + (nrun + 1); + + temp += _cpu_page_wight_ * (outersize + nrun * innersize); + Assert(temp >= 0); + + return (temp); } -/* +/* * compute-rel-size-- - * Computes the size of each relation in 'rel-list' (after applying - * restrictions), by multiplying the selectivity of each restriction - * by the original size of the relation. - * - * Sets the 'size' field for each relation entry with this computed size. - * + * Computes the size of each relation in 'rel-list' (after applying + * restrictions), by multiplying the selectivity of each restriction + * by the original size of the relation. + * + * Sets the 'size' field for each relation entry with this computed size. + * * Returns the size. */ -int compute_rel_size(Rel *rel) +int +compute_rel_size(Rel * rel) { - Cost temp; - int temp1; - - temp = rel->tuples * product_selec(rel->clauseinfo); - Assert(temp >= 0); - if (temp >= (MAXINT - 1)) { - temp1 = MAXINT; - } else { - temp1 = ceil((double) temp); - } - Assert(temp1 >= 0); - Assert(temp1 <= MAXINT); - return(temp1); + Cost temp; + int temp1; + + temp = rel->tuples * product_selec(rel->clauseinfo); + Assert(temp >= 0); + if (temp >= (MAXINT - 1)) + { + temp1 = MAXINT; + } + else + { + temp1 = ceil((double) temp); + } + Assert(temp1 >= 0); + Assert(temp1 <= MAXINT); + return (temp1); } -/* +/* * compute-rel-width-- - * Computes the width in bytes of a tuple from 'rel'. - * + * Computes the width in bytes of a tuple from 'rel'. + * * Returns the width of the tuple as a fixnum. */ int -compute_rel_width(Rel *rel) +compute_rel_width(Rel * rel) { - return (compute_targetlist_width(get_actual_tlist(rel->targetlist))); + return (compute_targetlist_width(get_actual_tlist(rel->targetlist))); } -/* +/* * compute-targetlist-width-- - * Computes the width in bytes of a tuple made from 'targetlist'. - * + * Computes the width in bytes of a tuple made from 'targetlist'. + * * Returns the width of the tuple as a fixnum. */ static int -compute_targetlist_width(List *targetlist) +compute_targetlist_width(List * targetlist) { - List *temp_tl; - int tuple_width = 0; - - foreach (temp_tl, targetlist) { - tuple_width = tuple_width + - compute_attribute_width(lfirst(temp_tl)); - } - return(tuple_width); + List *temp_tl; + int tuple_width = 0; + + foreach(temp_tl, targetlist) + { + tuple_width = tuple_width + + compute_attribute_width(lfirst(temp_tl)); + } + return (tuple_width); } -/* +/* * compute-attribute-width-- - * Given a target list entry, find the size in bytes of the attribute. - * - * If a field is variable-length, it is assumed to be at least the size - * of a TID field. - * + * Given a target list entry, find the size in bytes of the attribute. + * + * If a field is variable-length, it is assumed to be at least the size + * of a TID field. + * * Returns the width of the attribute as a fixnum. */ static int -compute_attribute_width(TargetEntry *tlistentry) +compute_attribute_width(TargetEntry * tlistentry) { - int width = get_typlen(tlistentry->resdom->restype); - if (width < 0) - return(_DEFAULT_ATTRIBUTE_WIDTH_); - else - return(width); + int width = get_typlen(tlistentry->resdom->restype); + + if (width < 0) + return (_DEFAULT_ATTRIBUTE_WIDTH_); + else + return (width); } -/* +/* * compute-joinrel-size-- - * Computes the size of the join relation 'joinrel'. - * + * Computes the size of the join relation 'joinrel'. + * * Returns a fixnum. */ int -compute_joinrel_size(JoinPath *joinpath) +compute_joinrel_size(JoinPath * joinpath) { - Cost temp = 1.0; - int temp1 = 0; - - temp *= ((Path*)joinpath->outerjoinpath)->parent->size; - temp *= ((Path*)joinpath->innerjoinpath)->parent->size; - - temp = temp * product_selec(joinpath->pathclauseinfo); - if (temp >= (MAXINT -1)) { - temp1 = MAXINT; - } else { - /* should be ceil here, we don't want joinrel size's of one, do we? */ - temp1 = ceil((double)temp); - } - Assert(temp1 >= 0); - - return(temp1); + Cost temp = 1.0; + int temp1 = 0; + + temp *= ((Path *) joinpath->outerjoinpath)->parent->size; + temp *= ((Path *) joinpath->innerjoinpath)->parent->size; + + temp = temp * product_selec(joinpath->pathclauseinfo); + if (temp >= (MAXINT - 1)) + { + temp1 = MAXINT; + } + else + { + + /* + * should be ceil here, we don't want joinrel size's of one, do + * we? + */ + temp1 = ceil((double) temp); + } + Assert(temp1 >= 0); + + return (temp1); } -/* +/* * page-size-- - * Returns an estimate of the number of pages covered by a given - * number of tuples of a given width (size in bytes). + * Returns an estimate of the number of pages covered by a given + * number of tuples of a given width (size in bytes). */ -int page_size(int tuples, int width) +int +page_size(int tuples, int width) { - int temp =0; + int temp = 0; - temp = ceil((double)(tuples * (width + sizeof(HeapTupleData))) - / BLCKSZ); - Assert(temp >= 0); - return(temp); + temp = ceil((double) (tuples * (width + sizeof(HeapTupleData))) + / BLCKSZ); + Assert(temp >= 0); + return (temp); } static double base_log(double x, double b) { - return(log(x)/log(b)); + return (log(x) / log(b)); } diff --git a/src/backend/optimizer/path/hashutils.c b/src/backend/optimizer/path/hashutils.c index cdbd9b6d901..5ec592ad1f9 100644 --- a/src/backend/optimizer/path/hashutils.c +++ b/src/backend/optimizer/path/hashutils.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * hashutils.c-- - * Utilities for finding applicable merge clauses and pathkeys + * Utilities for finding applicable merge clauses and pathkeys * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.2 1997/09/07 04:43:34 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,101 +20,109 @@ #include "optimizer/clauses.h" -static HInfo *match_hashop_hashinfo(Oid hashop, List *hashinfo_list); +static HInfo *match_hashop_hashinfo(Oid hashop, List * hashinfo_list); -/* +/* * group-clauses-by-hashop-- - * If a join clause node in 'clauseinfo-list' is hashjoinable, store - * it within a hashinfo node containing other clause nodes with the same - * hash operator. - * + * If a join clause node in 'clauseinfo-list' is hashjoinable, store + * it within a hashinfo node containing other clause nodes with the same + * hash operator. + * * 'clauseinfo-list' is the list of clauseinfo nodes * 'inner-relid' is the relid of the inner join relation - * + * * Returns the new list of hashinfo nodes. - * + * */ -List * -group_clauses_by_hashop(List *clauseinfo_list, - int inner_relid) +List * +group_clauses_by_hashop(List * clauseinfo_list, + int inner_relid) { - List *hashinfo_list = NIL; - CInfo *clauseinfo = (CInfo*)NULL; - List *i = NIL; - Oid hashjoinop = 0; - - foreach (i,clauseinfo_list) { - clauseinfo = (CInfo*)lfirst(i); - hashjoinop = clauseinfo->hashjoinoperator; - - /* - * Create a new hashinfo node and add it to 'hashinfo-list' if one - * does not yet exist for this hash operator. - */ - if (hashjoinop ) { - HInfo *xhashinfo = (HInfo*)NULL; - Expr *clause = clauseinfo->clause; - Var *leftop = get_leftop(clause); - Var *rightop = get_rightop(clause); - JoinKey *keys = (JoinKey*)NULL; - - xhashinfo = - match_hashop_hashinfo(hashjoinop,hashinfo_list); - - if (inner_relid == leftop->varno){ - keys = makeNode(JoinKey); - keys->outer = rightop; - keys->inner = leftop; - } else { - keys = makeNode(JoinKey); - keys->outer = leftop; - keys->inner = rightop; - } - - if (xhashinfo==NULL) { - xhashinfo = makeNode(HInfo); - xhashinfo->hashop = hashjoinop; - - xhashinfo->jmethod.jmkeys = NIL; - xhashinfo->jmethod.clauses = NIL; - - /* XXX was push */ - hashinfo_list = lappend(hashinfo_list,xhashinfo); - hashinfo_list = nreverse(hashinfo_list); - } - - xhashinfo->jmethod.clauses = - lcons(clause, xhashinfo->jmethod.clauses); - - xhashinfo->jmethod.jmkeys = - lcons(keys, xhashinfo->jmethod.jmkeys); + List *hashinfo_list = NIL; + CInfo *clauseinfo = (CInfo *) NULL; + List *i = NIL; + Oid hashjoinop = 0; + + foreach(i, clauseinfo_list) + { + clauseinfo = (CInfo *) lfirst(i); + hashjoinop = clauseinfo->hashjoinoperator; + + /* + * Create a new hashinfo node and add it to 'hashinfo-list' if one + * does not yet exist for this hash operator. + */ + if (hashjoinop) + { + HInfo *xhashinfo = (HInfo *) NULL; + Expr *clause = clauseinfo->clause; + Var *leftop = get_leftop(clause); + Var *rightop = get_rightop(clause); + JoinKey *keys = (JoinKey *) NULL; + + xhashinfo = + match_hashop_hashinfo(hashjoinop, hashinfo_list); + + if (inner_relid == leftop->varno) + { + keys = makeNode(JoinKey); + keys->outer = rightop; + keys->inner = leftop; + } + else + { + keys = makeNode(JoinKey); + keys->outer = leftop; + keys->inner = rightop; + } + + if (xhashinfo == NULL) + { + xhashinfo = makeNode(HInfo); + xhashinfo->hashop = hashjoinop; + + xhashinfo->jmethod.jmkeys = NIL; + xhashinfo->jmethod.clauses = NIL; + + /* XXX was push */ + hashinfo_list = lappend(hashinfo_list, xhashinfo); + hashinfo_list = nreverse(hashinfo_list); + } + + xhashinfo->jmethod.clauses = + lcons(clause, xhashinfo->jmethod.clauses); + + xhashinfo->jmethod.jmkeys = + lcons(keys, xhashinfo->jmethod.jmkeys); + } } - } - return(hashinfo_list); + return (hashinfo_list); } -/* +/* * match-hashop-hashinfo-- - * Searches the list 'hashinfo-list' for a hashinfo node whose hash op - * field equals 'hashop'. - * + * Searches the list 'hashinfo-list' for a hashinfo node whose hash op + * field equals 'hashop'. + * * Returns the node if it exists. - * + * */ -static HInfo * -match_hashop_hashinfo(Oid hashop, List *hashinfo_list) +static HInfo * +match_hashop_hashinfo(Oid hashop, List * hashinfo_list) { - Oid key = 0; - HInfo *xhashinfo = (HInfo*)NULL; - List *i = NIL; - - foreach( i, hashinfo_list) { - xhashinfo = (HInfo*)lfirst(i); - key = xhashinfo->hashop; - if (hashop == key) { /* found */ - return(xhashinfo); /* should be a hashinfo node ! */ + Oid key = 0; + HInfo *xhashinfo = (HInfo *) NULL; + List *i = NIL; + + foreach(i, hashinfo_list) + { + xhashinfo = (HInfo *) lfirst(i); + key = xhashinfo->hashop; + if (hashop == key) + { /* found */ + return (xhashinfo); /* should be a hashinfo node ! */ + } } - } - return((HInfo*)NIL); + return ((HInfo *) NIL); } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index f5b70e43a0f..bd9bc15ace0 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * indxpath.c-- - * Routines to determine which indices are usable for scanning a - * given relation + * Routines to determine which indices are usable for scanning a + * given relation * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.8 1997/08/12 22:53:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.9 1997/09/07 04:43:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -43,308 +43,321 @@ #include "catalog/pg_proc.h" #include "executor/executor.h" -#include "parser/parsetree.h" /* for getrelid() */ - - -static void match_index_orclauses(Rel *rel, Rel *index, int indexkey, - int xclass, List *clauseinfo_list); -static bool match_index_to_operand(int indexkey, Expr *operand, - Rel *rel, Rel *index); -static List *match_index_orclause(Rel *rel, Rel *index, int indexkey, - int xclass, List *or_clauses, List *other_matching_indices); -static List *group_clauses_by_indexkey(Rel *rel, Rel *index, - int *indexkeys, Oid *classes, List *clauseinfo_list); -static List *group_clauses_by_ikey_for_joins(Rel *rel, Rel *index, - int *indexkeys, Oid *classes, List *join_cinfo_list, List *restr_cinfo_list); -static CInfo *match_clause_to_indexkey(Rel *rel, Rel *index, int indexkey, - int xclass, CInfo *clauseInfo, bool join); -static bool pred_test(List *predicate_list, List *clauseinfo_list, - List *joininfo_list); -static bool one_pred_test(Expr *predicate, List *clauseinfo_list); -static bool one_pred_clause_expr_test(Expr *predicate, Node *clause); -static bool one_pred_clause_test(Expr *predicate, Node *clause); -static bool clause_pred_clause_test(Expr *predicate, Node *clause); -static List *indexable_joinclauses (Rel *rel, Rel *index, - List *joininfo_list, List *clauseinfo_list); -static List *index_innerjoin(Query *root, Rel *rel, - List *clausegroup_list, Rel *index); -static List *create_index_paths(Query *root, Rel *rel, Rel *index, - List *clausegroup_list, bool join); -static List *add_index_paths(List *indexpaths, List *new_indexpaths); -static bool function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index); -static bool SingleAttributeIndex(Rel *index); +#include "parser/parsetree.h" /* for getrelid() */ + + +static void +match_index_orclauses(Rel * rel, Rel * index, int indexkey, + int xclass, List * clauseinfo_list); +static bool +match_index_to_operand(int indexkey, Expr * operand, + Rel * rel, Rel * index); +static List * +match_index_orclause(Rel * rel, Rel * index, int indexkey, + int xclass, List * or_clauses, List * other_matching_indices); +static List * +group_clauses_by_indexkey(Rel * rel, Rel * index, + int *indexkeys, Oid * classes, List * clauseinfo_list); +static List * +group_clauses_by_ikey_for_joins(Rel * rel, Rel * index, + int *indexkeys, Oid * classes, List * join_cinfo_list, List * restr_cinfo_list); +static CInfo * +match_clause_to_indexkey(Rel * rel, Rel * index, int indexkey, + int xclass, CInfo * clauseInfo, bool join); +static bool +pred_test(List * predicate_list, List * clauseinfo_list, + List * joininfo_list); +static bool one_pred_test(Expr * predicate, List * clauseinfo_list); +static bool one_pred_clause_expr_test(Expr * predicate, Node * clause); +static bool one_pred_clause_test(Expr * predicate, Node * clause); +static bool clause_pred_clause_test(Expr * predicate, Node * clause); +static List * +indexable_joinclauses(Rel * rel, Rel * index, + List * joininfo_list, List * clauseinfo_list); +static List * +index_innerjoin(Query * root, Rel * rel, + List * clausegroup_list, Rel * index); +static List * +create_index_paths(Query * root, Rel * rel, Rel * index, + List * clausegroup_list, bool join); +static List *add_index_paths(List * indexpaths, List * new_indexpaths); +static bool function_index_operand(Expr * funcOpnd, Rel * rel, Rel * index); +static bool SingleAttributeIndex(Rel * index); /* If Spyros can use a constant PRS2_BOOL_TYPEID, I can use this */ #define BOOL_TYPEID ((Oid) 16) -/* +/* * find-index-paths-- - * Finds all possible index paths by determining which indices in the - * list 'indices' are usable. - * - * To be usable, an index must match against either a set of - * restriction clauses or join clauses. - * - * Note that the current implementation requires that there exist - * matching clauses for every key in the index (i.e., no partial - * matches are allowed). - * - * If an index can't be used with restriction clauses, but its keys - * match those of the result sort order (according to information stored - * within 'sortkeys'), then the index is also considered. + * Finds all possible index paths by determining which indices in the + * list 'indices' are usable. + * + * To be usable, an index must match against either a set of + * restriction clauses or join clauses. + * + * Note that the current implementation requires that there exist + * matching clauses for every key in the index (i.e., no partial + * matches are allowed). + * + * If an index can't be used with restriction clauses, but its keys + * match those of the result sort order (according to information stored + * within 'sortkeys'), then the index is also considered. * * 'rel' is the relation entry to which these index paths correspond * 'indices' is a list of possible index paths * 'clauseinfo-list' is a list of restriction clauseinfo nodes for 'rel' * 'joininfo-list' is a list of joininfo nodes for 'rel' * 'sortkeys' is a node describing the result sort order (from - * (find_sortkeys)) - * + * (find_sortkeys)) + * * Returns a list of index nodes. - * + * */ -List * -find_index_paths (Query *root, - Rel *rel, - List *indices, - List *clauseinfo_list, - List *joininfo_list) +List * +find_index_paths(Query * root, + Rel * rel, + List * indices, + List * clauseinfo_list, + List * joininfo_list) { - List *scanclausegroups = NIL; - List *scanpaths = NIL; - Rel *index = (Rel *)NULL; - List *joinclausegroups = NIL; - List *joinpaths = NIL; - List *retval = NIL; - - if(indices == NIL) - return(NULL); - - index = (Rel*)lfirst (indices); - - retval = find_index_paths(root, - rel, - lnext (indices), - clauseinfo_list, - joininfo_list); - - /* If this is a partial index, return if it fails the predicate test */ - if (index->indpred != NIL) - if (!pred_test(index->indpred, clauseinfo_list, joininfo_list)) - return retval; - - /* 1. If this index has only one key, try matching it against - * subclauses of an 'or' clause. The fields of the clauseinfo - * nodes are marked with lists of the matching indices no path - * are actually created. - * - * XXX NOTE: Currently btrees dos not support indices with - * > 1 key, so the following test will always be true for - * now but we have decided not to support index-scans - * on disjunction . -- lp - */ - if (SingleAttributeIndex(index)) - { - match_index_orclauses (rel, - index, - index->indexkeys[0], - index->classlist[0], - clauseinfo_list); - } + List *scanclausegroups = NIL; + List *scanpaths = NIL; + Rel *index = (Rel *) NULL; + List *joinclausegroups = NIL; + List *joinpaths = NIL; + List *retval = NIL; + + if (indices == NIL) + return (NULL); + + index = (Rel *) lfirst(indices); + + retval = find_index_paths(root, + rel, + lnext(indices), + clauseinfo_list, + joininfo_list); - /* - * 2. If the keys of this index match any of the available - * restriction clauses, then create pathnodes corresponding - * to each group of usable clauses. - */ - scanclausegroups = group_clauses_by_indexkey(rel, - index, - index->indexkeys, - index->classlist, - clauseinfo_list); - - scanpaths = NIL; - if (scanclausegroups != NIL) - scanpaths = create_index_paths (root, - rel, - index, - scanclausegroups, - false); - - /* - * 3. If this index can be used with any join clause, then - * create pathnodes for each group of usable clauses. An - * index can be used with a join clause if its ordering is - * useful for a mergejoin, or if the index can possibly be - * used for scanning the inner relation of a nestloop join. - */ - joinclausegroups = indexable_joinclauses(rel,index,joininfo_list, clauseinfo_list); - joinpaths = NIL; - - if (joinclausegroups != NIL) + /* If this is a partial index, return if it fails the predicate test */ + if (index->indpred != NIL) + if (!pred_test(index->indpred, clauseinfo_list, joininfo_list)) + return retval; + + /* + * 1. If this index has only one key, try matching it against + * subclauses of an 'or' clause. The fields of the clauseinfo nodes + * are marked with lists of the matching indices no path are actually + * created. + * + * XXX NOTE: Currently btrees dos not support indices with > 1 key, so + * the following test will always be true for now but we have decided + * not to support index-scans on disjunction . -- lp + */ + if (SingleAttributeIndex(index)) { - List *new_join_paths = create_index_paths(root, rel, + match_index_orclauses(rel, index, - joinclausegroups, - true); - List *innerjoin_paths = index_innerjoin(root, rel,joinclausegroups,index); + index->indexkeys[0], + index->classlist[0], + clauseinfo_list); + } + + /* + * 2. If the keys of this index match any of the available restriction + * clauses, then create pathnodes corresponding to each group of + * usable clauses. + */ + scanclausegroups = group_clauses_by_indexkey(rel, + index, + index->indexkeys, + index->classlist, + clauseinfo_list); + + scanpaths = NIL; + if (scanclausegroups != NIL) + scanpaths = create_index_paths(root, + rel, + index, + scanclausegroups, + false); + + /* + * 3. If this index can be used with any join clause, then create + * pathnodes for each group of usable clauses. An index can be used + * with a join clause if its ordering is useful for a mergejoin, or if + * the index can possibly be used for scanning the inner relation of a + * nestloop join. + */ + joinclausegroups = indexable_joinclauses(rel, index, joininfo_list, clauseinfo_list); + joinpaths = NIL; - rel->innerjoin = nconc (rel->innerjoin, innerjoin_paths); - joinpaths = new_join_paths; + if (joinclausegroups != NIL) + { + List *new_join_paths = create_index_paths(root, rel, + index, + joinclausegroups, + true); + List *innerjoin_paths = index_innerjoin(root, rel, joinclausegroups, index); + + rel->innerjoin = nconc(rel->innerjoin, innerjoin_paths); + joinpaths = new_join_paths; } - - /* - * Some sanity checks to make sure that - * the indexpath is valid. - */ - if (joinpaths!=NULL) - retval = add_index_paths(joinpaths,retval); - if (scanpaths!=NULL) - retval = add_index_paths(scanpaths,retval); - - return retval; + + /* + * Some sanity checks to make sure that the indexpath is valid. + */ + if (joinpaths != NULL) + retval = add_index_paths(joinpaths, retval); + if (scanpaths != NULL) + retval = add_index_paths(scanpaths, retval); + + return retval; } /**************************************************************************** - * ---- ROUTINES TO MATCH 'OR' CLAUSES ---- + * ---- ROUTINES TO MATCH 'OR' CLAUSES ---- ****************************************************************************/ -/* +/* * match-index-orclauses-- - * Attempt to match an index against subclauses within 'or' clauses. - * If the index does match, then the clause is marked with information - * about the index. - * - * Essentially, this adds 'index' to the list of indices in the - * ClauseInfo field of each of the clauses which it matches. - * + * Attempt to match an index against subclauses within 'or' clauses. + * If the index does match, then the clause is marked with information + * about the index. + * + * Essentially, this adds 'index' to the list of indices in the + * ClauseInfo field of each of the clauses which it matches. + * * 'rel' is the node of the relation on which the index is defined. * 'index' is the index node. * 'indexkey' is the (single) key of the index * 'class' is the class of the operator corresponding to 'indexkey'. * 'clauseinfo-list' is the list of available restriction clauses. - * + * * Returns nothing. - * + * */ static void -match_index_orclauses(Rel *rel, - Rel *index, - int indexkey, - int xclass, - List *clauseinfo_list) +match_index_orclauses(Rel * rel, + Rel * index, + int indexkey, + int xclass, + List * clauseinfo_list) { - CInfo *clauseinfo = (CInfo*)NULL; - List *i = NIL; - - foreach (i, clauseinfo_list) { - clauseinfo = (CInfo*)lfirst(i); - if (valid_or_clause(clauseinfo)) { - - /* Mark the 'or' clause with a list of indices which - * match each of its subclauses. The list is - * generated by adding 'index' to the existing - * list where appropriate. - */ - clauseinfo->indexids = - match_index_orclause (rel,index,indexkey, - xclass, - clauseinfo->clause->args, - clauseinfo->indexids); + CInfo *clauseinfo = (CInfo *) NULL; + List *i = NIL; + + foreach(i, clauseinfo_list) + { + clauseinfo = (CInfo *) lfirst(i); + if (valid_or_clause(clauseinfo)) + { + + /* + * Mark the 'or' clause with a list of indices which match + * each of its subclauses. The list is generated by adding + * 'index' to the existing list where appropriate. + */ + clauseinfo->indexids = + match_index_orclause(rel, index, indexkey, + xclass, + clauseinfo->clause->args, + clauseinfo->indexids); + } } - } } /* * match_index_operand-- - * Generalize test for a match between an existing index's key - * and the operand on the rhs of a restriction clause. Now check - * for functional indices as well. + * Generalize test for a match between an existing index's key + * and the operand on the rhs of a restriction clause. Now check + * for functional indices as well. */ -static bool +static bool match_index_to_operand(int indexkey, - Expr *operand, - Rel *rel, - Rel *index) + Expr * operand, + Rel * rel, + Rel * index) { - /* - * Normal index. - */ - if (index->indproc == InvalidOid) - return match_indexkey_operand(indexkey, (Var*)operand, rel); - - /* - * functional index check - */ - return (function_index_operand(operand, rel, index)); + + /* + * Normal index. + */ + if (index->indproc == InvalidOid) + return match_indexkey_operand(indexkey, (Var *) operand, rel); + + /* + * functional index check + */ + return (function_index_operand(operand, rel, index)); } -/* +/* * match-index-orclause-- - * Attempts to match an index against the subclauses of an 'or' clause. - * - * A match means that: - * (1) the operator within the subclause can be used with one - * of the index's operator classes, and - * (2) there is a usable key that matches the variable within a - * sargable clause. - * + * Attempts to match an index against the subclauses of an 'or' clause. + * + * A match means that: + * (1) the operator within the subclause can be used with one + * of the index's operator classes, and + * (2) there is a usable key that matches the variable within a + * sargable clause. + * * 'or-clauses' are the remaining subclauses within the 'or' clause * 'other-matching-indices' is the list of information on other indices - * that have already been matched to subclauses within this - * particular 'or' clause (i.e., a list previously generated by - * this routine) - * + * that have already been matched to subclauses within this + * particular 'or' clause (i.e., a list previously generated by + * this routine) + * * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where * a,b,c are nodes of indices that match the first subclause in * 'or-clauses', d,e,f match the second subclause, no indices * match the third, g,h match the fourth, etc. */ -static List * -match_index_orclause(Rel *rel, - Rel *index, - int indexkey, - int xclass, - List *or_clauses, - List *other_matching_indices) +static List * +match_index_orclause(Rel * rel, + Rel * index, + int indexkey, + int xclass, + List * or_clauses, + List * other_matching_indices) { - Node *clause = NULL; - List *matched_indices = other_matching_indices; - List *index_list = NIL; - List *clist; - List *ind; - - if (!matched_indices) - matched_indices = lcons(NIL, NIL); - - for (clist = or_clauses, ind = matched_indices; - clist; - clist = lnext(clist), ind = lnext(ind)) + Node *clause = NULL; + List *matched_indices = other_matching_indices; + List *index_list = NIL; + List *clist; + List *ind; + + if (!matched_indices) + matched_indices = lcons(NIL, NIL); + + for (clist = or_clauses, ind = matched_indices; + clist; + clist = lnext(clist), ind = lnext(ind)) { - clause = lfirst(clist); - if (is_opclause (clause) && - op_class(((Oper*)((Expr*)clause)->oper)->opno, - xclass, index->relam) && - match_index_to_operand(indexkey, - (Expr*)get_leftop((Expr*)clause), - rel, - index) && - IsA(get_rightop((Expr*)clause),Const)) { - - matched_indices = lcons(index, matched_indices); - index_list = lappend(index_list, - matched_indices); - } + clause = lfirst(clist); + if (is_opclause(clause) && + op_class(((Oper *) ((Expr *) clause)->oper)->opno, + xclass, index->relam) && + match_index_to_operand(indexkey, + (Expr *) get_leftop((Expr *) clause), + rel, + index) && + IsA(get_rightop((Expr *) clause), Const)) + { + + matched_indices = lcons(index, matched_indices); + index_list = lappend(index_list, + matched_indices); + } } - return(index_list); - + return (index_list); + } /**************************************************************************** - * ---- ROUTINES TO CHECK RESTRICTIONS ---- + * ---- ROUTINES TO CHECK RESTRICTIONS ---- ****************************************************************************/ @@ -358,176 +371,177 @@ match_index_orclause(Rel *rel, * keys list represent the arguments to the function. -mer 3 Oct. 1991 */ #define DoneMatchingIndexKeys(indexkeys, index) \ - (indexkeys[0] == 0 || \ - (index->indproc != InvalidOid)) + (indexkeys[0] == 0 || \ + (index->indproc != InvalidOid)) -/* +/* * group-clauses-by-indexkey-- - * Determines whether there are clauses which will match each and every - * one of the remaining keys of an index. - * + * Determines whether there are clauses which will match each and every + * one of the remaining keys of an index. + * * 'rel' is the node of the relation corresponding to the index. * 'indexkeys' are the remaining index keys to be matched. * 'classes' are the classes of the index operators on those keys. * 'clauses' is either: - * (1) the list of available restriction clauses on a single - * relation, or - * (2) a list of join clauses between 'rel' and a fixed set of - * relations, - * depending on the value of 'join'. + * (1) the list of available restriction clauses on a single + * relation, or + * (2) a list of join clauses between 'rel' and a fixed set of + * relations, + * depending on the value of 'join'. + * + * NOTE: it works now for restriction clauses only. - vadim 03/18/97 * - * NOTE: it works now for restriction clauses only. - vadim 03/18/97 - * * Returns all possible groups of clauses that will match (given that * one or more clauses can match any of the remaining keys). - * E.g., if you have clauses A, B, and C, ((A B) (A C)) might be + * E.g., if you have clauses A, B, and C, ((A B) (A C)) might be * returned for an index with 2 keys. - * + * */ -static List * -group_clauses_by_indexkey(Rel *rel, - Rel *index, - int *indexkeys, - Oid *classes, - List *clauseinfo_list) +static List * +group_clauses_by_indexkey(Rel * rel, + Rel * index, + int *indexkeys, + Oid * classes, + List * clauseinfo_list) { - List *curCinfo = NIL; - CInfo *matched_clause = (CInfo*)NULL; - List *clausegroup = NIL; - int curIndxKey; - Oid curClass; + List *curCinfo = NIL; + CInfo *matched_clause = (CInfo *) NULL; + List *clausegroup = NIL; + int curIndxKey; + Oid curClass; - if (clauseinfo_list == NIL) - return NIL; + if (clauseinfo_list == NIL) + return NIL; - while ( !DoneMatchingIndexKeys(indexkeys, index) ) - { - List *tempgroup = NIL; - - curIndxKey = indexkeys[0]; - curClass = classes[0]; - - foreach (curCinfo,clauseinfo_list) - { - CInfo *temp = (CInfo*)lfirst(curCinfo); - - matched_clause = match_clause_to_indexkey (rel, - index, - curIndxKey, - curClass, - temp, - false); - if (!matched_clause) - continue; - - tempgroup = lappend(tempgroup, matched_clause); - } - if ( tempgroup == NIL ) - break; + while (!DoneMatchingIndexKeys(indexkeys, index)) + { + List *tempgroup = NIL; + + curIndxKey = indexkeys[0]; + curClass = classes[0]; + + foreach(curCinfo, clauseinfo_list) + { + CInfo *temp = (CInfo *) lfirst(curCinfo); + + matched_clause = match_clause_to_indexkey(rel, + index, + curIndxKey, + curClass, + temp, + false); + if (!matched_clause) + continue; - clausegroup = nconc (clausegroup, tempgroup); - - indexkeys++; - classes++; - - } + tempgroup = lappend(tempgroup, matched_clause); + } + if (tempgroup == NIL) + break; - /* clausegroup holds all matched clauses ordered by indexkeys */ + clausegroup = nconc(clausegroup, tempgroup); + + indexkeys++; + classes++; + + } - if (clausegroup != NIL) - return(lcons(clausegroup, NIL)); - return NIL; + /* clausegroup holds all matched clauses ordered by indexkeys */ + + if (clausegroup != NIL) + return (lcons(clausegroup, NIL)); + return NIL; } -/* +/* * group-clauses-by-ikey-for-joins-- - * special edition of group-clauses-by-indexkey - will - * match join & restriction clauses. See comment in indexable_joinclauses. - * - vadim 03/18/97 - * + * special edition of group-clauses-by-indexkey - will + * match join & restriction clauses. See comment in indexable_joinclauses. + * - vadim 03/18/97 + * */ -static List * -group_clauses_by_ikey_for_joins(Rel *rel, - Rel *index, - int *indexkeys, - Oid *classes, - List *join_cinfo_list, - List *restr_cinfo_list) +static List * +group_clauses_by_ikey_for_joins(Rel * rel, + Rel * index, + int *indexkeys, + Oid * classes, + List * join_cinfo_list, + List * restr_cinfo_list) { - List *curCinfo = NIL; - CInfo *matched_clause = (CInfo*)NULL; - List *clausegroup = NIL; - int curIndxKey; - Oid curClass; - bool jfound = false; - - if (join_cinfo_list == NIL) - return NIL; + List *curCinfo = NIL; + CInfo *matched_clause = (CInfo *) NULL; + List *clausegroup = NIL; + int curIndxKey; + Oid curClass; + bool jfound = false; + + if (join_cinfo_list == NIL) + return NIL; + + while (!DoneMatchingIndexKeys(indexkeys, index)) + { + List *tempgroup = NIL; + + curIndxKey = indexkeys[0]; + curClass = classes[0]; + + foreach(curCinfo, join_cinfo_list) + { + CInfo *temp = (CInfo *) lfirst(curCinfo); + + matched_clause = match_clause_to_indexkey(rel, + index, + curIndxKey, + curClass, + temp, + true); + if (!matched_clause) + continue; + + tempgroup = lappend(tempgroup, matched_clause); + jfound = true; + } + foreach(curCinfo, restr_cinfo_list) + { + CInfo *temp = (CInfo *) lfirst(curCinfo); + + matched_clause = match_clause_to_indexkey(rel, + index, + curIndxKey, + curClass, + temp, + false); + if (!matched_clause) + continue; + + tempgroup = lappend(tempgroup, matched_clause); + } + if (tempgroup == NIL) + break; + + clausegroup = nconc(clausegroup, tempgroup); + + indexkeys++; + classes++; - while ( !DoneMatchingIndexKeys(indexkeys, index) ) - { - List *tempgroup = NIL; - - curIndxKey = indexkeys[0]; - curClass = classes[0]; - - foreach (curCinfo,join_cinfo_list) - { - CInfo *temp = (CInfo*)lfirst(curCinfo); - - matched_clause = match_clause_to_indexkey (rel, - index, - curIndxKey, - curClass, - temp, - true); - if (!matched_clause) - continue; - - tempgroup = lappend(tempgroup, matched_clause); - jfound = true; } - foreach (curCinfo,restr_cinfo_list) - { - CInfo *temp = (CInfo*)lfirst(curCinfo); - - matched_clause = match_clause_to_indexkey (rel, - index, - curIndxKey, - curClass, - temp, - false); - if (!matched_clause) - continue; - - tempgroup = lappend(tempgroup, matched_clause); + + /* clausegroup holds all matched clauses ordered by indexkeys */ + + if (clausegroup != NIL) + { + + /* + * if no one join clause was matched then there ain't clauses for + * joins at all. + */ + if (!jfound) + { + freeList(clausegroup); + return NIL; + } + return (lcons(clausegroup, NIL)); } - if ( tempgroup == NIL ) - break; - - clausegroup = nconc (clausegroup, tempgroup); - - indexkeys++; - classes++; - - } - - /* clausegroup holds all matched clauses ordered by indexkeys */ - - if (clausegroup != NIL) - { - /* - * if no one join clause was matched then there ain't clauses - * for joins at all. - */ - if ( !jfound ) - { - freeList (clausegroup); - return NIL; - } - return(lcons(clausegroup, NIL)); - } - return NIL; + return NIL; } /* @@ -537,798 +551,867 @@ group_clauses_by_ikey_for_joins(Rel *rel, * Now we can match with functional indices. */ #define IndexScanableOperand(opnd, indkeys, rel, index) \ - ((index->indproc == InvalidOid) ? \ - match_indexkey_operand(indkeys, opnd, rel) : \ - function_index_operand((Expr*)opnd,rel,index)) + ((index->indproc == InvalidOid) ? \ + match_indexkey_operand(indkeys, opnd, rel) : \ + function_index_operand((Expr*)opnd,rel,index)) /* * There was - * equal_indexkey_var(indkeys,opnd) : \ + * equal_indexkey_var(indkeys,opnd) : \ * above, and now - * match_indexkey_operand(indkeys, opnd, rel) : \ + * match_indexkey_operand(indkeys, opnd, rel) : \ * - vadim 01/22/97 */ -/* +/* * match_clause_to-indexkey-- - * Finds the first of a relation's available restriction clauses that - * matches a key of an index. - * - * To match, the clause must: - * (1) be in the form (op var const) if the clause is a single- - * relation clause, and - * (2) contain an operator which is in the same class as the index - * operator for this key. - * - * If the clause being matched is a join clause, then 'join' is t. - * - * Returns a single clauseinfo node corresponding to the matching + * Finds the first of a relation's available restriction clauses that + * matches a key of an index. + * + * To match, the clause must: + * (1) be in the form (op var const) if the clause is a single- + * relation clause, and + * (2) contain an operator which is in the same class as the index + * operator for this key. + * + * If the clause being matched is a join clause, then 'join' is t. + * + * Returns a single clauseinfo node corresponding to the matching * clause. * * NOTE: returns nil if clause is an or_clause. - * + * */ -static CInfo * -match_clause_to_indexkey(Rel *rel, - Rel *index, - int indexkey, - int xclass, - CInfo *clauseInfo, - bool join) +static CInfo * +match_clause_to_indexkey(Rel * rel, + Rel * index, + int indexkey, + int xclass, + CInfo * clauseInfo, + bool join) { - Expr *clause = clauseInfo->clause; - Var *leftop, *rightop; - Oid join_op = InvalidOid; - Oid restrict_op = InvalidOid; - bool isIndexable = false; - - if (or_clause((Node*)clause) || - not_clause((Node*)clause) || single_node((Node*)clause)) - return ((CInfo*)NULL); - - leftop = get_leftop(clause); - rightop = get_rightop(clause); - /* - * If this is not a join clause, check for clauses of the form: - * (operator var/func constant) and (operator constant var/func) - */ - if (!join) - { + Expr *clause = clauseInfo->clause; + Var *leftop, + *rightop; + Oid join_op = InvalidOid; + Oid restrict_op = InvalidOid; + bool isIndexable = false; + + if (or_clause((Node *) clause) || + not_clause((Node *) clause) || single_node((Node *) clause)) + return ((CInfo *) NULL); + + leftop = get_leftop(clause); + rightop = get_rightop(clause); + /* - * Check for standard s-argable clause + * If this is not a join clause, check for clauses of the form: + * (operator var/func constant) and (operator constant var/func) */ -#ifdef INDEXSCAN_PATCH - /* Handle also function parameters. DZ - 27-8-1996 */ - if ((rightop && IsA(rightop,Const)) || - (rightop && IsA(rightop,Param))) -#else - if (rightop && IsA(rightop,Const)) -#endif + if (!join) { - restrict_op = ((Oper*)((Expr*)clause)->oper)->opno; - isIndexable = - ( op_class(restrict_op, xclass, index->relam) && - IndexScanableOperand(leftop, - indexkey, - rel, - index) ); - } - /* - * Must try to commute the clause to standard s-arg format. - */ + /* + * Check for standard s-argable clause + */ #ifdef INDEXSCAN_PATCH - /* ...And here... - vadim 01/22/97 */ - else if ((leftop && IsA(leftop,Const)) || - (leftop && IsA(leftop,Param))) + /* Handle also function parameters. DZ - 27-8-1996 */ + if ((rightop && IsA(rightop, Const)) || + (rightop && IsA(rightop, Param))) #else - else if (leftop && IsA(leftop,Const)) + if (rightop && IsA(rightop, Const)) #endif - { - restrict_op = - get_commutator(((Oper*)((Expr*)clause)->oper)->opno); - - if ( (restrict_op != InvalidOid) && - op_class(restrict_op, xclass, index->relam) && - IndexScanableOperand(rightop, - indexkey,rel,index) ) - { - isIndexable = true; + { + restrict_op = ((Oper *) ((Expr *) clause)->oper)->opno; + isIndexable = + (op_class(restrict_op, xclass, index->relam) && + IndexScanableOperand(leftop, + indexkey, + rel, + index)); + } + /* - * In place list modification. - * (op const var/func) -> (op var/func const) + * Must try to commute the clause to standard s-arg format. */ - CommuteClause((Node*)clause); - } - } - } - /* - * Check for an indexable scan on one of the join relations. - * clause is of the form (operator var/func var/func) - */ - else - { - if (rightop - && match_index_to_operand(indexkey,(Expr*)rightop,rel,index)) - { - - join_op = get_commutator(((Oper*)((Expr*)clause)->oper)->opno); - - } else if (leftop - && match_index_to_operand(indexkey, - (Expr*)leftop,rel,index)) - { - join_op = ((Oper*)((Expr*)clause)->oper)->opno; +#ifdef INDEXSCAN_PATCH + /* ...And here... - vadim 01/22/97 */ + else if ((leftop && IsA(leftop, Const)) || + (leftop && IsA(leftop, Param))) +#else + else if (leftop && IsA(leftop, Const)) +#endif + { + restrict_op = + get_commutator(((Oper *) ((Expr *) clause)->oper)->opno); + + if ((restrict_op != InvalidOid) && + op_class(restrict_op, xclass, index->relam) && + IndexScanableOperand(rightop, + indexkey, rel, index)) + { + isIndexable = true; + + /* + * In place list modification. (op const var/func) -> (op + * var/func const) + */ + CommuteClause((Node *) clause); + } + } } - if ( join_op && op_class(join_op,xclass,index->relam) && - join_clause_p((Node*)clause)) + /* + * Check for an indexable scan on one of the join relations. clause is + * of the form (operator var/func var/func) + */ + else { - isIndexable = true; - - /* - * If we're using the operand's commutator we must - * commute the clause. - */ - if (join_op != ((Oper*)((Expr*)clause)->oper)->opno) - CommuteClause((Node*)clause); + if (rightop + && match_index_to_operand(indexkey, (Expr *) rightop, rel, index)) + { + + join_op = get_commutator(((Oper *) ((Expr *) clause)->oper)->opno); + + } + else if (leftop + && match_index_to_operand(indexkey, + (Expr *) leftop, rel, index)) + { + join_op = ((Oper *) ((Expr *) clause)->oper)->opno; + } + + if (join_op && op_class(join_op, xclass, index->relam) && + join_clause_p((Node *) clause)) + { + isIndexable = true; + + /* + * If we're using the operand's commutator we must commute the + * clause. + */ + if (join_op != ((Oper *) ((Expr *) clause)->oper)->opno) + CommuteClause((Node *) clause); + } } - } - if (isIndexable) - return(clauseInfo); + if (isIndexable) + return (clauseInfo); - return(NULL); + return (NULL); } /**************************************************************************** - * ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ---- + * ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ---- ****************************************************************************/ -/* +/* * pred_test-- - * Does the "predicate inclusion test" for partial indexes. + * Does the "predicate inclusion test" for partial indexes. * - * Recursively checks whether the clauses in clauseinfo_list imply - * that the given predicate is true. + * Recursively checks whether the clauses in clauseinfo_list imply + * that the given predicate is true. * - * This routine (together with the routines it calls) iterates over - * ANDs in the predicate first, then reduces the qualification - * clauses down to their constituent terms, and iterates over ORs - * in the predicate last. This order is important to make the test - * succeed whenever possible (assuming the predicate has been - * successfully cnfify()-ed). --Nels, Jan '93 + * This routine (together with the routines it calls) iterates over + * ANDs in the predicate first, then reduces the qualification + * clauses down to their constituent terms, and iterates over ORs + * in the predicate last. This order is important to make the test + * succeed whenever possible (assuming the predicate has been + * successfully cnfify()-ed). --Nels, Jan '93 */ -static bool -pred_test(List *predicate_list, List *clauseinfo_list, List *joininfo_list) +static bool +pred_test(List * predicate_list, List * clauseinfo_list, List * joininfo_list) { - List *pred, *items, *item; - - /* - * Note: if Postgres tried to optimize queries by forming equivalence - * classes over equi-joined attributes (i.e., if it recognized that a - * qualification such as "where a.b=c.d and a.b=5" could make use of - * an index on c.d), then we could use that equivalence class info - * here with joininfo_list to do more complete tests for the usability - * of a partial index. For now, the test only uses restriction - * clauses (those in clauseinfo_list). --Nels, Dec '92 - */ - - if (predicate_list == NULL) - return true; /* no predicate: the index is usable */ - if (clauseinfo_list == NULL) - return false; /* no restriction clauses: the test must fail */ - - foreach (pred, predicate_list) { - /* if any clause is not implied, the whole predicate is not implied */ - if (and_clause(lfirst(pred))) { - items = ((Expr*)lfirst(pred))->args; - foreach (item, items) { - if (!one_pred_test(lfirst(item), clauseinfo_list)) - return false; - } + List *pred, + *items, + *item; + + /* + * Note: if Postgres tried to optimize queries by forming equivalence + * classes over equi-joined attributes (i.e., if it recognized that a + * qualification such as "where a.b=c.d and a.b=5" could make use of + * an index on c.d), then we could use that equivalence class info + * here with joininfo_list to do more complete tests for the usability + * of a partial index. For now, the test only uses restriction + * clauses (those in clauseinfo_list). --Nels, Dec '92 + */ + + if (predicate_list == NULL) + return true; /* no predicate: the index is usable */ + if (clauseinfo_list == NULL) + return false; /* no restriction clauses: the test must + * fail */ + + foreach(pred, predicate_list) + { + + /* + * if any clause is not implied, the whole predicate is not + * implied + */ + if (and_clause(lfirst(pred))) + { + items = ((Expr *) lfirst(pred))->args; + foreach(item, items) + { + if (!one_pred_test(lfirst(item), clauseinfo_list)) + return false; + } + } + else if (!one_pred_test(lfirst(pred), clauseinfo_list)) + return false; } - else if (!one_pred_test(lfirst(pred), clauseinfo_list)) - return false; - } - return true; + return true; } -/* +/* * one_pred_test-- - * Does the "predicate inclusion test" for one conjunct of a predicate - * expression. + * Does the "predicate inclusion test" for one conjunct of a predicate + * expression. */ -static bool -one_pred_test(Expr *predicate, List *clauseinfo_list) +static bool +one_pred_test(Expr * predicate, List * clauseinfo_list) { - CInfo *clauseinfo; - List *item; - - Assert(predicate != NULL); - foreach (item, clauseinfo_list) { - clauseinfo = (CInfo *)lfirst(item); - /* if any clause implies the predicate, return true */ - if (one_pred_clause_expr_test(predicate, (Node*)clauseinfo->clause)) - return true; - } - return false; + CInfo *clauseinfo; + List *item; + + Assert(predicate != NULL); + foreach(item, clauseinfo_list) + { + clauseinfo = (CInfo *) lfirst(item); + /* if any clause implies the predicate, return true */ + if (one_pred_clause_expr_test(predicate, (Node *) clauseinfo->clause)) + return true; + } + return false; } -/* +/* * one_pred_clause_expr_test-- - * Does the "predicate inclusion test" for a general restriction-clause - * expression. + * Does the "predicate inclusion test" for a general restriction-clause + * expression. */ -static bool -one_pred_clause_expr_test(Expr *predicate, Node *clause) +static bool +one_pred_clause_expr_test(Expr * predicate, Node * clause) { - List *items, *item; - - if (is_opclause(clause)) - return one_pred_clause_test(predicate, clause); - else if (or_clause(clause)) { - items = ((Expr*)clause)->args; - foreach (item, items) { - /* if any OR item doesn't imply the predicate, clause doesn't */ - if (!one_pred_clause_expr_test(predicate, lfirst(item))) + List *items, + *item; + + if (is_opclause(clause)) + return one_pred_clause_test(predicate, clause); + else if (or_clause(clause)) + { + items = ((Expr *) clause)->args; + foreach(item, items) + { + /* if any OR item doesn't imply the predicate, clause doesn't */ + if (!one_pred_clause_expr_test(predicate, lfirst(item))) + return false; + } + return true; + } + else if (and_clause(clause)) + { + items = ((Expr *) clause)->args; + foreach(item, items) + { + + /* + * if any AND item implies the predicate, the whole clause + * does + */ + if (one_pred_clause_expr_test(predicate, lfirst(item))) + return true; + } return false; } - return true; - }else if (and_clause(clause)) { - items = ((Expr*)clause)->args; - foreach (item, items) { - /* if any AND item implies the predicate, the whole clause does */ - if (one_pred_clause_expr_test(predicate, lfirst(item))) - return true; + else + { + /* unknown clause type never implies the predicate */ + return false; } - return false; - }else { - /* unknown clause type never implies the predicate */ - return false; - } } -/* +/* * one_pred_clause_test-- - * Does the "predicate inclusion test" for one conjunct of a predicate - * expression for a simple restriction clause. + * Does the "predicate inclusion test" for one conjunct of a predicate + * expression for a simple restriction clause. */ -static bool -one_pred_clause_test(Expr *predicate, Node *clause) +static bool +one_pred_clause_test(Expr * predicate, Node * clause) { - List *items, *item; - - if (is_opclause((Node*)predicate)) - return clause_pred_clause_test(predicate, clause); - else if (or_clause((Node*)predicate)) { - items = predicate->args; - foreach (item, items) { - /* if any item is implied, the whole predicate is implied */ - if (one_pred_clause_test(lfirst(item), clause)) + List *items, + *item; + + if (is_opclause((Node *) predicate)) + return clause_pred_clause_test(predicate, clause); + else if (or_clause((Node *) predicate)) + { + items = predicate->args; + foreach(item, items) + { + /* if any item is implied, the whole predicate is implied */ + if (one_pred_clause_test(lfirst(item), clause)) + return true; + } + return false; + } + else if (and_clause((Node *) predicate)) + { + items = predicate->args; + foreach(item, items) + { + + /* + * if any item is not implied, the whole predicate is not + * implied + */ + if (!one_pred_clause_test(lfirst(item), clause)) + return false; + } return true; } - return false; - }else if (and_clause((Node*)predicate)) { - items = predicate->args; - foreach (item, items) { - /* - * if any item is not implied, the whole predicate is not - * implied - */ - if (!one_pred_clause_test(lfirst(item), clause)) + else + { + elog(DEBUG, "Unsupported predicate type, index will not be used"); return false; } - return true; - } - else { - elog(DEBUG, "Unsupported predicate type, index will not be used"); - return false; - } } /* * Define an "operator implication table" for btree operators ("strategies"). - * The "strategy numbers" are: (1) < (2) <= (3) = (4) >= (5) > + * The "strategy numbers" are: (1) < (2) <= (3) = (4) >= (5) > * * The interpretation of: * - * test_op = BT_implic_table[given_op-1][target_op-1] + * test_op = BT_implic_table[given_op-1][target_op-1] * * where test_op, given_op and target_op are strategy numbers (from 1 to 5) * of btree operators, is as follows: * - * If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you - * want to determine whether "ATTR target_op CONST2" must also be true, then - * you can use "CONST1 test_op CONST2" as a test. If this test returns true, - * then the target expression must be true; if the test returns false, then - * the target expression may be false. + * If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you + * want to determine whether "ATTR target_op CONST2" must also be true, then + * you can use "CONST1 test_op CONST2" as a test. If this test returns true, + * then the target expression must be true; if the test returns false, then + * the target expression may be false. * * An entry where test_op==0 means the implication cannot be determined, i.e., * this test should always be considered false. */ -StrategyNumber BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = { - {2, 2, 0, 0, 0}, - {1, 2, 0, 0, 0}, - {1, 2, 3, 4, 5}, - {0, 0, 0, 4, 5}, - {0, 0, 0, 4, 4} +StrategyNumber BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = { + {2, 2, 0, 0, 0}, + {1, 2, 0, 0, 0}, + {1, 2, 3, 4, 5}, + {0, 0, 0, 4, 5}, + {0, 0, 0, 4, 4} }; -/* +/* * clause_pred_clause_test-- - * Use operator class info to check whether clause implies predicate. - * - * Does the "predicate inclusion test" for a "simple clause" predicate - * for a single "simple clause" restriction. Currently, this only handles - * (binary boolean) operators that are in some btree operator class. - * Eventually, rtree operators could also be handled by defining an - * appropriate "RT_implic_table" array. + * Use operator class info to check whether clause implies predicate. + * + * Does the "predicate inclusion test" for a "simple clause" predicate + * for a single "simple clause" restriction. Currently, this only handles + * (binary boolean) operators that are in some btree operator class. + * Eventually, rtree operators could also be handled by defining an + * appropriate "RT_implic_table" array. */ -static bool -clause_pred_clause_test(Expr *predicate, Node *clause) +static bool +clause_pred_clause_test(Expr * predicate, Node * clause) { - Var *pred_var, *clause_var; - Const *pred_const, *clause_const; - Oid pred_op, clause_op, test_op; - Oid opclass_id; - StrategyNumber pred_strategy, clause_strategy, test_strategy; - Oper *test_oper; - Expr *test_expr; - bool test_result, isNull; - Relation relation; - HeapScanDesc scan; - HeapTuple tuple; - ScanKeyData entry[3]; - Form_pg_amop form; - - pred_var = (Var*)get_leftop(predicate); - pred_const = (Const*)get_rightop(predicate); - clause_var = (Var*)get_leftop((Expr*)clause); - clause_const = (Const*)get_rightop((Expr*)clause); - - /* Check the basic form; for now, only allow the simplest case */ - if (!is_opclause(clause) || - !IsA(clause_var,Var) || - !IsA(clause_const,Const) || - !IsA(predicate->oper,Oper) || - !IsA(pred_var,Var) || - !IsA(pred_const,Const)) { - return false; - } + Var *pred_var, + *clause_var; + Const *pred_const, + *clause_const; + Oid pred_op, + clause_op, + test_op; + Oid opclass_id; + StrategyNumber pred_strategy, + clause_strategy, + test_strategy; + Oper *test_oper; + Expr *test_expr; + bool test_result, + isNull; + Relation relation; + HeapScanDesc scan; + HeapTuple tuple; + ScanKeyData entry[3]; + Form_pg_amop form; + + pred_var = (Var *) get_leftop(predicate); + pred_const = (Const *) get_rightop(predicate); + clause_var = (Var *) get_leftop((Expr *) clause); + clause_const = (Const *) get_rightop((Expr *) clause); + + /* Check the basic form; for now, only allow the simplest case */ + if (!is_opclause(clause) || + !IsA(clause_var, Var) || + !IsA(clause_const, Const) || + !IsA(predicate->oper, Oper) || + !IsA(pred_var, Var) || + !IsA(pred_const, Const)) + { + return false; + } - /* - * The implication can't be determined unless the predicate and the clause - * refer to the same attribute. - */ - if (clause_var->varattno != pred_var->varattno) - return false; + /* + * The implication can't be determined unless the predicate and the + * clause refer to the same attribute. + */ + if (clause_var->varattno != pred_var->varattno) + return false; - /* Get the operators for the two clauses we're comparing */ - pred_op = ((Oper*)((Expr*)predicate)->oper)->opno; - clause_op = ((Oper*)((Expr*)clause)->oper)->opno; - - - /* - * 1. Find a "btree" strategy number for the pred_op - */ - /* XXX - hardcoded amopid value 403 to find "btree" operator classes */ - ScanKeyEntryInitialize(&entry[0], 0, - Anum_pg_amop_amopid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(403)); - - ScanKeyEntryInitialize(&entry[1], 0, - Anum_pg_amop_amopopr, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(pred_op)); - - relation = heap_openr(AccessMethodOperatorRelationName); - - /* - * The following assumes that any given operator will only be in a single - * btree operator class. This is true at least for all the pre-defined - * operator classes. If it isn't true, then whichever operator class - * happens to be returned first for the given operator will be used to - * find the associated strategy numbers for the test. --Nels, Jan '93 - */ - scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); - tuple = heap_getnext(scan, false, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(DEBUG, "clause_pred_clause_test: unknown pred_op"); - return false; - } - form = (Form_pg_amop) GETSTRUCT(tuple); + /* Get the operators for the two clauses we're comparing */ + pred_op = ((Oper *) ((Expr *) predicate)->oper)->opno; + clause_op = ((Oper *) ((Expr *) clause)->oper)->opno; - /* Get the predicate operator's strategy number (1 to 5) */ - pred_strategy = (StrategyNumber)form->amopstrategy; - /* Remember which operator class this strategy number came from */ - opclass_id = form->amopclaid; + /* + * 1. Find a "btree" strategy number for the pred_op + */ + /* XXX - hardcoded amopid value 403 to find "btree" operator classes */ + ScanKeyEntryInitialize(&entry[0], 0, + Anum_pg_amop_amopid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(403)); - heap_endscan(scan); + ScanKeyEntryInitialize(&entry[1], 0, + Anum_pg_amop_amopopr, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(pred_op)); + relation = heap_openr(AccessMethodOperatorRelationName); - /* - * 2. From the same opclass, find a strategy num for the clause_op - */ - ScanKeyEntryInitialize(&entry[1], 0, - Anum_pg_amop_amopclaid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(opclass_id)); + /* + * The following assumes that any given operator will only be in a + * single btree operator class. This is true at least for all the + * pre-defined operator classes. If it isn't true, then whichever + * operator class happens to be returned first for the given operator + * will be used to find the associated strategy numbers for the test. + * --Nels, Jan '93 + */ + scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); + tuple = heap_getnext(scan, false, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(DEBUG, "clause_pred_clause_test: unknown pred_op"); + return false; + } + form = (Form_pg_amop) GETSTRUCT(tuple); - ScanKeyEntryInitialize(&entry[2], 0, - Anum_pg_amop_amopopr, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(clause_op)); + /* Get the predicate operator's strategy number (1 to 5) */ + pred_strategy = (StrategyNumber) form->amopstrategy; - scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); - tuple = heap_getnext(scan, false, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(DEBUG, "clause_pred_clause_test: unknown clause_op"); - return false; - } - form = (Form_pg_amop) GETSTRUCT(tuple); + /* Remember which operator class this strategy number came from */ + opclass_id = form->amopclaid; - /* Get the restriction clause operator's strategy number (1 to 5) */ - clause_strategy = (StrategyNumber)form->amopstrategy; - heap_endscan(scan); + heap_endscan(scan); - /* - * 3. Look up the "test" strategy number in the implication table - */ + /* + * 2. From the same opclass, find a strategy num for the clause_op + */ + ScanKeyEntryInitialize(&entry[1], 0, + Anum_pg_amop_amopclaid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(opclass_id)); + + ScanKeyEntryInitialize(&entry[2], 0, + Anum_pg_amop_amopopr, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(clause_op)); + + scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); + tuple = heap_getnext(scan, false, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(DEBUG, "clause_pred_clause_test: unknown clause_op"); + return false; + } + form = (Form_pg_amop) GETSTRUCT(tuple); - test_strategy = BT_implic_table[clause_strategy-1][pred_strategy-1]; - if (test_strategy == 0) - return false; /* the implication cannot be determined */ + /* Get the restriction clause operator's strategy number (1 to 5) */ + clause_strategy = (StrategyNumber) form->amopstrategy; + heap_endscan(scan); - /* - * 4. From the same opclass, find the operator for the test strategy - */ + /* + * 3. Look up the "test" strategy number in the implication table + */ - ScanKeyEntryInitialize(&entry[2], 0, - Anum_pg_amop_amopstrategy, - Integer16EqualRegProcedure, - Int16GetDatum(test_strategy)); + test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1]; + if (test_strategy == 0) + return false; /* the implication cannot be determined */ - scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); - tuple = heap_getnext(scan, false, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(DEBUG, "clause_pred_clause_test: unknown test_op"); - return false; - } - form = (Form_pg_amop) GETSTRUCT(tuple); - /* Get the test operator */ - test_op = form->amopopr; - heap_endscan(scan); + /* + * 4. From the same opclass, find the operator for the test strategy + */ + ScanKeyEntryInitialize(&entry[2], 0, + Anum_pg_amop_amopstrategy, + Integer16EqualRegProcedure, + Int16GetDatum(test_strategy)); - /* - * 5. Evaluate the test - */ - test_oper = makeOper(test_op, /* opno */ - InvalidOid, /* opid */ - BOOL_TYPEID, /* opresulttype */ - 0, /* opsize */ - NULL); /* op_fcache */ - replace_opid(test_oper); + scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); + tuple = heap_getnext(scan, false, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(DEBUG, "clause_pred_clause_test: unknown test_op"); + return false; + } + form = (Form_pg_amop) GETSTRUCT(tuple); + + /* Get the test operator */ + test_op = form->amopopr; + heap_endscan(scan); - test_expr = make_opclause(test_oper, - copyObject(clause_const), - copyObject(pred_const)); + + /* + * 5. Evaluate the test + */ + test_oper = makeOper(test_op, /* opno */ + InvalidOid, /* opid */ + BOOL_TYPEID, /* opresulttype */ + 0, /* opsize */ + NULL); /* op_fcache */ + replace_opid(test_oper); + + test_expr = make_opclause(test_oper, + copyObject(clause_const), + copyObject(pred_const)); #ifndef OMIT_PARTIAL_INDEX - test_result = ExecEvalExpr((Node*)test_expr, NULL, &isNull, NULL); -#endif /* OMIT_PARTIAL_INDEX */ - if (isNull) { - elog(DEBUG, "clause_pred_clause_test: null test result"); - return false; - } - return test_result; + test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL); +#endif /* OMIT_PARTIAL_INDEX */ + if (isNull) + { + elog(DEBUG, "clause_pred_clause_test: null test result"); + return false; + } + return test_result; } /**************************************************************************** - * ---- ROUTINES TO CHECK JOIN CLAUSES ---- + * ---- ROUTINES TO CHECK JOIN CLAUSES ---- ****************************************************************************/ -/* +/* * indexable-joinclauses-- - * Finds all groups of join clauses from among 'joininfo-list' that can - * be used in conjunction with 'index'. - * - * The first clause in the group is marked as having the other relation - * in the join clause as its outer join relation. - * + * Finds all groups of join clauses from among 'joininfo-list' that can + * be used in conjunction with 'index'. + * + * The first clause in the group is marked as having the other relation + * in the join clause as its outer join relation. + * * Returns a list of these clause groups. * - * Added: clauseinfo_list - list of restriction CInfos. It's to - * support multi-column indices in joins and for cases - * when a key is in both join & restriction clauses. - vadim 03/18/97 - * + * Added: clauseinfo_list - list of restriction CInfos. It's to + * support multi-column indices in joins and for cases + * when a key is in both join & restriction clauses. - vadim 03/18/97 + * */ -static List * -indexable_joinclauses(Rel *rel, Rel *index, - List *joininfo_list, List *clauseinfo_list) +static List * +indexable_joinclauses(Rel * rel, Rel * index, + List * joininfo_list, List * clauseinfo_list) { - JInfo *joininfo = (JInfo*)NULL; - List *cg_list = NIL; - List *i = NIL; - List *clausegroups = NIL; - - foreach(i,joininfo_list) { - joininfo = (JInfo*)lfirst(i); - - if ( joininfo->jinfoclauseinfo == NIL ) - continue; - clausegroups = - group_clauses_by_ikey_for_joins (rel, - index, - index->indexkeys, - index->classlist, - joininfo->jinfoclauseinfo, - clauseinfo_list); - - if (clausegroups != NIL) { - List *clauses = lfirst(clausegroups); - - ((CInfo*)lfirst(clauses))->cinfojoinid = - joininfo->otherrels; + JInfo *joininfo = (JInfo *) NULL; + List *cg_list = NIL; + List *i = NIL; + List *clausegroups = NIL; + + foreach(i, joininfo_list) + { + joininfo = (JInfo *) lfirst(i); + + if (joininfo->jinfoclauseinfo == NIL) + continue; + clausegroups = + group_clauses_by_ikey_for_joins(rel, + index, + index->indexkeys, + index->classlist, + joininfo->jinfoclauseinfo, + clauseinfo_list); + + if (clausegroups != NIL) + { + List *clauses = lfirst(clausegroups); + + ((CInfo *) lfirst(clauses))->cinfojoinid = + joininfo->otherrels; + } + cg_list = nconc(cg_list, clausegroups); } - cg_list = nconc(cg_list,clausegroups); - } - return(cg_list); + return (cg_list); } /**************************************************************************** - * ---- PATH CREATION UTILITIES ---- + * ---- PATH CREATION UTILITIES ---- ****************************************************************************/ /* * extract_restrict_clauses - - * the list of clause info contains join clauses and restriction clauses. - * This routine returns the restriction clauses only. + * the list of clause info contains join clauses and restriction clauses. + * This routine returns the restriction clauses only. */ #ifdef NOT_USED -static List * -extract_restrict_clauses(List *clausegroup) +static List * +extract_restrict_clauses(List * clausegroup) { - List *restrict_cls = NIL; - List *l; - - foreach (l, clausegroup) { - CInfo *cinfo = lfirst(l); - - if (!join_clause_p((Node*)cinfo->clause)) { - restrict_cls = lappend(restrict_cls, cinfo); + List *restrict_cls = NIL; + List *l; + + foreach(l, clausegroup) + { + CInfo *cinfo = lfirst(l); + + if (!join_clause_p((Node *) cinfo->clause)) + { + restrict_cls = lappend(restrict_cls, cinfo); + } } - } - return restrict_cls; + return restrict_cls; } + #endif -/* +/* * index-innerjoin-- - * Creates index path nodes corresponding to paths to be used as inner - * relations in nestloop joins. + * Creates index path nodes corresponding to paths to be used as inner + * relations in nestloop joins. * * 'clausegroup-list' is a list of list of clauseinfo nodes which can use * 'index' on their inner relation. - * + * * Returns a list of index pathnodes. - * + * */ -static List * -index_innerjoin(Query *root, Rel *rel, List *clausegroup_list, Rel *index) +static List * +index_innerjoin(Query * root, Rel * rel, List * clausegroup_list, Rel * index) { - List *clausegroup = NIL; - List *cg_list = NIL; - List *i = NIL; - IndexPath *pathnode = (IndexPath*)NULL; - Cost temp_selec; - float temp_pages; - - foreach(i,clausegroup_list) { - List *attnos, *values, *flags; - - clausegroup = lfirst(i); - pathnode = makeNode(IndexPath); - - get_joinvars(lfirsti(rel->relids),clausegroup, - &attnos, &values, &flags); - index_selectivity(lfirsti(index->relids), - index->classlist, - get_opnos(clausegroup), - getrelid(lfirsti(rel->relids), - root->rtable), - attnos, - values, - flags, - length(clausegroup), - &temp_pages, - &temp_selec); - pathnode->path.pathtype = T_IndexScan; - pathnode->path.parent = rel; - pathnode->indexid = index->relids; - pathnode->indexkeys = index->indexkeys; - pathnode->indexqual = clausegroup; - - pathnode->path.joinid = ((CInfo*)lfirst(clausegroup))->cinfojoinid; - - pathnode->path.path_cost = - cost_index((Oid)lfirsti(index->relids), - (int)temp_pages, - temp_selec, - rel->pages, - rel->tuples, - index->pages, - index->tuples, - true); - - /* copy clauseinfo list into path for expensive function processing - -- JMH, 7/7/92 */ - pathnode->path.locclauseinfo = - set_difference(copyObject((Node*)rel->clauseinfo), - clausegroup); - -#if 0 /* fix xfunc */ - /* add in cost for expensive functions! -- JMH, 7/7/92 */ - if (XfuncMode != XFUNC_OFF) { - ((Path*)pathnode)->path_cost += - xfunc_get_path_cost((Path*)pathnode); - } + List *clausegroup = NIL; + List *cg_list = NIL; + List *i = NIL; + IndexPath *pathnode = (IndexPath *) NULL; + Cost temp_selec; + float temp_pages; + + foreach(i, clausegroup_list) + { + List *attnos, + *values, + *flags; + + clausegroup = lfirst(i); + pathnode = makeNode(IndexPath); + + get_joinvars(lfirsti(rel->relids), clausegroup, + &attnos, &values, &flags); + index_selectivity(lfirsti(index->relids), + index->classlist, + get_opnos(clausegroup), + getrelid(lfirsti(rel->relids), + root->rtable), + attnos, + values, + flags, + length(clausegroup), + &temp_pages, + &temp_selec); + pathnode->path.pathtype = T_IndexScan; + pathnode->path.parent = rel; + pathnode->indexid = index->relids; + pathnode->indexkeys = index->indexkeys; + pathnode->indexqual = clausegroup; + + pathnode->path.joinid = ((CInfo *) lfirst(clausegroup))->cinfojoinid; + + pathnode->path.path_cost = + cost_index((Oid) lfirsti(index->relids), + (int) temp_pages, + temp_selec, + rel->pages, + rel->tuples, + index->pages, + index->tuples, + true); + + /* + * copy clauseinfo list into path for expensive function + * processing -- JMH, 7/7/92 + */ + pathnode->path.locclauseinfo = + set_difference(copyObject((Node *) rel->clauseinfo), + clausegroup); + +#if 0 /* fix xfunc */ + /* add in cost for expensive functions! -- JMH, 7/7/92 */ + if (XfuncMode != XFUNC_OFF) + { + ((Path *) pathnode)->path_cost += + xfunc_get_path_cost((Path *) pathnode); + } #endif - cg_list = lappend(cg_list,pathnode); - } - return(cg_list); + cg_list = lappend(cg_list, pathnode); + } + return (cg_list); } -/* +/* * create-index-paths-- - * Creates a list of index path nodes for each group of clauses - * (restriction or join) that can be used in conjunction with an index. - * + * Creates a list of index path nodes for each group of clauses + * (restriction or join) that can be used in conjunction with an index. + * * 'rel' is the relation for which 'index' is defined - * 'clausegroup-list' is the list of clause groups (lists of clauseinfo - * nodes) grouped by mergesortorder + * 'clausegroup-list' is the list of clause groups (lists of clauseinfo + * nodes) grouped by mergesortorder * 'join' is a flag indicating whether or not the clauses are join - * clauses - * + * clauses + * * Returns a list of new index path nodes. - * + * */ -static List * -create_index_paths(Query *root, - Rel *rel, - Rel *index, - List *clausegroup_list, - bool join) +static List * +create_index_paths(Query * root, + Rel * rel, + Rel * index, + List * clausegroup_list, + bool join) { - List *clausegroup = NIL; - List *ip_list = NIL; - List *i = NIL; - List *j = NIL; - IndexPath *temp_path; - - foreach(i, clausegroup_list) { - CInfo *clauseinfo; - List *temp_node = NIL; - bool temp = true; - - clausegroup = lfirst(i); - - foreach (j,clausegroup) { - clauseinfo = (CInfo*)lfirst(j); - if (!(join_clause_p((Node*)clauseinfo->clause) && - equal_path_merge_ordering(index->ordering, - clauseinfo->mergesortorder))) { - temp = false; - } - } + List *clausegroup = NIL; + List *ip_list = NIL; + List *i = NIL; + List *j = NIL; + IndexPath *temp_path; - if (!join || temp) { /* restriction, ordering scan */ - temp_path = create_index_path (root, rel,index,clausegroup,join); - temp_node = - lcons(temp_path, NIL); - ip_list = nconc(ip_list,temp_node); - } - } - return(ip_list); + foreach(i, clausegroup_list) + { + CInfo *clauseinfo; + List *temp_node = NIL; + bool temp = true; + + clausegroup = lfirst(i); + + foreach(j, clausegroup) + { + clauseinfo = (CInfo *) lfirst(j); + if (!(join_clause_p((Node *) clauseinfo->clause) && + equal_path_merge_ordering(index->ordering, + clauseinfo->mergesortorder))) + { + temp = false; + } + } + + if (!join || temp) + { /* restriction, ordering scan */ + temp_path = create_index_path(root, rel, index, clausegroup, join); + temp_node = + lcons(temp_path, NIL); + ip_list = nconc(ip_list, temp_node); + } + } + return (ip_list); } -static List * -add_index_paths(List *indexpaths, List *new_indexpaths) +static List * +add_index_paths(List * indexpaths, List * new_indexpaths) { - return append(indexpaths, new_indexpaths); + return append(indexpaths, new_indexpaths); } -static bool -function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index) +static bool +function_index_operand(Expr * funcOpnd, Rel * rel, Rel * index) { - Oid heapRelid = (Oid)lfirsti(rel->relids); - Func *function; - List *funcargs; - int *indexKeys = index->indexkeys; - List *arg; - int i; - - /* - * sanity check, make sure we know what we're dealing with here. - */ - if (funcOpnd==NULL || - nodeTag(funcOpnd)!=T_Expr || funcOpnd->opType!=FUNC_EXPR || - funcOpnd->oper==NULL || indexKeys==NULL) - return false; + Oid heapRelid = (Oid) lfirsti(rel->relids); + Func *function; + List *funcargs; + int *indexKeys = index->indexkeys; + List *arg; + int i; - function = (Func*)funcOpnd->oper; - funcargs = funcOpnd->args; + /* + * sanity check, make sure we know what we're dealing with here. + */ + if (funcOpnd == NULL || + nodeTag(funcOpnd) != T_Expr || funcOpnd->opType != FUNC_EXPR || + funcOpnd->oper == NULL || indexKeys == NULL) + return false; - if (function->funcid != index->indproc) - return false; + function = (Func *) funcOpnd->oper; + funcargs = funcOpnd->args; + + if (function->funcid != index->indproc) + return false; + + /* + * Check that the arguments correspond to the same arguments used to + * create the functional index. To do this we must check that 1. + * refer to the right relatiion. 2. the args have the right attr. + * numbers in the right order. + * + * + * Check all args refer to the correct relation (i.e. the one with the + * functional index defined on it (rel). To do this we can simply + * compare range table entry numbers, they must be the same. + */ + foreach(arg, funcargs) + { + if (heapRelid != ((Var *) lfirst(arg))->varno) + return false; + } + + /* + * check attr numbers and order. + */ + i = 0; + foreach(arg, funcargs) + { + + if (indexKeys[i] == 0) + return (false); - /* - * Check that the arguments correspond to the same arguments used - * to create the functional index. To do this we must check that - * 1. refer to the right relatiion. - * 2. the args have the right attr. numbers in the right order. - * - * - * Check all args refer to the correct relation (i.e. the one with - * the functional index defined on it (rel). To do this we can - * simply compare range table entry numbers, they must be the same. - */ - foreach (arg, funcargs) { - if (heapRelid != ((Var*)lfirst(arg))->varno) - return false; - } - - /* - * check attr numbers and order. - */ - i = 0; - foreach (arg, funcargs) { - - if (indexKeys[i]==0) - return (false); - - if (((Var*)lfirst(arg))->varattno != indexKeys[i]) - return (false); - - i++; - } - - return true; + if (((Var *) lfirst(arg))->varattno != indexKeys[i]) + return (false); + + i++; + } + + return true; } -static bool -SingleAttributeIndex(Rel *index) +static bool +SingleAttributeIndex(Rel * index) { - /* - * return false for now as I don't know if we support index scans - * on disjunction and the code doesn't work - */ - return (false); + + /* + * return false for now as I don't know if we support index scans on + * disjunction and the code doesn't work + */ + return (false); #if 0 - /* - * Non-functional indices. - */ - if (index->indproc == InvalidOid) - return (index->indexkeys[0] != 0 && - index->indexkeys[1] == 0); - - /* - * We have a functional index which is a single attr index - */ - return true; + + /* + * Non-functional indices. + */ + if (index->indproc == InvalidOid) + return (index->indexkeys[0] != 0 && + index->indexkeys[1] == 0); + + /* + * We have a functional index which is a single attr index + */ + return true; #endif } diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 87365278ffa..c20558cf42b 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * joinpath.c-- - * Routines to find all possible paths for processing a set of joins + * Routines to find all possible paths for processing a set of joins * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.2 1996/10/31 10:59:00 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.3 1997/09/07 04:43:38 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,269 +26,286 @@ #include "optimizer/paths.h" #include "optimizer/pathnode.h" #include "optimizer/keys.h" -#include "optimizer/cost.h" /* for _enable_{hashjoin, _enable_mergesort} */ - -static Path *best_innerjoin(List *join_paths, List *outer_relid); -static List *sort_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *mergeinfo_list); -static List *match_unsorted_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *outerpath_list, Path *cheapest_inner, Path *best_innerjoin, - List *mergeinfo_list); -static List *match_unsorted_inner(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *innerpath_list, List *mergeinfo_list); -static bool EnoughMemoryForHashjoin(Rel *hashrel); -static List *hash_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *hashinfo_list); - -/* +#include "optimizer/cost.h" /* for _enable_{hashjoin, + * _enable_mergesort} */ + +static Path *best_innerjoin(List * join_paths, List * outer_relid); +static List * +sort_inner_and_outer(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * mergeinfo_list); +static List * +match_unsorted_outer(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * outerpath_list, Path * cheapest_inner, Path * best_innerjoin, + List * mergeinfo_list); +static List * +match_unsorted_inner(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * innerpath_list, List * mergeinfo_list); +static bool EnoughMemoryForHashjoin(Rel * hashrel); +static List * +hash_inner_and_outer(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * hashinfo_list); + +/* * find-all-join-paths-- - * Creates all possible ways to process joins for each of the join - * relations in the list 'joinrels.' Each unique path will be included - * in the join relation's 'pathlist' field. - * - * In postgres, n-way joins are handled left-only(permuting clauseless - * joins doesn't usually win much). - * - * if BushyPlanFlag is true, bushy tree plans will be generated + * Creates all possible ways to process joins for each of the join + * relations in the list 'joinrels.' Each unique path will be included + * in the join relation's 'pathlist' field. + * + * In postgres, n-way joins are handled left-only(permuting clauseless + * joins doesn't usually win much). + * + * if BushyPlanFlag is true, bushy tree plans will be generated * * 'joinrels' is the list of relation entries to be joined - * + * * Modifies the pathlist field of the appropriate rel node to contain * the unique join paths. * If bushy trees are considered, may modify the relid field of the * join rel nodes to flatten the lists. - * - * Returns nothing of interest. (?) + * + * Returns nothing of interest. (?) * It does a destructive modification. */ void -find_all_join_paths(Query *root, List *joinrels) +find_all_join_paths(Query * root, List * joinrels) { - List *mergeinfo_list = NIL; - List *hashinfo_list = NIL; - List *temp_list = NIL; - List *path = NIL; - - while (joinrels != NIL) { - Rel *joinrel = (Rel *)lfirst(joinrels); - List *innerrelids; - List *outerrelids; - Rel *innerrel; - Rel *outerrel; - Path *bestinnerjoin; - List *pathlist = NIL; - - innerrelids = lsecond(joinrel->relids); - outerrelids = lfirst(joinrel->relids); - - /* - * base relation id is an integer and join relation relid is a - * list of integers. - */ - innerrel = (length(innerrelids)==1)? - get_base_rel(root, lfirsti(innerrelids)) : get_join_rel(root,innerrelids); - outerrel = (length(outerrelids)==1)? - get_base_rel(root, lfirsti(outerrelids)) : get_join_rel(root, outerrelids); - - bestinnerjoin = best_innerjoin(innerrel->innerjoin, - outerrel->relids); - if( _enable_mergesort_ ) { - mergeinfo_list = - group_clauses_by_order(joinrel->clauseinfo, - lfirsti(innerrel->relids)); - } - - if( _enable_hashjoin_ ) { - hashinfo_list = - group_clauses_by_hashop(joinrel->clauseinfo, - lfirsti(innerrel->relids)); - } - - /* need to flatten the relids list */ - joinrel->relids = intAppend(outerrelids, innerrelids); - - /* - * 1. Consider mergesort paths where both relations must be - * explicitly sorted. - */ - pathlist = sort_inner_and_outer(joinrel,outerrel, - innerrel,mergeinfo_list); - - /* - * 2. Consider paths where the outer relation need not be explicitly - * sorted. This may include either nestloops and mergesorts where - * the outer path is already ordered. - */ - pathlist = - add_pathlist(joinrel, pathlist, - match_unsorted_outer(joinrel, - outerrel, - innerrel, - outerrel->pathlist, - (Path*)innerrel->cheapestpath, - bestinnerjoin, - mergeinfo_list)); - - /* - * 3. Consider paths where the inner relation need not be explicitly - * sorted. This may include nestloops and mergesorts the actual - * nestloop nodes were constructed in (match-unsorted-outer). - */ - pathlist = - add_pathlist(joinrel,pathlist, - match_unsorted_inner(joinrel,outerrel, - innerrel, - innerrel->pathlist, - mergeinfo_list)); - - /* - * 4. Consider paths where both outer and inner relations must be - * hashed before being joined. - */ - - pathlist = - add_pathlist(joinrel, pathlist, - hash_inner_and_outer(joinrel,outerrel, - innerrel,hashinfo_list)); - - joinrel->pathlist = pathlist; - - /* - * 'OuterJoinCost is only valid when calling (match-unsorted-inner) - * with the same arguments as the previous invokation of - * (match-unsorted-outer), so clear the field before going on. - */ - temp_list = innerrel->pathlist; - foreach(path, temp_list) { - - /* - * XXX - * - * This gross hack is to get around an apparent optimizer bug on - * Sparc (or maybe it is a bug of ours?) that causes really wierd - * behavior. - */ - if (IsA_JoinPath(path)) { - ((Path*)lfirst(path))->outerjoincost = (Cost) 0; - } - - /* do it iff it is a join path, which is not always - true, esp since the base level */ + List *mergeinfo_list = NIL; + List *hashinfo_list = NIL; + List *temp_list = NIL; + List *path = NIL; + + while (joinrels != NIL) + { + Rel *joinrel = (Rel *) lfirst(joinrels); + List *innerrelids; + List *outerrelids; + Rel *innerrel; + Rel *outerrel; + Path *bestinnerjoin; + List *pathlist = NIL; + + innerrelids = lsecond(joinrel->relids); + outerrelids = lfirst(joinrel->relids); + + /* + * base relation id is an integer and join relation relid is a + * list of integers. + */ + innerrel = (length(innerrelids) == 1) ? + get_base_rel(root, lfirsti(innerrelids)) : get_join_rel(root, innerrelids); + outerrel = (length(outerrelids) == 1) ? + get_base_rel(root, lfirsti(outerrelids)) : get_join_rel(root, outerrelids); + + bestinnerjoin = best_innerjoin(innerrel->innerjoin, + outerrel->relids); + if (_enable_mergesort_) + { + mergeinfo_list = + group_clauses_by_order(joinrel->clauseinfo, + lfirsti(innerrel->relids)); + } + + if (_enable_hashjoin_) + { + hashinfo_list = + group_clauses_by_hashop(joinrel->clauseinfo, + lfirsti(innerrel->relids)); + } + + /* need to flatten the relids list */ + joinrel->relids = intAppend(outerrelids, innerrelids); + + /* + * 1. Consider mergesort paths where both relations must be + * explicitly sorted. + */ + pathlist = sort_inner_and_outer(joinrel, outerrel, + innerrel, mergeinfo_list); + + /* + * 2. Consider paths where the outer relation need not be + * explicitly sorted. This may include either nestloops and + * mergesorts where the outer path is already ordered. + */ + pathlist = + add_pathlist(joinrel, pathlist, + match_unsorted_outer(joinrel, + outerrel, + innerrel, + outerrel->pathlist, + (Path *) innerrel->cheapestpath, + bestinnerjoin, + mergeinfo_list)); + + /* + * 3. Consider paths where the inner relation need not be + * explicitly sorted. This may include nestloops and mergesorts + * the actual nestloop nodes were constructed in + * (match-unsorted-outer). + */ + pathlist = + add_pathlist(joinrel, pathlist, + match_unsorted_inner(joinrel, outerrel, + innerrel, + innerrel->pathlist, + mergeinfo_list)); + + /* + * 4. Consider paths where both outer and inner relations must be + * hashed before being joined. + */ + + pathlist = + add_pathlist(joinrel, pathlist, + hash_inner_and_outer(joinrel, outerrel, + innerrel, hashinfo_list)); + + joinrel->pathlist = pathlist; + + /* + * 'OuterJoinCost is only valid when calling + * (match-unsorted-inner) with the same arguments as the previous + * invokation of (match-unsorted-outer), so clear the field before + * going on. + */ + temp_list = innerrel->pathlist; + foreach(path, temp_list) + { + + /* + * XXX + * + * This gross hack is to get around an apparent optimizer bug on + * Sparc (or maybe it is a bug of ours?) that causes really + * wierd behavior. + */ + if (IsA_JoinPath(path)) + { + ((Path *) lfirst(path))->outerjoincost = (Cost) 0; + } + + /* + * do it iff it is a join path, which is not always true, esp + * since the base level + */ + } + + joinrels = lnext(joinrels); } - - joinrels = lnext(joinrels); - } } -/* +/* * best-innerjoin-- - * Find the cheapest index path that has already been identified by - * (indexable_joinclauses) as being a possible inner path for the given - * outer relation in a nestloop join. - * + * Find the cheapest index path that has already been identified by + * (indexable_joinclauses) as being a possible inner path for the given + * outer relation in a nestloop join. + * * 'join-paths' is a list of join nodes * 'outer-relid' is the relid of the outer join relation - * + * * Returns the pathnode of the selected path. */ -static Path * -best_innerjoin(List *join_paths, List *outer_relids) +static Path * +best_innerjoin(List * join_paths, List * outer_relids) { - Path *cheapest = (Path*)NULL; - List *join_path; - - foreach(join_path, join_paths) { - Path *path = (Path *)lfirst(join_path); + Path *cheapest = (Path *) NULL; + List *join_path; + + foreach(join_path, join_paths) + { + Path *path = (Path *) lfirst(join_path); - if (intMember(lfirsti(path->joinid), outer_relids) - && ((cheapest==NULL || - path_is_cheaper((Path*)lfirst(join_path),cheapest)))) { + if (intMember(lfirsti(path->joinid), outer_relids) + && ((cheapest == NULL || + path_is_cheaper((Path *) lfirst(join_path), cheapest)))) + { - cheapest = (Path*)lfirst(join_path); + cheapest = (Path *) lfirst(join_path); + } } - } - return(cheapest); + return (cheapest); } -/* +/* * sort-inner-and-outer-- - * Create mergesort join paths by explicitly sorting both the outer and - * inner join relations on each available merge ordering. - * + * Create mergesort join paths by explicitly sorting both the outer and + * inner join relations on each available merge ordering. + * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'mergeinfo-list' is a list of nodes containing info on(mergesortable) - * clauses for joining the relations - * + * clauses for joining the relations + * * Returns a list of mergesort paths. */ -static List * -sort_inner_and_outer(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *mergeinfo_list) +static List * +sort_inner_and_outer(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * mergeinfo_list) { - List *ms_list = NIL; - MInfo *xmergeinfo = (MInfo*)NULL; - MergePath *temp_node = (MergePath*)NULL; - List *i; - List *outerkeys = NIL; - List *innerkeys = NIL; - List *merge_pathkeys = NIL; - - foreach(i, mergeinfo_list) { - xmergeinfo = (MInfo *)lfirst(i); - - outerkeys = - extract_path_keys(xmergeinfo->jmethod.jmkeys, - outerrel->targetlist, - OUTER); - - innerkeys = - extract_path_keys(xmergeinfo->jmethod.jmkeys, - innerrel->targetlist, - INNER); - - merge_pathkeys = - new_join_pathkeys(outerkeys, joinrel->targetlist, - xmergeinfo->jmethod.clauses); - - temp_node = - create_mergesort_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - (Path*)outerrel->cheapestpath, - (Path*)innerrel->cheapestpath, - merge_pathkeys, - xmergeinfo->m_ordering, - xmergeinfo->jmethod.clauses, - outerkeys, - innerkeys); - - ms_list = lappend(ms_list, temp_node); - } - return(ms_list); + List *ms_list = NIL; + MInfo *xmergeinfo = (MInfo *) NULL; + MergePath *temp_node = (MergePath *) NULL; + List *i; + List *outerkeys = NIL; + List *innerkeys = NIL; + List *merge_pathkeys = NIL; + + foreach(i, mergeinfo_list) + { + xmergeinfo = (MInfo *) lfirst(i); + + outerkeys = + extract_path_keys(xmergeinfo->jmethod.jmkeys, + outerrel->targetlist, + OUTER); + + innerkeys = + extract_path_keys(xmergeinfo->jmethod.jmkeys, + innerrel->targetlist, + INNER); + + merge_pathkeys = + new_join_pathkeys(outerkeys, joinrel->targetlist, + xmergeinfo->jmethod.clauses); + + temp_node = + create_mergesort_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + (Path *) outerrel->cheapestpath, + (Path *) innerrel->cheapestpath, + merge_pathkeys, + xmergeinfo->m_ordering, + xmergeinfo->jmethod.clauses, + outerkeys, + innerkeys); + + ms_list = lappend(ms_list, temp_node); + } + return (ms_list); } -/* +/* * match-unsorted-outer-- - * Creates possible join paths for processing a single join relation - * 'joinrel' by employing either iterative substitution or - * mergesorting on each of its possible outer paths(assuming that the - * outer relation need not be explicitly sorted). - * - * 1. The inner path is the cheapest available inner path. - * 2. Mergesort wherever possible. Mergesorts are considered if there - * are mergesortable join clauses between the outer and inner join - * relations such that the outer path is keyed on the variables - * appearing in the clauses. The corresponding inner merge path is - * either a path whose keys match those of the outer path(if such a - * path is available) or an explicit sort on the appropriate inner - * join keys, whichever is cheaper. - * + * Creates possible join paths for processing a single join relation + * 'joinrel' by employing either iterative substitution or + * mergesorting on each of its possible outer paths(assuming that the + * outer relation need not be explicitly sorted). + * + * 1. The inner path is the cheapest available inner path. + * 2. Mergesort wherever possible. Mergesorts are considered if there + * are mergesortable join clauses between the outer and inner join + * relations such that the outer path is keyed on the variables + * appearing in the clauses. The corresponding inner merge path is + * either a path whose keys match those of the outer path(if such a + * path is available) or an explicit sort on the appropriate inner + * join keys, whichever is cheaper. + * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation @@ -296,331 +313,355 @@ sort_inner_and_outer(Rel *joinrel, * 'cheapest-inner' is the cheapest inner path * 'best-innerjoin' is the best inner index path(if any) * 'mergeinfo-list' is a list of nodes containing info on mergesortable - * clauses - * + * clauses + * * Returns a list of possible join path nodes. */ -static List * -match_unsorted_outer(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *outerpath_list, - Path *cheapest_inner, - Path *best_innerjoin, - List *mergeinfo_list) +static List * +match_unsorted_outer(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * outerpath_list, + Path * cheapest_inner, + Path * best_innerjoin, + List * mergeinfo_list) { - Path *outerpath = (Path*)NULL; - List *jp_list = NIL; - List *temp_node = NIL; - List *merge_pathkeys = NIL; - Path *nestinnerpath =(Path*)NULL; - List *paths = NIL; - List *i = NIL; - PathOrder *outerpath_ordering = NULL; - - foreach(i,outerpath_list) { - List *clauses = NIL; - List *matchedJoinKeys = NIL; - List *matchedJoinClauses = NIL; - MInfo *xmergeinfo = (MInfo*)NULL; - - outerpath = (Path*)lfirst(i); - - outerpath_ordering = &outerpath->p_ordering; - - if (outerpath_ordering) { - xmergeinfo = - match_order_mergeinfo(outerpath_ordering, - mergeinfo_list); - } - - if (xmergeinfo) { - clauses = xmergeinfo->jmethod.clauses; - } - - if (clauses) { - List *keys = xmergeinfo->jmethod.jmkeys; - List *clauses = xmergeinfo->jmethod.clauses; - - matchedJoinKeys = - match_pathkeys_joinkeys(outerpath->keys, - keys, - clauses, - OUTER, - &matchedJoinClauses); - merge_pathkeys = - new_join_pathkeys(outerpath->keys, - joinrel->targetlist, clauses); - } else { - merge_pathkeys = outerpath->keys; - } - - if(best_innerjoin && - path_is_cheaper(best_innerjoin, cheapest_inner)) { - nestinnerpath = best_innerjoin; - } else { - nestinnerpath = cheapest_inner; - } - - paths = lcons(create_nestloop_path(joinrel, - outerrel, - outerpath, - nestinnerpath, - merge_pathkeys), - NIL); - - if (clauses && matchedJoinKeys) { - bool path_is_cheaper_than_sort; - List *varkeys = NIL; - Path *mergeinnerpath = - match_paths_joinkeys(matchedJoinKeys, - outerpath_ordering, - innerrel->pathlist, - INNER); - - path_is_cheaper_than_sort = - (bool) (mergeinnerpath && - (mergeinnerpath->path_cost < - (cheapest_inner->path_cost + - cost_sort(matchedJoinKeys, - innerrel->size, - innerrel->width, - false)))); - if(!path_is_cheaper_than_sort) { - varkeys = - extract_path_keys(matchedJoinKeys, - innerrel->targetlist, - INNER); - } - - - /* - * Keep track of the cost of the outer path used with - * this ordered inner path for later processing in - * (match-unsorted-inner), since it isn't a sort and - * thus wouldn't otherwise be considered. - */ - if (path_is_cheaper_than_sort) { - mergeinnerpath->outerjoincost = outerpath->path_cost; - } else { - mergeinnerpath = cheapest_inner; - } - - temp_node = - lcons(create_mergesort_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - outerpath, - mergeinnerpath, - merge_pathkeys, - xmergeinfo->m_ordering, - matchedJoinClauses, - NIL, - varkeys), - paths); - } else { - temp_node = paths; - } - jp_list = nconc(jp_list, temp_node); - } - return(jp_list); + Path *outerpath = (Path *) NULL; + List *jp_list = NIL; + List *temp_node = NIL; + List *merge_pathkeys = NIL; + Path *nestinnerpath = (Path *) NULL; + List *paths = NIL; + List *i = NIL; + PathOrder *outerpath_ordering = NULL; + + foreach(i, outerpath_list) + { + List *clauses = NIL; + List *matchedJoinKeys = NIL; + List *matchedJoinClauses = NIL; + MInfo *xmergeinfo = (MInfo *) NULL; + + outerpath = (Path *) lfirst(i); + + outerpath_ordering = &outerpath->p_ordering; + + if (outerpath_ordering) + { + xmergeinfo = + match_order_mergeinfo(outerpath_ordering, + mergeinfo_list); + } + + if (xmergeinfo) + { + clauses = xmergeinfo->jmethod.clauses; + } + + if (clauses) + { + List *keys = xmergeinfo->jmethod.jmkeys; + List *clauses = xmergeinfo->jmethod.clauses; + + matchedJoinKeys = + match_pathkeys_joinkeys(outerpath->keys, + keys, + clauses, + OUTER, + &matchedJoinClauses); + merge_pathkeys = + new_join_pathkeys(outerpath->keys, + joinrel->targetlist, clauses); + } + else + { + merge_pathkeys = outerpath->keys; + } + + if (best_innerjoin && + path_is_cheaper(best_innerjoin, cheapest_inner)) + { + nestinnerpath = best_innerjoin; + } + else + { + nestinnerpath = cheapest_inner; + } + + paths = lcons(create_nestloop_path(joinrel, + outerrel, + outerpath, + nestinnerpath, + merge_pathkeys), + NIL); + + if (clauses && matchedJoinKeys) + { + bool path_is_cheaper_than_sort; + List *varkeys = NIL; + Path *mergeinnerpath = + match_paths_joinkeys(matchedJoinKeys, + outerpath_ordering, + innerrel->pathlist, + INNER); + + path_is_cheaper_than_sort = + (bool) (mergeinnerpath && + (mergeinnerpath->path_cost < + (cheapest_inner->path_cost + + cost_sort(matchedJoinKeys, + innerrel->size, + innerrel->width, + false)))); + if (!path_is_cheaper_than_sort) + { + varkeys = + extract_path_keys(matchedJoinKeys, + innerrel->targetlist, + INNER); + } + + + /* + * Keep track of the cost of the outer path used with this + * ordered inner path for later processing in + * (match-unsorted-inner), since it isn't a sort and thus + * wouldn't otherwise be considered. + */ + if (path_is_cheaper_than_sort) + { + mergeinnerpath->outerjoincost = outerpath->path_cost; + } + else + { + mergeinnerpath = cheapest_inner; + } + + temp_node = + lcons(create_mergesort_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + outerpath, + mergeinnerpath, + merge_pathkeys, + xmergeinfo->m_ordering, + matchedJoinClauses, + NIL, + varkeys), + paths); + } + else + { + temp_node = paths; + } + jp_list = nconc(jp_list, temp_node); + } + return (jp_list); } -/* +/* * match-unsorted-inner -- - * Find the cheapest ordered join path for a given(ordered, unsorted) - * inner join path. - * - * Scans through each path available on an inner join relation and tries - * matching its ordering keys against those of mergejoin clauses. - * If 1. an appropriately-ordered inner path and matching mergeclause are - * found, and - * 2. sorting the cheapest outer path is cheaper than using an ordered - * but unsorted outer path(as was considered in - * (match-unsorted-outer)), - * then this merge path is considered. - * + * Find the cheapest ordered join path for a given(ordered, unsorted) + * inner join path. + * + * Scans through each path available on an inner join relation and tries + * matching its ordering keys against those of mergejoin clauses. + * If 1. an appropriately-ordered inner path and matching mergeclause are + * found, and + * 2. sorting the cheapest outer path is cheaper than using an ordered + * but unsorted outer path(as was considered in + * (match-unsorted-outer)), + * then this merge path is considered. + * * 'joinrel' is the join result relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'innerpath-list' is the list of possible inner join paths * 'mergeinfo-list' is a list of nodes containing info on mergesortable - * clauses - * + * clauses + * * Returns a list of possible merge paths. */ -static List * -match_unsorted_inner(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *innerpath_list, - List *mergeinfo_list) +static List * +match_unsorted_inner(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * innerpath_list, + List * mergeinfo_list) { - Path *innerpath = (Path*)NULL; - List *mp_list = NIL; - List *temp_node = NIL; - PathOrder *innerpath_ordering = NULL; - Cost temp1 = 0.0; - bool temp2 = false; - List *i = NIL; - - foreach (i, innerpath_list) { - MInfo *xmergeinfo = (MInfo*)NULL; - List *clauses = NIL; - List *matchedJoinKeys = NIL; - List *matchedJoinClauses = NIL; - - innerpath = (Path*)lfirst(i); - - innerpath_ordering = &innerpath->p_ordering; - - if (innerpath_ordering) { - xmergeinfo = - match_order_mergeinfo(innerpath_ordering, - mergeinfo_list); - } - - if (xmergeinfo) { - clauses = ((JoinMethod*)xmergeinfo)->clauses; - } - - if (clauses) { - List *keys = xmergeinfo->jmethod.jmkeys; - List *cls = xmergeinfo->jmethod.clauses; - - matchedJoinKeys = - match_pathkeys_joinkeys(innerpath->keys, - keys, - cls, - INNER, - &matchedJoinClauses); - } - - /* - * (match-unsorted-outer) if it is applicable. - * 'OuterJoinCost was set above in - */ - if (clauses && matchedJoinKeys) { - temp1 = outerrel->cheapestpath->path_cost + - cost_sort(matchedJoinKeys, outerrel->size, outerrel->width, - false); - - temp2 = (bool) (FLOAT_IS_ZERO(innerpath->outerjoincost) - || (innerpath->outerjoincost > temp1)); - - if(temp2) { - List *outerkeys = - extract_path_keys(matchedJoinKeys, - outerrel->targetlist, - OUTER); - List *merge_pathkeys = - new_join_pathkeys(outerkeys, - joinrel->targetlist, - clauses); - - temp_node = - lcons(create_mergesort_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - (Path*)outerrel->cheapestpath, - innerpath, - merge_pathkeys, - xmergeinfo->m_ordering, - matchedJoinClauses, - outerkeys, - NIL), - NIL); - - mp_list = nconc(mp_list,temp_node); - } + Path *innerpath = (Path *) NULL; + List *mp_list = NIL; + List *temp_node = NIL; + PathOrder *innerpath_ordering = NULL; + Cost temp1 = 0.0; + bool temp2 = false; + List *i = NIL; + + foreach(i, innerpath_list) + { + MInfo *xmergeinfo = (MInfo *) NULL; + List *clauses = NIL; + List *matchedJoinKeys = NIL; + List *matchedJoinClauses = NIL; + + innerpath = (Path *) lfirst(i); + + innerpath_ordering = &innerpath->p_ordering; + + if (innerpath_ordering) + { + xmergeinfo = + match_order_mergeinfo(innerpath_ordering, + mergeinfo_list); + } + + if (xmergeinfo) + { + clauses = ((JoinMethod *) xmergeinfo)->clauses; + } + + if (clauses) + { + List *keys = xmergeinfo->jmethod.jmkeys; + List *cls = xmergeinfo->jmethod.clauses; + + matchedJoinKeys = + match_pathkeys_joinkeys(innerpath->keys, + keys, + cls, + INNER, + &matchedJoinClauses); + } + + /* + * (match-unsorted-outer) if it is applicable. 'OuterJoinCost was + * set above in + */ + if (clauses && matchedJoinKeys) + { + temp1 = outerrel->cheapestpath->path_cost + + cost_sort(matchedJoinKeys, outerrel->size, outerrel->width, + false); + + temp2 = (bool) (FLOAT_IS_ZERO(innerpath->outerjoincost) + || (innerpath->outerjoincost > temp1)); + + if (temp2) + { + List *outerkeys = + extract_path_keys(matchedJoinKeys, + outerrel->targetlist, + OUTER); + List *merge_pathkeys = + new_join_pathkeys(outerkeys, + joinrel->targetlist, + clauses); + + temp_node = + lcons(create_mergesort_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + (Path *) outerrel->cheapestpath, + innerpath, + merge_pathkeys, + xmergeinfo->m_ordering, + matchedJoinClauses, + outerkeys, + NIL), + NIL); + + mp_list = nconc(mp_list, temp_node); + } + } } - } - return(mp_list); - + return (mp_list); + } -static bool -EnoughMemoryForHashjoin(Rel *hashrel) +static bool +EnoughMemoryForHashjoin(Rel * hashrel) { - int ntuples; - int tupsize; - int pages; - - ntuples = hashrel->size; - if (ntuples == 0) ntuples = 1000; - tupsize = hashrel->width + sizeof(HeapTupleData); - pages = page_size(ntuples, tupsize); - /* - * if amount of buffer space below hashjoin threshold, - * return false - */ - if (ceil(sqrt((double)pages)) > NBuffers) - return false; - return true; + int ntuples; + int tupsize; + int pages; + + ntuples = hashrel->size; + if (ntuples == 0) + ntuples = 1000; + tupsize = hashrel->width + sizeof(HeapTupleData); + pages = page_size(ntuples, tupsize); + + /* + * if amount of buffer space below hashjoin threshold, return false + */ + if (ceil(sqrt((double) pages)) > NBuffers) + return false; + return true; } -/* - * hash-inner-and-outer-- XXX HASH - * Create hashjoin join paths by explicitly hashing both the outer and - * inner join relations on each available hash op. - * +/* + * hash-inner-and-outer-- XXX HASH + * Create hashjoin join paths by explicitly hashing both the outer and + * inner join relations on each available hash op. + * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'hashinfo-list' is a list of nodes containing info on(hashjoinable) - * clauses for joining the relations - * + * clauses for joining the relations + * * Returns a list of hashjoin paths. */ -static List * -hash_inner_and_outer(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *hashinfo_list) +static List * +hash_inner_and_outer(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * hashinfo_list) { - HInfo *xhashinfo = (HInfo*)NULL; - List *hjoin_list = NIL; - HashPath *temp_node = (HashPath*)NULL; - List *i = NIL; - List *outerkeys = NIL; - List *innerkeys = NIL; - List *hash_pathkeys = NIL; - - foreach (i, hashinfo_list) { - xhashinfo = (HInfo*)lfirst(i); - outerkeys = - extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys, - outerrel->targetlist, - OUTER); - innerkeys = - extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys, - innerrel->targetlist, - INNER); - hash_pathkeys = - new_join_pathkeys(outerkeys, - joinrel->targetlist, - ((JoinMethod*)xhashinfo)->clauses); - - if (EnoughMemoryForHashjoin(innerrel)) { - temp_node = create_hashjoin_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - (Path*)outerrel->cheapestpath, - (Path*)innerrel->cheapestpath, - hash_pathkeys, - xhashinfo->hashop, - ((JoinMethod*)xhashinfo)->clauses, - outerkeys, - innerkeys); - hjoin_list = lappend(hjoin_list, temp_node); + HInfo *xhashinfo = (HInfo *) NULL; + List *hjoin_list = NIL; + HashPath *temp_node = (HashPath *) NULL; + List *i = NIL; + List *outerkeys = NIL; + List *innerkeys = NIL; + List *hash_pathkeys = NIL; + + foreach(i, hashinfo_list) + { + xhashinfo = (HInfo *) lfirst(i); + outerkeys = + extract_path_keys(((JoinMethod *) xhashinfo)->jmkeys, + outerrel->targetlist, + OUTER); + innerkeys = + extract_path_keys(((JoinMethod *) xhashinfo)->jmkeys, + innerrel->targetlist, + INNER); + hash_pathkeys = + new_join_pathkeys(outerkeys, + joinrel->targetlist, + ((JoinMethod *) xhashinfo)->clauses); + + if (EnoughMemoryForHashjoin(innerrel)) + { + temp_node = create_hashjoin_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + (Path *) outerrel->cheapestpath, + (Path *) innerrel->cheapestpath, + hash_pathkeys, + xhashinfo->hashop, + ((JoinMethod *) xhashinfo)->clauses, + outerkeys, + innerkeys); + hjoin_list = lappend(hjoin_list, temp_node); + } } - } - return(hjoin_list); + return (hjoin_list); } - diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 00f8a04a050..98762f9800c 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * joinrels.c-- - * Routines to determine which relations should be joined + * Routines to determine which relations should be joined * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.4 1997/06/05 09:33:52 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.5 1997/09/07 04:43:40 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,467 +24,508 @@ #include "optimizer/pathnode.h" #ifdef USE_RIGHT_SIDED_PLANS -bool _use_right_sided_plans_ = true; +bool _use_right_sided_plans_ = true; + #else -bool _use_right_sided_plans_ = false; +bool _use_right_sided_plans_ = false; + #endif -static List *find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list); -static List *find_clauseless_joins(Rel *outer_rel, List *inner_rels); -static Rel *init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo); -static List *new_join_tlist(List *tlist, List *other_relids, - int first_resdomno); -static List *new_joininfo_list(List *joininfo_list, List *join_relids); -static void add_superrels(Rel *rel, Rel *super_rel); -static bool nonoverlap_rels(Rel *rel1, Rel *rel2); -static bool nonoverlap_sets(List *s1, List *s2); -static void set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel, - JInfo *jinfo); - -/* +static List *find_clause_joins(Query * root, Rel * outer_rel, List * joininfo_list); +static List *find_clauseless_joins(Rel * outer_rel, List * inner_rels); +static Rel *init_join_rel(Rel * outer_rel, Rel * inner_rel, JInfo * joininfo); +static List * +new_join_tlist(List * tlist, List * other_relids, + int first_resdomno); +static List *new_joininfo_list(List * joininfo_list, List * join_relids); +static void add_superrels(Rel * rel, Rel * super_rel); +static bool nonoverlap_rels(Rel * rel1, Rel * rel2); +static bool nonoverlap_sets(List * s1, List * s2); +static void +set_joinrel_size(Rel * joinrel, Rel * outer_rel, Rel * inner_rel, + JInfo * jinfo); + +/* * find-join-rels-- - * Find all possible joins for each of the outer join relations in - * 'outer-rels'. A rel node is created for each possible join relation, - * and the resulting list of nodes is returned. If at all possible, only - * those relations for which join clauses exist are considered. If none - * of these exist for a given relation, all remaining possibilities are - * considered. - * + * Find all possible joins for each of the outer join relations in + * 'outer-rels'. A rel node is created for each possible join relation, + * and the resulting list of nodes is returned. If at all possible, only + * those relations for which join clauses exist are considered. If none + * of these exist for a given relation, all remaining possibilities are + * considered. + * * 'outer-rels' is the list of rel nodes - * + * * Returns a list of rel nodes corresponding to the new join relations. */ -List * -find_join_rels(Query *root, List *outer_rels) +List * +find_join_rels(Query * root, List * outer_rels) { - List *joins = NIL; - List *join_list = NIL; - List *r = NIL; - - foreach(r, outer_rels) { - Rel *outer_rel = (Rel *)lfirst(r); - - if(!(joins = find_clause_joins(root, outer_rel,outer_rel->joininfo))) - if (BushyPlanFlag) - joins = find_clauseless_joins(outer_rel,outer_rels); - else - joins = find_clauseless_joins(outer_rel,root->base_relation_list_); - - join_list = nconc(join_list, joins); - } - - return(join_list); + List *joins = NIL; + List *join_list = NIL; + List *r = NIL; + + foreach(r, outer_rels) + { + Rel *outer_rel = (Rel *) lfirst(r); + + if (!(joins = find_clause_joins(root, outer_rel, outer_rel->joininfo))) + if (BushyPlanFlag) + joins = find_clauseless_joins(outer_rel, outer_rels); + else + joins = find_clauseless_joins(outer_rel, root->base_relation_list_); + + join_list = nconc(join_list, joins); + } + + return (join_list); } -/* +/* * find-clause-joins-- - * Determines whether joins can be performed between an outer relation - * 'outer-rel' and those relations within 'outer-rel's joininfo nodes - * (i.e., relations that participate in join clauses that 'outer-rel' - * participates in). This is possible if all but one of the relations - * contained within the join clauses of the joininfo node are already - * contained within 'outer-rel'. + * Determines whether joins can be performed between an outer relation + * 'outer-rel' and those relations within 'outer-rel's joininfo nodes + * (i.e., relations that participate in join clauses that 'outer-rel' + * participates in). This is possible if all but one of the relations + * contained within the join clauses of the joininfo node are already + * contained within 'outer-rel'. * * 'outer-rel' is the relation entry for the outer relation - * 'joininfo-list' is a list of join clauses which 'outer-rel' - * participates in - * + * 'joininfo-list' is a list of join clauses which 'outer-rel' + * participates in + * * Returns a list of new join relations. */ -static List * -find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list) +static List * +find_clause_joins(Query * root, Rel * outer_rel, List * joininfo_list) { - List *join_list = NIL; - List *i = NIL; - - foreach (i, joininfo_list) { - JInfo *joininfo = (JInfo*)lfirst(i); - Rel *rel; - - if(!joininfo->inactive) { - List *other_rels = joininfo->otherrels; - - if(other_rels != NIL) { - if(length(other_rels) == 1) { - rel = init_join_rel(outer_rel, - get_base_rel(root, lfirsti(other_rels)), - joininfo); - /* how about right-sided plan ? */ - if ( _use_right_sided_plans_ && - length (outer_rel->relids) > 1 ) - { - if (rel != NULL) - join_list = lappend(join_list, rel); - rel = init_join_rel(get_base_rel(root, lfirsti(other_rels)), - outer_rel, - joininfo); - } - } else if (BushyPlanFlag) { - rel = init_join_rel(outer_rel, - get_join_rel(root, other_rels), - joininfo); - } else { - rel = NULL; - } + List *join_list = NIL; + List *i = NIL; - if (rel != NULL) - join_list = lappend(join_list, rel); - } + foreach(i, joininfo_list) + { + JInfo *joininfo = (JInfo *) lfirst(i); + Rel *rel; + + if (!joininfo->inactive) + { + List *other_rels = joininfo->otherrels; + + if (other_rels != NIL) + { + if (length(other_rels) == 1) + { + rel = init_join_rel(outer_rel, + get_base_rel(root, lfirsti(other_rels)), + joininfo); + /* how about right-sided plan ? */ + if (_use_right_sided_plans_ && + length(outer_rel->relids) > 1) + { + if (rel != NULL) + join_list = lappend(join_list, rel); + rel = init_join_rel(get_base_rel(root, lfirsti(other_rels)), + outer_rel, + joininfo); + } + } + else if (BushyPlanFlag) + { + rel = init_join_rel(outer_rel, + get_join_rel(root, other_rels), + joininfo); + } + else + { + rel = NULL; + } + + if (rel != NULL) + join_list = lappend(join_list, rel); + } + } } - } - return(join_list); + return (join_list); } -/* +/* * find-clauseless-joins-- - * Given an outer relation 'outer-rel' and a list of inner relations - * 'inner-rels', create a join relation between 'outer-rel' and each - * member of 'inner-rels' that isn't already included in 'outer-rel'. - * + * Given an outer relation 'outer-rel' and a list of inner relations + * 'inner-rels', create a join relation between 'outer-rel' and each + * member of 'inner-rels' that isn't already included in 'outer-rel'. + * * Returns a list of new join relations. */ -static List * -find_clauseless_joins(Rel *outer_rel, List *inner_rels) +static List * +find_clauseless_joins(Rel * outer_rel, List * inner_rels) { - Rel *inner_rel; - List *t_list = NIL; - List *temp_node = NIL; - List *i = NIL; - - foreach (i, inner_rels) { - inner_rel = (Rel *)lfirst(i); - if(nonoverlap_rels(inner_rel, outer_rel)) { - temp_node = lcons(init_join_rel(outer_rel, - inner_rel, - (JInfo*)NULL), - NIL); - t_list = nconc(t_list,temp_node); - } - } - - return(t_list); + Rel *inner_rel; + List *t_list = NIL; + List *temp_node = NIL; + List *i = NIL; + + foreach(i, inner_rels) + { + inner_rel = (Rel *) lfirst(i); + if (nonoverlap_rels(inner_rel, outer_rel)) + { + temp_node = lcons(init_join_rel(outer_rel, + inner_rel, + (JInfo *) NULL), + NIL); + t_list = nconc(t_list, temp_node); + } + } + + return (t_list); } -/* +/* * init-join-rel-- - * Creates and initializes a new join relation. - * + * Creates and initializes a new join relation. + * * 'outer-rel' and 'inner-rel' are relation nodes for the relations to be - * joined + * joined * 'joininfo' is the joininfo node(join clause) containing both - * 'outer-rel' and 'inner-rel', if any exists - * + * 'outer-rel' and 'inner-rel', if any exists + * * Returns the new join relation node. */ -static Rel * -init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo) +static Rel * +init_join_rel(Rel * outer_rel, Rel * inner_rel, JInfo * joininfo) { - Rel *joinrel = makeNode(Rel); - List *joinrel_joininfo_list = NIL; - List *new_outer_tlist; - List *new_inner_tlist; - - /* - * Create a new tlist by removing irrelevant elements from both - * tlists of the outer and inner join relations and then merging - * the results together. - */ - new_outer_tlist = - new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */ - inner_rel->relids, 1); - new_inner_tlist = - new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */ - outer_rel->relids, - length(new_outer_tlist) + 1); - - joinrel->relids = NIL; - joinrel->indexed = false; - joinrel->pages = 0; - joinrel->tuples = 0; - joinrel->width = 0; -/* joinrel->targetlist = NIL;*/ - joinrel->pathlist = NIL; - joinrel->unorderedpath = (Path *)NULL; - joinrel->cheapestpath = (Path *)NULL; - joinrel->pruneable = true; - joinrel->classlist = NULL; - joinrel->relam = InvalidOid; - joinrel->ordering = NULL; - joinrel->clauseinfo = NIL; - joinrel->joininfo = NULL; - joinrel->innerjoin = NIL; - joinrel->superrels = NIL; - - joinrel->relids = lcons(outer_rel->relids, /* ??? aren't they lists? -ay */ - lcons(inner_rel->relids, NIL)); - - new_outer_tlist = nconc(new_outer_tlist,new_inner_tlist); - joinrel->targetlist = new_outer_tlist; - - if (joininfo) { - joinrel->clauseinfo = joininfo->jinfoclauseinfo; - if (BushyPlanFlag) - joininfo->inactive = true; - } - - joinrel_joininfo_list = - new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo), - intAppend(outer_rel->relids, inner_rel->relids)); - - joinrel->joininfo = joinrel_joininfo_list; - - set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo); - - return(joinrel); + Rel *joinrel = makeNode(Rel); + List *joinrel_joininfo_list = NIL; + List *new_outer_tlist; + List *new_inner_tlist; + + /* + * Create a new tlist by removing irrelevant elements from both tlists + * of the outer and inner join relations and then merging the results + * together. + */ + new_outer_tlist = + new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */ + inner_rel->relids, 1); + new_inner_tlist = + new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */ + outer_rel->relids, + length(new_outer_tlist) + 1); + + joinrel->relids = NIL; + joinrel->indexed = false; + joinrel->pages = 0; + joinrel->tuples = 0; + joinrel->width = 0; +/* joinrel->targetlist = NIL;*/ + joinrel->pathlist = NIL; + joinrel->unorderedpath = (Path *) NULL; + joinrel->cheapestpath = (Path *) NULL; + joinrel->pruneable = true; + joinrel->classlist = NULL; + joinrel->relam = InvalidOid; + joinrel->ordering = NULL; + joinrel->clauseinfo = NIL; + joinrel->joininfo = NULL; + joinrel->innerjoin = NIL; + joinrel->superrels = NIL; + + joinrel->relids = lcons(outer_rel->relids, /* ??? aren't they lists? + * -ay */ + lcons(inner_rel->relids, NIL)); + + new_outer_tlist = nconc(new_outer_tlist, new_inner_tlist); + joinrel->targetlist = new_outer_tlist; + + if (joininfo) + { + joinrel->clauseinfo = joininfo->jinfoclauseinfo; + if (BushyPlanFlag) + joininfo->inactive = true; + } + + joinrel_joininfo_list = + new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo), + intAppend(outer_rel->relids, inner_rel->relids)); + + joinrel->joininfo = joinrel_joininfo_list; + + set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo); + + return (joinrel); } -/* +/* * new-join-tlist-- - * Builds a join relations's target list by keeping those elements that - * will be in the final target list and any other elements that are still - * needed for future joins. For a target list entry to still be needed - * for future joins, its 'joinlist' field must not be empty after removal - * of all relids in 'other-relids'. - * + * Builds a join relations's target list by keeping those elements that + * will be in the final target list and any other elements that are still + * needed for future joins. For a target list entry to still be needed + * for future joins, its 'joinlist' field must not be empty after removal + * of all relids in 'other-relids'. + * * 'tlist' is the target list of one of the join relations * 'other-relids' is a list of relids contained within the other - * join relation + * join relation * 'first-resdomno' is the resdom number to use for the first created - * target list entry - * + * target list entry + * * Returns the new target list. */ -static List * -new_join_tlist(List *tlist, - List *other_relids, - int first_resdomno) +static List * +new_join_tlist(List * tlist, + List * other_relids, + int first_resdomno) { - int resdomno = first_resdomno - 1; - TargetEntry *xtl = NULL; - List *temp_node = NIL; - List *t_list = NIL; - List *i = NIL; - List *join_list = NIL; - bool in_final_tlist =false; - - - foreach(i,tlist) { - xtl= lfirst(i); - in_final_tlist = (join_list==NIL); - if( in_final_tlist) { - resdomno += 1; - temp_node = - lcons(create_tl_element(get_expr(xtl), - resdomno), - NIL); - t_list = nconc(t_list,temp_node); - } - } - - return(t_list); + int resdomno = first_resdomno - 1; + TargetEntry *xtl = NULL; + List *temp_node = NIL; + List *t_list = NIL; + List *i = NIL; + List *join_list = NIL; + bool in_final_tlist = false; + + + foreach(i, tlist) + { + xtl = lfirst(i); + in_final_tlist = (join_list == NIL); + if (in_final_tlist) + { + resdomno += 1; + temp_node = + lcons(create_tl_element(get_expr(xtl), + resdomno), + NIL); + t_list = nconc(t_list, temp_node); + } + } + + return (t_list); } -/* +/* * new-joininfo-list-- - * Builds a join relation's joininfo list by checking for join clauses - * which still need to used in future joins involving this relation. A - * join clause is still needed if there are still relations in the clause - * not contained in the list of relations comprising this join relation. - * New joininfo nodes are only created and added to - * 'current-joininfo-list' if a node for a particular join hasn't already - * been created. + * Builds a join relation's joininfo list by checking for join clauses + * which still need to used in future joins involving this relation. A + * join clause is still needed if there are still relations in the clause + * not contained in the list of relations comprising this join relation. + * New joininfo nodes are only created and added to + * 'current-joininfo-list' if a node for a particular join hasn't already + * been created. * - * 'current-joininfo-list' contains a list of those joininfo nodes that - * have already been built + * 'current-joininfo-list' contains a list of those joininfo nodes that + * have already been built * 'joininfo-list' is the list of join clauses involving this relation - * 'join-relids' is a list of relids corresponding to the relations - * currently being joined - * + * 'join-relids' is a list of relids corresponding to the relations + * currently being joined + * * Returns a list of joininfo nodes, new and old. */ -static List * -new_joininfo_list(List *joininfo_list, List *join_relids) +static List * +new_joininfo_list(List * joininfo_list, List * join_relids) { - List *current_joininfo_list = NIL; - List *new_otherrels = NIL; - JInfo *other_joininfo = (JInfo*)NULL; - List *xjoininfo = NIL; - - foreach (xjoininfo, joininfo_list) { - List *or; - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - - new_otherrels = joininfo->otherrels; - foreach (or, new_otherrels) - { - if ( intMember (lfirsti(or), join_relids) ) - new_otherrels = lremove ((void*)lfirst(or), new_otherrels); - } - joininfo->otherrels = new_otherrels; - if ( new_otherrels != NIL ) + List *current_joininfo_list = NIL; + List *new_otherrels = NIL; + JInfo *other_joininfo = (JInfo *) NULL; + List *xjoininfo = NIL; + + foreach(xjoininfo, joininfo_list) { - other_joininfo = joininfo_member(new_otherrels, - current_joininfo_list); - if(other_joininfo) { - other_joininfo->jinfoclauseinfo = - (List*)LispUnion(joininfo->jinfoclauseinfo, - other_joininfo->jinfoclauseinfo); - }else { - other_joininfo = makeNode(JInfo); - - other_joininfo->otherrels = - joininfo->otherrels; - other_joininfo->jinfoclauseinfo = - joininfo->jinfoclauseinfo; - other_joininfo->mergesortable = - joininfo->mergesortable; - other_joininfo->hashjoinable = - joininfo->hashjoinable; - other_joininfo->inactive = false; - - current_joininfo_list = lcons(other_joininfo, - current_joininfo_list); - } + List *or; + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + new_otherrels = joininfo->otherrels; + foreach(or, new_otherrels) + { + if (intMember(lfirsti(or), join_relids)) + new_otherrels = lremove((void *) lfirst(or), new_otherrels); + } + joininfo->otherrels = new_otherrels; + if (new_otherrels != NIL) + { + other_joininfo = joininfo_member(new_otherrels, + current_joininfo_list); + if (other_joininfo) + { + other_joininfo->jinfoclauseinfo = + (List *) LispUnion(joininfo->jinfoclauseinfo, + other_joininfo->jinfoclauseinfo); + } + else + { + other_joininfo = makeNode(JInfo); + + other_joininfo->otherrels = + joininfo->otherrels; + other_joininfo->jinfoclauseinfo = + joininfo->jinfoclauseinfo; + other_joininfo->mergesortable = + joininfo->mergesortable; + other_joininfo->hashjoinable = + joininfo->hashjoinable; + other_joininfo->inactive = false; + + current_joininfo_list = lcons(other_joininfo, + current_joininfo_list); + } + } } - } - return(current_joininfo_list); + return (current_joininfo_list); } /* * add-new-joininfos-- - * For each new join relation, create new joininfos that - * use the join relation as inner relation, and add - * the new joininfos to those rel nodes that still - * have joins with the join relation. + * For each new join relation, create new joininfos that + * use the join relation as inner relation, and add + * the new joininfos to those rel nodes that still + * have joins with the join relation. * * 'joinrels' is a list of join relations. * * Modifies the joininfo field of appropriate rel nodes. */ void -add_new_joininfos(Query *root, List *joinrels, List *outerrels) +add_new_joininfos(Query * root, List * joinrels, List * outerrels) { - List *xjoinrel = NIL; - List *xrelid = NIL; - List *xrel = NIL; - List *xjoininfo = NIL; - - foreach(xjoinrel, joinrels) { - Rel *joinrel = (Rel *)lfirst(xjoinrel); - foreach(xrelid, joinrel->relids) { - Relid relid = (Relid)lfirst(xrelid); - Rel *rel = get_join_rel(root, relid); - add_superrels(rel,joinrel); + List *xjoinrel = NIL; + List *xrelid = NIL; + List *xrel = NIL; + List *xjoininfo = NIL; + + foreach(xjoinrel, joinrels) + { + Rel *joinrel = (Rel *) lfirst(xjoinrel); + + foreach(xrelid, joinrel->relids) + { + Relid relid = (Relid) lfirst(xrelid); + Rel *rel = get_join_rel(root, relid); + + add_superrels(rel, joinrel); + } } - } - foreach(xjoinrel, joinrels) { - Rel *joinrel = (Rel *)lfirst(xjoinrel); - - foreach(xjoininfo, joinrel->joininfo) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - List *other_rels = joininfo->otherrels; - List *clause_info = joininfo->jinfoclauseinfo; - bool mergesortable = joininfo->mergesortable; - bool hashjoinable = joininfo->hashjoinable; - - foreach(xrelid, other_rels) { - Relid relid = (Relid)lfirst(xrelid); - Rel *rel = get_join_rel(root, relid); - List *super_rels = rel->superrels; - List *xsuper_rel = NIL; - JInfo *new_joininfo = makeNode(JInfo); - - new_joininfo->otherrels = joinrel->relids; - new_joininfo->jinfoclauseinfo = clause_info; - new_joininfo->mergesortable = mergesortable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->inactive = false; - rel->joininfo = - lappend(rel->joininfo, new_joininfo); - - foreach(xsuper_rel, super_rels) { - Rel *super_rel = (Rel *)lfirst(xsuper_rel); - - if( nonoverlap_rels(super_rel,joinrel) ) { - List *new_relids = super_rel->relids; - JInfo *other_joininfo = - joininfo_member(new_relids, - joinrel->joininfo); - - if (other_joininfo) { - other_joininfo->jinfoclauseinfo = - (List*)LispUnion(clause_info, - other_joininfo->jinfoclauseinfo); - } else { - JInfo *new_joininfo = makeNode(JInfo); - - new_joininfo->otherrels = new_relids; - new_joininfo->jinfoclauseinfo = clause_info; - new_joininfo->mergesortable = mergesortable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->inactive = false; - joinrel->joininfo = - lappend(joinrel->joininfo, - new_joininfo); + foreach(xjoinrel, joinrels) + { + Rel *joinrel = (Rel *) lfirst(xjoinrel); + + foreach(xjoininfo, joinrel->joininfo) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + List *other_rels = joininfo->otherrels; + List *clause_info = joininfo->jinfoclauseinfo; + bool mergesortable = joininfo->mergesortable; + bool hashjoinable = joininfo->hashjoinable; + + foreach(xrelid, other_rels) + { + Relid relid = (Relid) lfirst(xrelid); + Rel *rel = get_join_rel(root, relid); + List *super_rels = rel->superrels; + List *xsuper_rel = NIL; + JInfo *new_joininfo = makeNode(JInfo); + + new_joininfo->otherrels = joinrel->relids; + new_joininfo->jinfoclauseinfo = clause_info; + new_joininfo->mergesortable = mergesortable; + new_joininfo->hashjoinable = hashjoinable; + new_joininfo->inactive = false; + rel->joininfo = + lappend(rel->joininfo, new_joininfo); + + foreach(xsuper_rel, super_rels) + { + Rel *super_rel = (Rel *) lfirst(xsuper_rel); + + if (nonoverlap_rels(super_rel, joinrel)) + { + List *new_relids = super_rel->relids; + JInfo *other_joininfo = + joininfo_member(new_relids, + joinrel->joininfo); + + if (other_joininfo) + { + other_joininfo->jinfoclauseinfo = + (List *) LispUnion(clause_info, + other_joininfo->jinfoclauseinfo); + } + else + { + JInfo *new_joininfo = makeNode(JInfo); + + new_joininfo->otherrels = new_relids; + new_joininfo->jinfoclauseinfo = clause_info; + new_joininfo->mergesortable = mergesortable; + new_joininfo->hashjoinable = hashjoinable; + new_joininfo->inactive = false; + joinrel->joininfo = + lappend(joinrel->joininfo, + new_joininfo); + } + } + } } - } } - } } - } - foreach(xrel, outerrels) { - Rel *rel = (Rel *)lfirst(xrel); - rel->superrels = NIL; - } + foreach(xrel, outerrels) + { + Rel *rel = (Rel *) lfirst(xrel); + + rel->superrels = NIL; + } } /* * final-join-rels-- - * Find the join relation that includes all the original - * relations, i.e. the final join result. + * Find the join relation that includes all the original + * relations, i.e. the final join result. * * 'join-rel-list' is a list of join relations. * * Returns the list of final join relations. */ -List * -final_join_rels(List *join_rel_list) +List * +final_join_rels(List * join_rel_list) { - List *xrel = NIL; - List *temp = NIL; - List *t_list = NIL; - - /* - * find the relations that has no further joins, - * i.e., its joininfos all have otherrels nil. - */ - foreach(xrel,join_rel_list) { - Rel *rel = (Rel *)lfirst(xrel); - List *xjoininfo = NIL; - bool final = true; - - foreach (xjoininfo, rel->joininfo) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - - if (joininfo->otherrels != NIL) { - final = false; - break; - } - } - if (final) { - temp = lcons(rel, NIL); - t_list = nconc(t_list, temp); + List *xrel = NIL; + List *temp = NIL; + List *t_list = NIL; + + /* + * find the relations that has no further joins, i.e., its joininfos + * all have otherrels nil. + */ + foreach(xrel, join_rel_list) + { + Rel *rel = (Rel *) lfirst(xrel); + List *xjoininfo = NIL; + bool final = true; + + foreach(xjoininfo, rel->joininfo) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + if (joininfo->otherrels != NIL) + { + final = false; + break; + } + } + if (final) + { + temp = lcons(rel, NIL); + t_list = nconc(t_list, temp); + } } - } - return(t_list); + return (t_list); } /* * add_superrels-- - * add rel to the temporary property list superrels. + * add rel to the temporary property list superrels. * * 'rel' a rel node * 'super-rel' rel node of a join relation that includes rel @@ -492,60 +533,69 @@ final_join_rels(List *join_rel_list) * Modifies the superrels field of rel */ static void -add_superrels(Rel *rel, Rel *super_rel) +add_superrels(Rel * rel, Rel * super_rel) { - rel->superrels = lappend(rel->superrels, super_rel); + rel->superrels = lappend(rel->superrels, super_rel); } /* * nonoverlap-rels-- - * test if two join relations overlap, i.e., includes the same - * relation. + * test if two join relations overlap, i.e., includes the same + * relation. * * 'rel1' and 'rel2' are two join relations * * Returns non-nil if rel1 and rel2 do not overlap. */ -static bool -nonoverlap_rels(Rel *rel1, Rel *rel2) +static bool +nonoverlap_rels(Rel * rel1, Rel * rel2) { - return(nonoverlap_sets(rel1->relids, rel2->relids)); + return (nonoverlap_sets(rel1->relids, rel2->relids)); } -static bool -nonoverlap_sets(List *s1, List *s2) +static bool +nonoverlap_sets(List * s1, List * s2) { - List *x = NIL; - - foreach(x,s1) { - int e = lfirsti(x); - if(intMember(e,s2)) - return(false); - } - return(true); + List *x = NIL; + + foreach(x, s1) + { + int e = lfirsti(x); + + if (intMember(e, s2)) + return (false); + } + return (true); } static void -set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel, JInfo *jinfo) +set_joinrel_size(Rel * joinrel, Rel * outer_rel, Rel * inner_rel, JInfo * jinfo) { - int ntuples; - float selec; - - /* voodoo magic. but better than a size of 0. I have no idea why - we didn't set the size before. -ay 2/95 */ - if (jinfo==NULL) { - /* worst case: the cartesian product */ - ntuples = outer_rel->tuples * inner_rel->tuples; - } else { - selec = product_selec(jinfo->jinfoclauseinfo); -/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */ - ntuples = outer_rel->tuples * inner_rel->tuples * selec; - } - - /* I bet sizes less than 1 will screw up optimization so - make the best case 1 instead of 0 - jolly*/ - if (ntuples < 1) - ntuples = 1; - - joinrel->tuples = ntuples; + int ntuples; + float selec; + + /* + * voodoo magic. but better than a size of 0. I have no idea why we + * didn't set the size before. -ay 2/95 + */ + if (jinfo == NULL) + { + /* worst case: the cartesian product */ + ntuples = outer_rel->tuples * inner_rel->tuples; + } + else + { + selec = product_selec(jinfo->jinfoclauseinfo); +/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */ + ntuples = outer_rel->tuples * inner_rel->tuples * selec; + } + + /* + * I bet sizes less than 1 will screw up optimization so make the best + * case 1 instead of 0 - jolly + */ + if (ntuples < 1) + ntuples = 1; + + joinrel->tuples = ntuples; } diff --git a/src/backend/optimizer/path/joinutils.c b/src/backend/optimizer/path/joinutils.c index 1be5a57f2ec..c88d3cf19e8 100644 --- a/src/backend/optimizer/path/joinutils.c +++ b/src/backend/optimizer/path/joinutils.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * joinutils.c-- - * Utilities for matching and building join and path keys + * Utilities for matching and building join and path keys * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/joinutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/joinutils.c,v 1.2 1997/09/07 04:43:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,407 +26,440 @@ #include "optimizer/ordering.h" -static int match_pathkey_joinkeys(List *pathkey, List *joinkeys, - int which_subkey); -static bool every_func(List *joinkeys, List *pathkey, - int which_subkey); -static List *new_join_pathkey(List *subkeys, - List *considered_subkeys, List *join_rel_tlist, - List *joinclauses); -static List *new_matching_subkeys(Var *subkey, List *considered_subkeys, - List *join_rel_tlist, List *joinclauses); +static int +match_pathkey_joinkeys(List * pathkey, List * joinkeys, + int which_subkey); +static bool +every_func(List * joinkeys, List * pathkey, + int which_subkey); +static List * +new_join_pathkey(List * subkeys, + List * considered_subkeys, List * join_rel_tlist, + List * joinclauses); +static List * +new_matching_subkeys(Var * subkey, List * considered_subkeys, + List * join_rel_tlist, List * joinclauses); /**************************************************************************** - * KEY COMPARISONS + * KEY COMPARISONS ****************************************************************************/ -/* +/* * match-pathkeys-joinkeys-- - * Attempts to match the keys of a path against the keys of join clauses. - * This is done by looking for a matching join key in 'joinkeys' for - * every path key in the list 'pathkeys'. If there is a matching join key - * (not necessarily unique) for every path key, then the list of - * corresponding join keys and join clauses are returned in the order in - * which the keys matched the path keys. - * + * Attempts to match the keys of a path against the keys of join clauses. + * This is done by looking for a matching join key in 'joinkeys' for + * every path key in the list 'pathkeys'. If there is a matching join key + * (not necessarily unique) for every path key, then the list of + * corresponding join keys and join clauses are returned in the order in + * which the keys matched the path keys. + * * 'pathkeys' is a list of path keys: - * ( ( (var) (var) ... ) ( (var) ... ) ) + * ( ( (var) (var) ... ) ( (var) ... ) ) * 'joinkeys' is a list of join keys: - * ( (outer inner) (outer inner) ... ) + * ( (outer inner) (outer inner) ... ) * 'joinclauses' is a list of clauses corresponding to the join keys in - * 'joinkeys' + * 'joinkeys' * 'which-subkey' is a flag that selects the desired subkey of a join key - * in 'joinkeys' - * + * in 'joinkeys' + * * Returns the join keys and corresponding join clauses in a list if all * of the path keys were matched: - * ( - * ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) ) - * ( clause0 ... clauseN ) - * ) + * ( + * ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) ) + * ( clause0 ... clauseN ) + * ) * and nil otherwise. - * + * * Returns a list of matched join keys and a list of matched join clauses * in matchedJoinClausesPtr. - ay 11/94 */ -List * -match_pathkeys_joinkeys(List *pathkeys, - List *joinkeys, - List *joinclauses, - int which_subkey, - List **matchedJoinClausesPtr) +List * +match_pathkeys_joinkeys(List * pathkeys, + List * joinkeys, + List * joinclauses, + int which_subkey, + List ** matchedJoinClausesPtr) { - List *matched_joinkeys = NIL; - List *matched_joinclauses = NIL; - List *pathkey = NIL; - List *i = NIL; - int matched_joinkey_index = -1; - - foreach(i, pathkeys) { - pathkey = lfirst(i); - matched_joinkey_index = - match_pathkey_joinkeys(pathkey, joinkeys, which_subkey); - - if (matched_joinkey_index != -1 ) { - List *xjoinkey = nth(matched_joinkey_index,joinkeys); - List *joinclause = nth(matched_joinkey_index,joinclauses); - - /* XXX was "push" function */ - matched_joinkeys = lappend(matched_joinkeys,xjoinkey); - matched_joinkeys = nreverse(matched_joinkeys); - - matched_joinclauses = lappend(matched_joinclauses,joinclause); - matched_joinclauses = nreverse(matched_joinclauses); - joinkeys = LispRemove(xjoinkey,joinkeys); - } else { - return(NIL); - } - - } - if(matched_joinkeys==NULL || - length(matched_joinkeys) != length(pathkeys)) { - return NIL; - } - - *matchedJoinClausesPtr = nreverse(matched_joinclauses); - return (nreverse(matched_joinkeys)); + List *matched_joinkeys = NIL; + List *matched_joinclauses = NIL; + List *pathkey = NIL; + List *i = NIL; + int matched_joinkey_index = -1; + + foreach(i, pathkeys) + { + pathkey = lfirst(i); + matched_joinkey_index = + match_pathkey_joinkeys(pathkey, joinkeys, which_subkey); + + if (matched_joinkey_index != -1) + { + List *xjoinkey = nth(matched_joinkey_index, joinkeys); + List *joinclause = nth(matched_joinkey_index, joinclauses); + + /* XXX was "push" function */ + matched_joinkeys = lappend(matched_joinkeys, xjoinkey); + matched_joinkeys = nreverse(matched_joinkeys); + + matched_joinclauses = lappend(matched_joinclauses, joinclause); + matched_joinclauses = nreverse(matched_joinclauses); + joinkeys = LispRemove(xjoinkey, joinkeys); + } + else + { + return (NIL); + } + + } + if (matched_joinkeys == NULL || + length(matched_joinkeys) != length(pathkeys)) + { + return NIL; + } + + *matchedJoinClausesPtr = nreverse(matched_joinclauses); + return (nreverse(matched_joinkeys)); } -/* +/* * match-pathkey-joinkeys-- - * Returns the 0-based index into 'joinkeys' of the first joinkey whose - * outer or inner subkey matches any subkey of 'pathkey'. + * Returns the 0-based index into 'joinkeys' of the first joinkey whose + * outer or inner subkey matches any subkey of 'pathkey'. */ static int -match_pathkey_joinkeys(List *pathkey, - List *joinkeys, - int which_subkey) +match_pathkey_joinkeys(List * pathkey, + List * joinkeys, + int which_subkey) { - Var *path_subkey; - int pos; - List *i = NIL; - List *x = NIL; - JoinKey *jk; - - foreach(i, pathkey) { - path_subkey = (Var *)lfirst(i); - pos = 0; - foreach(x, joinkeys) { - jk = (JoinKey*)lfirst(x); - if(var_equal(path_subkey, - extract_subkey(jk, which_subkey))) - return(pos); - pos++; + Var *path_subkey; + int pos; + List *i = NIL; + List *x = NIL; + JoinKey *jk; + + foreach(i, pathkey) + { + path_subkey = (Var *) lfirst(i); + pos = 0; + foreach(x, joinkeys) + { + jk = (JoinKey *) lfirst(x); + if (var_equal(path_subkey, + extract_subkey(jk, which_subkey))) + return (pos); + pos++; + } } - } - return(-1); /* no index found */ + return (-1); /* no index found */ } -/* +/* * match-paths-joinkeys-- - * Attempts to find a path in 'paths' whose keys match a set of join - * keys 'joinkeys'. To match, - * 1. the path node ordering must equal 'ordering'. - * 2. each subkey of a given path must match(i.e., be(var_equal) to) the - * appropriate subkey of the corresponding join key in 'joinkeys', - * i.e., the Nth path key must match its subkeys against the subkey of - * the Nth join key in 'joinkeys'. - * - * 'joinkeys' is the list of key pairs to which the path keys must be - * matched + * Attempts to find a path in 'paths' whose keys match a set of join + * keys 'joinkeys'. To match, + * 1. the path node ordering must equal 'ordering'. + * 2. each subkey of a given path must match(i.e., be(var_equal) to) the + * appropriate subkey of the corresponding join key in 'joinkeys', + * i.e., the Nth path key must match its subkeys against the subkey of + * the Nth join key in 'joinkeys'. + * + * 'joinkeys' is the list of key pairs to which the path keys must be + * matched * 'ordering' is the ordering of the(outer) path to which 'joinkeys' - * must correspond + * must correspond * 'paths' is a list of(inner) paths which are to be matched against - * each join key in 'joinkeys' + * each join key in 'joinkeys' * 'which-subkey' is a flag that selects the desired subkey of a join key - * in 'joinkeys' - * + * in 'joinkeys' + * * Returns the matching path node if one exists, nil otherwise. */ -static bool -every_func(List *joinkeys, List *pathkey, int which_subkey) +static bool +every_func(List * joinkeys, List * pathkey, int which_subkey) { - JoinKey *xjoinkey; - Var *temp; - Var *tempkey = NULL; - bool found = false; - List *i = NIL; - List *j = NIL; - - foreach(i,joinkeys) { - xjoinkey = (JoinKey*)lfirst(i); - found = false; - foreach(j,pathkey) { - temp = (Var*)lfirst((List*)lfirst(j)); - if(temp == NULL) continue; - tempkey = extract_subkey(xjoinkey,which_subkey); - if(var_equal(tempkey, temp)) { - found = true; - break; - } + JoinKey *xjoinkey; + Var *temp; + Var *tempkey = NULL; + bool found = false; + List *i = NIL; + List *j = NIL; + + foreach(i, joinkeys) + { + xjoinkey = (JoinKey *) lfirst(i); + found = false; + foreach(j, pathkey) + { + temp = (Var *) lfirst((List *) lfirst(j)); + if (temp == NULL) + continue; + tempkey = extract_subkey(xjoinkey, which_subkey); + if (var_equal(tempkey, temp)) + { + found = true; + break; + } + } + if (found == false) + return (false); } - if(found == false) - return(false); - } - return(found); + return (found); } /* * match_paths_joinkeys - - * find the cheapest path that matches the join keys + * find the cheapest path that matches the join keys */ -Path * -match_paths_joinkeys(List *joinkeys, - PathOrder *ordering, - List *paths, - int which_subkey) +Path * +match_paths_joinkeys(List * joinkeys, + PathOrder * ordering, + List * paths, + int which_subkey) { - Path *matched_path = NULL ; - bool key_match = false; - List *i = NIL; - - foreach(i,paths) { - Path *path = (Path*)lfirst(i); - - key_match = every_func(joinkeys, path->keys, which_subkey); - - if (equal_path_path_ordering(ordering, - &path->p_ordering) && - length(joinkeys) == length(path->keys) && - key_match) { - - if (matched_path) { - if (path->path_cost < matched_path->path_cost) - matched_path = path; - } else { - matched_path = path; - } + Path *matched_path = NULL; + bool key_match = false; + List *i = NIL; + + foreach(i, paths) + { + Path *path = (Path *) lfirst(i); + + key_match = every_func(joinkeys, path->keys, which_subkey); + + if (equal_path_path_ordering(ordering, + &path->p_ordering) && + length(joinkeys) == length(path->keys) && + key_match) + { + + if (matched_path) + { + if (path->path_cost < matched_path->path_cost) + matched_path = path; + } + else + { + matched_path = path; + } + } } - } - return matched_path; + return matched_path; } -/* +/* * extract-path-keys-- - * Builds a subkey list for a path by pulling one of the subkeys from - * a list of join keys 'joinkeys' and then finding the var node in the - * target list 'tlist' that corresponds to that subkey. - * + * Builds a subkey list for a path by pulling one of the subkeys from + * a list of join keys 'joinkeys' and then finding the var node in the + * target list 'tlist' that corresponds to that subkey. + * * 'joinkeys' is a list of join key pairs * 'tlist' is a relation target list * 'which-subkey' is a flag that selects the desired subkey of a join key - * in 'joinkeys' - * + * in 'joinkeys' + * * Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)). * [I've no idea why they have to be list of lists. Should be fixed. -ay 12/94] */ -List * -extract_path_keys(List *joinkeys, - List *tlist, - int which_subkey) +List * +extract_path_keys(List * joinkeys, + List * tlist, + int which_subkey) { - List *pathkeys = NIL; - List *jk; - - foreach(jk, joinkeys) { - JoinKey *jkey = (JoinKey*)lfirst(jk); - Var *var, *key; - List *p; - - /* - * find the right Var in the target list for this key - */ - var = (Var*)extract_subkey(jkey, which_subkey); - key = (Var*)matching_tlvar(var, tlist); - - /* - * include it in the pathkeys list if we haven't already done so - */ - foreach(p, pathkeys) { - Var *pkey = lfirst((List*)lfirst(p)); /* XXX fix me */ - if (key == pkey) - break; - } - if (p!=NIL) - continue; /* key already in pathkeys */ + List *pathkeys = NIL; + List *jk; + + foreach(jk, joinkeys) + { + JoinKey *jkey = (JoinKey *) lfirst(jk); + Var *var, + *key; + List *p; - pathkeys = - lappend(pathkeys, lcons(key,NIL)); - } - return(pathkeys); + /* + * find the right Var in the target list for this key + */ + var = (Var *) extract_subkey(jkey, which_subkey); + key = (Var *) matching_tlvar(var, tlist); + + /* + * include it in the pathkeys list if we haven't already done so + */ + foreach(p, pathkeys) + { + Var *pkey = lfirst((List *) lfirst(p)); /* XXX fix me */ + + if (key == pkey) + break; + } + if (p != NIL) + continue; /* key already in pathkeys */ + + pathkeys = + lappend(pathkeys, lcons(key, NIL)); + } + return (pathkeys); } /**************************************************************************** - * NEW PATHKEY FORMATION + * NEW PATHKEY FORMATION ****************************************************************************/ -/* +/* * new-join-pathkeys-- - * Find the path keys for a join relation by finding all vars in the list - * of join clauses 'joinclauses' such that: - * (1) the var corresponding to the outer join relation is a - * key on the outer path - * (2) the var appears in the target list of the join relation - * In other words, add to each outer path key the inner path keys that - * are required for qualification. - * + * Find the path keys for a join relation by finding all vars in the list + * of join clauses 'joinclauses' such that: + * (1) the var corresponding to the outer join relation is a + * key on the outer path + * (2) the var appears in the target list of the join relation + * In other words, add to each outer path key the inner path keys that + * are required for qualification. + * * 'outer-pathkeys' is the list of the outer path's path keys * 'join-rel-tlist' is the target list of the join relation * 'joinclauses' is the list of restricting join clauses - * - * Returns the list of new path keys. - * + * + * Returns the list of new path keys. + * */ -List * -new_join_pathkeys(List *outer_pathkeys, - List *join_rel_tlist, - List *joinclauses) -{ - List *outer_pathkey = NIL; - List *t_list = NIL; - List *x; - List *i = NIL; - - foreach(i, outer_pathkeys) { - outer_pathkey = lfirst(i); - x = new_join_pathkey(outer_pathkey, NIL, - join_rel_tlist,joinclauses); - if (x!=NIL) { - t_list = lappend(t_list, x); +List * +new_join_pathkeys(List * outer_pathkeys, + List * join_rel_tlist, + List * joinclauses) +{ + List *outer_pathkey = NIL; + List *t_list = NIL; + List *x; + List *i = NIL; + + foreach(i, outer_pathkeys) + { + outer_pathkey = lfirst(i); + x = new_join_pathkey(outer_pathkey, NIL, + join_rel_tlist, joinclauses); + if (x != NIL) + { + t_list = lappend(t_list, x); + } } - } - return(t_list); + return (t_list); } -/* +/* * new-join-pathkey-- - * Finds new vars that become subkeys due to qualification clauses that - * contain any previously considered subkeys. These new subkeys plus the - * subkeys from 'subkeys' form a new pathkey for the join relation. - * - * Note that each returned subkey is the var node found in - * 'join-rel-tlist' rather than the joinclause var node. - * + * Finds new vars that become subkeys due to qualification clauses that + * contain any previously considered subkeys. These new subkeys plus the + * subkeys from 'subkeys' form a new pathkey for the join relation. + * + * Note that each returned subkey is the var node found in + * 'join-rel-tlist' rather than the joinclause var node. + * * 'subkeys' is a list of subkeys for which matching subkeys are to be - * found + * found * 'considered-subkeys' is the current list of all subkeys corresponding - * to a given pathkey - * + * to a given pathkey + * * Returns a new pathkey(list of subkeys). - * + * */ -static List * -new_join_pathkey(List *subkeys, - List *considered_subkeys, - List *join_rel_tlist, - List *joinclauses) +static List * +new_join_pathkey(List * subkeys, + List * considered_subkeys, + List * join_rel_tlist, + List * joinclauses) { - List *t_list = NIL; - Var *subkey; - List *i = NIL; - List *matched_subkeys = NIL; - Expr *tlist_key = (Expr*)NULL; - List *newly_considered_subkeys = NIL; - - foreach (i, subkeys) { - subkey = (Var *)lfirst(i); - if(subkey == NULL) - break; /* XXX something is wrong */ - matched_subkeys = - new_matching_subkeys(subkey,considered_subkeys, - join_rel_tlist,joinclauses); - tlist_key = matching_tlvar(subkey,join_rel_tlist); - newly_considered_subkeys = NIL; - - if (tlist_key) { - if(!member(tlist_key, matched_subkeys)) - newly_considered_subkeys = lcons(tlist_key, - matched_subkeys); - } - else { - newly_considered_subkeys = matched_subkeys; - } - - considered_subkeys = - append(considered_subkeys, newly_considered_subkeys); - - t_list = nconc(t_list,newly_considered_subkeys); - } - return(t_list); + List *t_list = NIL; + Var *subkey; + List *i = NIL; + List *matched_subkeys = NIL; + Expr *tlist_key = (Expr *) NULL; + List *newly_considered_subkeys = NIL; + + foreach(i, subkeys) + { + subkey = (Var *) lfirst(i); + if (subkey == NULL) + break; /* XXX something is wrong */ + matched_subkeys = + new_matching_subkeys(subkey, considered_subkeys, + join_rel_tlist, joinclauses); + tlist_key = matching_tlvar(subkey, join_rel_tlist); + newly_considered_subkeys = NIL; + + if (tlist_key) + { + if (!member(tlist_key, matched_subkeys)) + newly_considered_subkeys = lcons(tlist_key, + matched_subkeys); + } + else + { + newly_considered_subkeys = matched_subkeys; + } + + considered_subkeys = + append(considered_subkeys, newly_considered_subkeys); + + t_list = nconc(t_list, newly_considered_subkeys); + } + return (t_list); } -/* +/* * new-matching-subkeys-- - * Returns a list of new subkeys: - * (1) which are not listed in 'considered-subkeys' - * (2) for which the "other" variable in some clause in 'joinclauses' is - * 'subkey' - * (3) which are mentioned in 'join-rel-tlist' - * - * Note that each returned subkey is the var node found in - * 'join-rel-tlist' rather than the joinclause var node. - * + * Returns a list of new subkeys: + * (1) which are not listed in 'considered-subkeys' + * (2) for which the "other" variable in some clause in 'joinclauses' is + * 'subkey' + * (3) which are mentioned in 'join-rel-tlist' + * + * Note that each returned subkey is the var node found in + * 'join-rel-tlist' rather than the joinclause var node. + * * 'subkey' is the var node for which we are trying to find matching - * clauses - * + * clauses + * * Returns a list of new subkeys. * */ -static List * -new_matching_subkeys(Var *subkey, - List *considered_subkeys, - List *join_rel_tlist, - List *joinclauses) +static List * +new_matching_subkeys(Var * subkey, + List * considered_subkeys, + List * join_rel_tlist, + List * joinclauses) { - Expr *joinclause = NULL; - List *t_list = NIL; - List *temp = NIL; - List *i = NIL; - Expr *tlist_other_var = (Expr *)NULL; - - foreach(i,joinclauses) { - joinclause = lfirst(i); - tlist_other_var = - matching_tlvar(other_join_clause_var(subkey,joinclause), - join_rel_tlist); - - if(tlist_other_var && - !(member(tlist_other_var,considered_subkeys))) { - - /* XXX was "push" function */ - considered_subkeys = lappend(considered_subkeys, - tlist_other_var); - - /* considered_subkeys = nreverse(considered_subkeys); - XXX -- I am not sure of this. */ - - temp = lcons(tlist_other_var, NIL); - t_list = nconc(t_list,temp); - } - } - return(t_list); + Expr *joinclause = NULL; + List *t_list = NIL; + List *temp = NIL; + List *i = NIL; + Expr *tlist_other_var = (Expr *) NULL; + + foreach(i, joinclauses) + { + joinclause = lfirst(i); + tlist_other_var = + matching_tlvar(other_join_clause_var(subkey, joinclause), + join_rel_tlist); + + if (tlist_other_var && + !(member(tlist_other_var, considered_subkeys))) + { + + /* XXX was "push" function */ + considered_subkeys = lappend(considered_subkeys, + tlist_other_var); + + /* + * considered_subkeys = nreverse(considered_subkeys); XXX -- I + * am not sure of this. + */ + + temp = lcons(tlist_other_var, NIL); + t_list = nconc(t_list, temp); + } + } + return (t_list); } diff --git a/src/backend/optimizer/path/mergeutils.c b/src/backend/optimizer/path/mergeutils.c index d5f0fdcb65b..93004a6741e 100644 --- a/src/backend/optimizer/path/mergeutils.c +++ b/src/backend/optimizer/path/mergeutils.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * mergeutils.c-- - * Utilities for finding applicable merge clauses and pathkeys + * Utilities for finding applicable merge clauses and pathkeys * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.2 1997/09/07 04:43:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,102 +21,110 @@ #include "optimizer/clauses.h" #include "optimizer/ordering.h" -/* +/* * group-clauses-by-order-- - * If a join clause node in 'clauseinfo-list' is mergesortable, store - * it within a mergeinfo node containing other clause nodes with the same - * mergesort ordering. - * + * If a join clause node in 'clauseinfo-list' is mergesortable, store + * it within a mergeinfo node containing other clause nodes with the same + * mergesort ordering. + * * 'clauseinfo-list' is the list of clauseinfo nodes * 'inner-relid' is the relid of the inner join relation - * + * * Returns the new list of mergeinfo nodes. - * + * */ -List * -group_clauses_by_order(List *clauseinfo_list, - int inner_relid) +List * +group_clauses_by_order(List * clauseinfo_list, + int inner_relid) { - List *mergeinfo_list = NIL; - List *xclauseinfo = NIL; - - foreach (xclauseinfo, clauseinfo_list) { - CInfo *clauseinfo = (CInfo *)lfirst(xclauseinfo); - MergeOrder *merge_ordering = clauseinfo->mergesortorder; - - if (merge_ordering) { - /* - * Create a new mergeinfo node and add it to - * 'mergeinfo-list' if one does not yet exist for this - * merge ordering. - */ - PathOrder p_ordering; - MInfo *xmergeinfo; - Expr *clause = clauseinfo->clause; - Var *leftop = get_leftop (clause); - Var *rightop = get_rightop (clause); - JoinKey *keys; - - p_ordering.ordtype = MERGE_ORDER; - p_ordering.ord.merge = merge_ordering; - xmergeinfo = - match_order_mergeinfo(&p_ordering, mergeinfo_list); - if (inner_relid == leftop->varno) { - keys = makeNode(JoinKey); - keys->outer = rightop; - keys->inner = leftop; - } else { - keys = makeNode(JoinKey); - keys->outer = leftop; - keys->inner = rightop; - } - - if (xmergeinfo==NULL) { - xmergeinfo = makeNode(MInfo); - - xmergeinfo->m_ordering = merge_ordering; - mergeinfo_list = lcons(xmergeinfo, - mergeinfo_list); - } - - ((JoinMethod *)xmergeinfo)->clauses = - lcons(clause, - ((JoinMethod *)xmergeinfo)->clauses); - ((JoinMethod *)xmergeinfo)->jmkeys = - lcons(keys, - ((JoinMethod *)xmergeinfo)->jmkeys); + List *mergeinfo_list = NIL; + List *xclauseinfo = NIL; + + foreach(xclauseinfo, clauseinfo_list) + { + CInfo *clauseinfo = (CInfo *) lfirst(xclauseinfo); + MergeOrder *merge_ordering = clauseinfo->mergesortorder; + + if (merge_ordering) + { + + /* + * Create a new mergeinfo node and add it to 'mergeinfo-list' + * if one does not yet exist for this merge ordering. + */ + PathOrder p_ordering; + MInfo *xmergeinfo; + Expr *clause = clauseinfo->clause; + Var *leftop = get_leftop(clause); + Var *rightop = get_rightop(clause); + JoinKey *keys; + + p_ordering.ordtype = MERGE_ORDER; + p_ordering.ord.merge = merge_ordering; + xmergeinfo = + match_order_mergeinfo(&p_ordering, mergeinfo_list); + if (inner_relid == leftop->varno) + { + keys = makeNode(JoinKey); + keys->outer = rightop; + keys->inner = leftop; + } + else + { + keys = makeNode(JoinKey); + keys->outer = leftop; + keys->inner = rightop; + } + + if (xmergeinfo == NULL) + { + xmergeinfo = makeNode(MInfo); + + xmergeinfo->m_ordering = merge_ordering; + mergeinfo_list = lcons(xmergeinfo, + mergeinfo_list); + } + + ((JoinMethod *) xmergeinfo)->clauses = + lcons(clause, + ((JoinMethod *) xmergeinfo)->clauses); + ((JoinMethod *) xmergeinfo)->jmkeys = + lcons(keys, + ((JoinMethod *) xmergeinfo)->jmkeys); + } } - } - return(mergeinfo_list); + return (mergeinfo_list); } -/* +/* * match-order-mergeinfo-- - * Searches the list 'mergeinfo-list' for a mergeinfo node whose order - * field equals 'ordering'. - * + * Searches the list 'mergeinfo-list' for a mergeinfo node whose order + * field equals 'ordering'. + * * Returns the node if it exists. - * + * */ -MInfo * -match_order_mergeinfo(PathOrder *ordering, List *mergeinfo_list) +MInfo * +match_order_mergeinfo(PathOrder * ordering, List * mergeinfo_list) { - MergeOrder *xmergeorder; - List *xmergeinfo = NIL; + MergeOrder *xmergeorder; + List *xmergeinfo = NIL; - foreach(xmergeinfo, mergeinfo_list) { - MInfo *mergeinfo = (MInfo*)lfirst(xmergeinfo); + foreach(xmergeinfo, mergeinfo_list) + { + MInfo *mergeinfo = (MInfo *) lfirst(xmergeinfo); - xmergeorder = mergeinfo->m_ordering; + xmergeorder = mergeinfo->m_ordering; - if ((ordering->ordtype==MERGE_ORDER && - equal_merge_merge_ordering(ordering->ord.merge, xmergeorder)) || - (ordering->ordtype==SORTOP_ORDER && - equal_path_merge_ordering(ordering->ord.sortop, xmergeorder))) { + if ((ordering->ordtype == MERGE_ORDER && + equal_merge_merge_ordering(ordering->ord.merge, xmergeorder)) || + (ordering->ordtype == SORTOP_ORDER && + equal_path_merge_ordering(ordering->ord.sortop, xmergeorder))) + { - return (mergeinfo); + return (mergeinfo); + } } - } - return((MInfo*) NIL); + return ((MInfo *) NIL); } diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index e040675e6ec..96408b78905 100644 --- a/src/backend/optimizer/path/orindxpath.c +++ b/src/backend/optimizer/path/orindxpath.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * orindxpath.c-- - * Routines to find index paths that match a set of 'or' clauses + * Routines to find index paths that match a set of 'or' clauses * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.2 1997/09/07 04:43:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -31,241 +31,267 @@ #include "parser/parsetree.h" -static void best_or_subclause_indices(Query *root, Rel *rel, List *subclauses, - List *indices, List *examined_indexids, Cost subcost, List *selectivities, - List **indexids, Cost *cost, List **selecs); -static void best_or_subclause_index(Query *root, Rel *rel, Expr *subclause, - List *indices, int *indexid, Cost *cost, Cost *selec); +static void +best_or_subclause_indices(Query * root, Rel * rel, List * subclauses, + List * indices, List * examined_indexids, Cost subcost, List * selectivities, + List ** indexids, Cost * cost, List ** selecs); +static void +best_or_subclause_index(Query * root, Rel * rel, Expr * subclause, + List * indices, int *indexid, Cost * cost, Cost * selec); -/* +/* * create-or-index-paths-- - * Creates index paths for indices that match 'or' clauses. - * + * Creates index paths for indices that match 'or' clauses. + * * 'rel' is the relation entry for which the paths are to be defined on * 'clauses' is the list of available restriction clause nodes - * + * * Returns a list of these index path nodes. - * + * */ -List * -create_or_index_paths(Query *root, - Rel *rel, List *clauses) +List * +create_or_index_paths(Query * root, + Rel * rel, List * clauses) { - List *t_list = NIL; - - if (clauses != NIL) { - CInfo *clausenode = (CInfo *) (lfirst (clauses)); - - /* Check to see if this clause is an 'or' clause, and, if so, - * whether or not each of the subclauses within the 'or' clause has - * been matched by an index (the 'Index field was set in - * (match_or) if no index matches a given subclause, one of the - * lists of index nodes returned by (get_index) will be 'nil'). - */ - if (valid_or_clause(clausenode) && - clausenode->indexids) { - List *temp = NIL; - List *index_list = NIL; - bool index_flag = true; - - index_list = clausenode->indexids; - foreach(temp,index_list) { - if (!temp) - index_flag = false; - } - if (index_flag) { /* used to be a lisp every function */ - IndexPath *pathnode = makeNode(IndexPath); - List *indexids; - Cost cost; - List *selecs; + List *t_list = NIL; + + if (clauses != NIL) + { + CInfo *clausenode = (CInfo *) (lfirst(clauses)); + + /* + * Check to see if this clause is an 'or' clause, and, if so, + * whether or not each of the subclauses within the 'or' clause + * has been matched by an index (the 'Index field was set in + * (match_or) if no index matches a given subclause, one of the + * lists of index nodes returned by (get_index) will be 'nil'). + */ + if (valid_or_clause(clausenode) && + clausenode->indexids) + { + List *temp = NIL; + List *index_list = NIL; + bool index_flag = true; + + index_list = clausenode->indexids; + foreach(temp, index_list) + { + if (!temp) + index_flag = false; + } + if (index_flag) + { /* used to be a lisp every function */ + IndexPath *pathnode = makeNode(IndexPath); + List *indexids; + Cost cost; + List *selecs; - best_or_subclause_indices(root, - rel, - clausenode->clause->args, - clausenode->indexids, - NIL, - (Cost)0, - NIL, - &indexids, - &cost, - &selecs); - - pathnode->path.pathtype = T_IndexScan; - pathnode->path.parent = rel; - pathnode->indexqual = - lcons(clausenode,NIL); - pathnode->indexid = indexids; - pathnode->path.path_cost = cost; - - /* copy clauseinfo list into path for expensive - function processing -- JMH, 7/7/92 */ - pathnode->path.locclauseinfo = - set_difference(clauses, - copyObject((Node*) - rel->clauseinfo)); - -#if 0 /* fix xfunc */ - /* add in cost for expensive functions! -- JMH, 7/7/92 */ - if (XfuncMode != XFUNC_OFF) { - ((Path*)pathnode)->path_cost += - xfunc_get_path_cost((Path)pathnode); + best_or_subclause_indices(root, + rel, + clausenode->clause->args, + clausenode->indexids, + NIL, + (Cost) 0, + NIL, + &indexids, + &cost, + &selecs); + + pathnode->path.pathtype = T_IndexScan; + pathnode->path.parent = rel; + pathnode->indexqual = + lcons(clausenode, NIL); + pathnode->indexid = indexids; + pathnode->path.path_cost = cost; + + /* + * copy clauseinfo list into path for expensive function + * processing -- JMH, 7/7/92 + */ + pathnode->path.locclauseinfo = + set_difference(clauses, + copyObject((Node *) + rel->clauseinfo)); + +#if 0 /* fix xfunc */ + /* add in cost for expensive functions! -- JMH, 7/7/92 */ + if (XfuncMode != XFUNC_OFF) + { + ((Path *) pathnode)->path_cost += + xfunc_get_path_cost((Path) pathnode); + } +#endif + clausenode->selectivity = (Cost) floatVal(selecs); + t_list = + lcons(pathnode, + create_or_index_paths(root, rel, lnext(clauses))); + } + else + { + t_list = create_or_index_paths(root, rel, lnext(clauses)); + } } -#endif - clausenode->selectivity = (Cost)floatVal(selecs); - t_list = - lcons(pathnode, - create_or_index_paths(root, rel,lnext(clauses))); - } else { - t_list = create_or_index_paths(root, rel,lnext(clauses)); - } } - } - return(t_list); + return (t_list); } -/* +/* * best-or-subclause-indices-- - * Determines the best index to be used in conjunction with each subclause - * of an 'or' clause and the cost of scanning a relation using these - * indices. The cost is the sum of the individual index costs. - * + * Determines the best index to be used in conjunction with each subclause + * of an 'or' clause and the cost of scanning a relation using these + * indices. The cost is the sum of the individual index costs. + * * 'rel' is the node of the relation on which the index is defined * 'subclauses' are the subclauses of the 'or' clause * 'indices' are those index nodes that matched subclauses of the 'or' - * clause - * 'examined-indexids' is a list of those index ids to be used with - * subclauses that have already been examined + * clause + * 'examined-indexids' is a list of those index ids to be used with + * subclauses that have already been examined * 'subcost' is the cost of using the indices in 'examined-indexids' * 'selectivities' is a list of the selectivities of subclauses that - * have already been examined - * + * have already been examined + * * Returns a list of the indexids, cost, and selectivities of each * subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID, * 'cost' is a flonum, and 's' is a flonum. */ static void -best_or_subclause_indices(Query *root, - Rel *rel, - List *subclauses, - List *indices, - List *examined_indexids, - Cost subcost, - List *selectivities, - List **indexids, /* return value */ - Cost *cost, /* return value */ - List **selecs) /* return value */ +best_or_subclause_indices(Query * root, + Rel * rel, + List * subclauses, + List * indices, + List * examined_indexids, + Cost subcost, + List * selectivities, + List ** indexids, /* return value */ + Cost * cost, /* return value */ + List ** selecs) /* return value */ { - if (subclauses==NIL) { - *indexids = nreverse(examined_indexids); - *cost = subcost; - *selecs = nreverse(selectivities); - } else { - int best_indexid; - Cost best_cost; - Cost best_selec; - - best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices), - &best_indexid, &best_cost, &best_selec); - - best_or_subclause_indices(root, - rel, - lnext(subclauses), - lnext(indices), - lconsi(best_indexid, examined_indexids), - subcost + best_cost, - lcons(makeFloat(best_selec), selectivities), - indexids, - cost, - selecs); - } - return; -} + if (subclauses == NIL) + { + *indexids = nreverse(examined_indexids); + *cost = subcost; + *selecs = nreverse(selectivities); + } + else + { + int best_indexid; + Cost best_cost; + Cost best_selec; + + best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices), + &best_indexid, &best_cost, &best_selec); -/* + best_or_subclause_indices(root, + rel, + lnext(subclauses), + lnext(indices), + lconsi(best_indexid, examined_indexids), + subcost + best_cost, + lcons(makeFloat(best_selec), selectivities), + indexids, + cost, + selecs); + } + return; +} + +/* * best-or-subclause-index-- - * Determines which is the best index to be used with a subclause of - * an 'or' clause by estimating the cost of using each index and selecting - * the least expensive. - * + * Determines which is the best index to be used with a subclause of + * an 'or' clause by estimating the cost of using each index and selecting + * the least expensive. + * * 'rel' is the node of the relation on which the index is defined * 'subclause' is the subclause * 'indices' is a list of index nodes that match the subclause - * + * * Returns a list (index-id index-subcost index-selectivity) * (a fixnum, a fixnum, and a flonum respectively). - * + * */ static void -best_or_subclause_index(Query *root, - Rel *rel, - Expr *subclause, - List *indices, - int *retIndexid, /* return value */ - Cost *retCost, /* return value */ - Cost *retSelec) /* return value */ +best_or_subclause_index(Query * root, + Rel * rel, + Expr * subclause, + List * indices, + int *retIndexid, /* return value */ + Cost * retCost, /* return value */ + Cost * retSelec) /* return value */ { - if (indices != NIL) { - Datum value; - int flag = 0; - Cost subcost; - Rel *index = (Rel *)lfirst (indices); - AttrNumber attno = (get_leftop (subclause))->varattno ; - Oid opno = ((Oper*)subclause->oper)->opno; - bool constant_on_right = non_null((Expr*)get_rightop(subclause)); - float npages, selec; - int subclause_indexid; - Cost subclause_cost; - Cost subclause_selec; - - if(constant_on_right) { - value = ((Const*)get_rightop (subclause))->constvalue; - } else { - value = NameGetDatum(""); - } - if(constant_on_right) { - flag = (_SELEC_IS_CONSTANT_ ||_SELEC_CONSTANT_RIGHT_); - } else { - flag = _SELEC_CONSTANT_RIGHT_; - } - index_selectivity(lfirsti(index->relids), - index->classlist, - lconsi(opno,NIL), - getrelid(lfirsti(rel->relids), - root->rtable), - lconsi(attno,NIL), - lconsi(value,NIL), - lconsi(flag,NIL), - 1, - &npages, - &selec); - - subcost = cost_index((Oid) lfirsti(index->relids), - (int)npages, - (Cost)selec, - rel->pages, - rel->tuples, - index->pages, - index->tuples, - false); - best_or_subclause_index(root, - rel, - subclause, - lnext(indices), - &subclause_indexid, - &subclause_cost, - &subclause_selec); + if (indices != NIL) + { + Datum value; + int flag = 0; + Cost subcost; + Rel *index = (Rel *) lfirst(indices); + AttrNumber attno = (get_leftop(subclause))->varattno; + Oid opno = ((Oper *) subclause->oper)->opno; + bool constant_on_right = non_null((Expr *) get_rightop(subclause)); + float npages, + selec; + int subclause_indexid; + Cost subclause_cost; + Cost subclause_selec; - if (subclause_indexid==0 || subcost < subclause_cost) { - *retIndexid = lfirsti(index->relids); - *retCost = subcost; - *retSelec = selec; - } else { - *retIndexid = 0; - *retCost = 0.0; - *retSelec = 0.0; - } - } - return; + if (constant_on_right) + { + value = ((Const *) get_rightop(subclause))->constvalue; + } + else + { + value = NameGetDatum(""); + } + if (constant_on_right) + { + flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_); + } + else + { + flag = _SELEC_CONSTANT_RIGHT_; + } + index_selectivity(lfirsti(index->relids), + index->classlist, + lconsi(opno, NIL), + getrelid(lfirsti(rel->relids), + root->rtable), + lconsi(attno, NIL), + lconsi(value, NIL), + lconsi(flag, NIL), + 1, + &npages, + &selec); + + subcost = cost_index((Oid) lfirsti(index->relids), + (int) npages, + (Cost) selec, + rel->pages, + rel->tuples, + index->pages, + index->tuples, + false); + best_or_subclause_index(root, + rel, + subclause, + lnext(indices), + &subclause_indexid, + &subclause_cost, + &subclause_selec); + + if (subclause_indexid == 0 || subcost < subclause_cost) + { + *retIndexid = lfirsti(index->relids); + *retCost = subcost; + *retSelec = selec; + } + else + { + *retIndexid = 0; + *retCost = 0.0; + *retSelec = 0.0; + } + } + return; } diff --git a/src/backend/optimizer/path/predmig.c b/src/backend/optimizer/path/predmig.c index 241ab4a12d7..c302af3b581 100644 --- a/src/backend/optimizer/path/predmig.c +++ b/src/backend/optimizer/path/predmig.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * predmig.c-- - * + * * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/predmig.c,v 1.2 1996/10/23 07:14:41 bryanh Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/predmig.c,v 1.3 1997/09/07 04:43:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -16,23 +16,23 @@ ** Main Routines to handle Predicate Migration (i.e. correct optimization ** of queries with expensive functions.) ** -** The reasoning behind some of these algorithms is rather detailed. -** Have a look at Sequoia Tech Report 92/13 for more info. Also +** The reasoning behind some of these algorithms is rather detailed. +** Have a look at Sequoia Tech Report 92/13 for more info. Also ** see Monma and Sidney's paper "Sequencing with Series-Parallel ** Precedence Constraints", in "Mathematics of Operations Research", ** volume 4 (1979), pp. 215-224. ** -** The main thing that this code does that wasn't handled in xfunc.c is +** The main thing that this code does that wasn't handled in xfunc.c is ** it considers the possibility that two joins in a stream may not ** be ordered by ascending rank -- in such a scenario, it may be optimal ** to pullup more restrictions than we did via xfunc_try_pullup. ** -** This code in some sense generalizes xfunc_try_pullup; if you +** This code in some sense generalizes xfunc_try_pullup; if you ** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this ** code will do everything that xfunc_try_pullup would have, and maybe -** more. However, this results in no pruning, which may slow down the +** more. However, this results in no pruning, which may slow down the ** optimizer and/or cause the system to run out of memory. -** -- JMH, 11/13/92 +** -- JMH, 11/13/92 */ #include "nodes/pg_list.h" @@ -49,331 +49,350 @@ #include "optimizer/tlist.h" #include "lib/qsort.h" -#define is_clause(node) (get_cinfo(node)) /* a stream node represents a - clause (not a join) iff it - has a non-NULL cinfo field */ - -static void xfunc_predmig(JoinPath pathnode, Stream streamroot, - Stream laststream, bool *progressp); -static bool xfunc_series_llel(Stream stream); -static bool xfunc_llel_chains(Stream root, Stream bottom); -static Stream xfunc_complete_stream(Stream stream); -static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme, - JoinPath joinpath); -static void xfunc_form_groups(Stream root, Stream bottom); -static void xfunc_free_stream(Stream root); -static Stream xfunc_add_clauses(Stream current); -static void xfunc_setup_group(Stream node, Stream bottom); -static Stream xfunc_streaminsert(CInfo clauseinfo, Stream current, - int clausetype); -static int xfunc_num_relids(Stream node); +#define is_clause(node) (get_cinfo(node)) /* a stream node + * represents a clause + * (not a join) iff it has + * a non-NULL cinfo field */ + +static void +xfunc_predmig(JoinPath pathnode, Stream streamroot, + Stream laststream, bool * progressp); +static bool xfunc_series_llel(Stream stream); +static bool xfunc_llel_chains(Stream root, Stream bottom); +static Stream xfunc_complete_stream(Stream stream); +static bool +xfunc_prdmig_pullup(Stream origstream, Stream pullme, + JoinPath joinpath); +static void xfunc_form_groups(Stream root, Stream bottom); +static void xfunc_free_stream(Stream root); +static Stream xfunc_add_clauses(Stream current); +static void xfunc_setup_group(Stream node, Stream bottom); +static Stream +xfunc_streaminsert(CInfo clauseinfo, Stream current, + int clausetype); +static int xfunc_num_relids(Stream node); static StreamPtr xfunc_get_downjoin(Stream node); static StreamPtr xfunc_get_upjoin(Stream node); -static Stream xfunc_stream_qsort(Stream root, Stream bottom); -static int xfunc_stream_compare(void *arg1, void *arg2); -static bool xfunc_check_stream(Stream node); -static bool xfunc_in_stream(Stream node, Stream stream); +static Stream xfunc_stream_qsort(Stream root, Stream bottom); +static int xfunc_stream_compare(void *arg1, void *arg2); +static bool xfunc_check_stream(Stream node); +static bool xfunc_in_stream(Stream node, Stream stream); -/* ----------------- MAIN FUNCTIONS ------------------------ */ +/* ----------------- MAIN FUNCTIONS ------------------------ */ /* ** xfunc_do_predmig -** wrapper for Predicate Migration. It calls xfunc_predmig until no +** wrapper for Predicate Migration. It calls xfunc_predmig until no ** more progress is made. -** return value says if any changes were ever made. +** return value says if any changes were ever made. */ -bool xfunc_do_predmig(Path root) +bool +xfunc_do_predmig(Path root) { - bool progress, changed = false; - - if (is_join(root)) - do - { - progress = false; - Assert(IsA(root,JoinPath)); - xfunc_predmig((JoinPath)root, (Stream)NULL, (Stream)NULL, - &progress); - if (changed && progress) - elog(DEBUG, "Needed to do a second round of predmig!\n"); - if (progress) changed = true; - } while (progress); - return(changed); + bool progress, + changed = false; + + if (is_join(root)) + do + { + progress = false; + Assert(IsA(root, JoinPath)); + xfunc_predmig((JoinPath) root, (Stream) NULL, (Stream) NULL, + &progress); + if (changed && progress) + elog(DEBUG, "Needed to do a second round of predmig!\n"); + if (progress) + changed = true; + } while (progress); + return (changed); } /* ** xfunc_predmig - ** The main routine for Predicate Migration. It traverses a join tree, - ** and for each root-to-leaf path in the plan tree it constructs a + ** The main routine for Predicate Migration. It traverses a join tree, + ** and for each root-to-leaf path in the plan tree it constructs a ** "Stream", which it passes to xfunc_series_llel for optimization. ** Destructively modifies the join tree (via predicate pullup). */ static void -xfunc_predmig(JoinPath pathnode, /* root of the join tree */ - Stream streamroot, - Stream laststream, /* for recursive calls -- these are - the root of the stream under - construction, and the lowest node - created so far */ - bool *progressp) +xfunc_predmig(JoinPath pathnode,/* root of the join tree */ + Stream streamroot, + Stream laststream,/* for recursive calls -- these are the + * root of the stream under construction, + * and the lowest node created so far */ + bool * progressp) { - Stream newstream; - - /* - ** traverse the join tree dfs-style, constructing a stream as you go. - ** When you hit a scan node, pass the stream off to xfunc_series_llel. - */ - - /* sanity check */ - if ((!streamroot && laststream) || - (streamroot && !laststream)) - elog(WARN, "called xfunc_predmig with bad inputs"); - if (streamroot) Assert(xfunc_check_stream(streamroot)); - - /* add path node to stream */ - newstream = RMakeStream(); - if (!streamroot) - streamroot = newstream; - set_upstream(newstream, (StreamPtr)laststream); - if (laststream) - set_downstream(laststream, (StreamPtr)newstream); - set_downstream(newstream, (StreamPtr)NULL); - set_pathptr(newstream, (pathPtr)pathnode); - set_cinfo(newstream, (CInfo)NULL); - set_clausetype(newstream, XFUNC_UNKNOWN); - - /* base case: we're at a leaf, call xfunc_series_llel */ - if (!is_join(pathnode)) + Stream newstream; + + /* + * * traverse the join tree dfs-style, constructing a stream as you + * go. * When you hit a scan node, pass the stream off to + * xfunc_series_llel. + */ + + /* sanity check */ + if ((!streamroot && laststream) || + (streamroot && !laststream)) + elog(WARN, "called xfunc_predmig with bad inputs"); + if (streamroot) + Assert(xfunc_check_stream(streamroot)); + + /* add path node to stream */ + newstream = RMakeStream(); + if (!streamroot) + streamroot = newstream; + set_upstream(newstream, (StreamPtr) laststream); + if (laststream) + set_downstream(laststream, (StreamPtr) newstream); + set_downstream(newstream, (StreamPtr) NULL); + set_pathptr(newstream, (pathPtr) pathnode); + set_cinfo(newstream, (CInfo) NULL); + set_clausetype(newstream, XFUNC_UNKNOWN); + + /* base case: we're at a leaf, call xfunc_series_llel */ + if (!is_join(pathnode)) { - /* form a fleshed-out copy of the stream */ - Stream fullstream = xfunc_complete_stream(streamroot); - - /* sort it via series-llel */ - if (xfunc_series_llel(fullstream)) - *progressp = true; - - /* free up the copy */ - xfunc_free_stream(fullstream); + /* form a fleshed-out copy of the stream */ + Stream fullstream = xfunc_complete_stream(streamroot); + + /* sort it via series-llel */ + if (xfunc_series_llel(fullstream)) + *progressp = true; + + /* free up the copy */ + xfunc_free_stream(fullstream); } - else + else { - /* visit left child */ - xfunc_predmig((JoinPath)get_outerjoinpath(pathnode), - streamroot, newstream, progressp); - - /* visit right child */ - xfunc_predmig((JoinPath)get_innerjoinpath(pathnode), - streamroot, newstream, progressp); + /* visit left child */ + xfunc_predmig((JoinPath) get_outerjoinpath(pathnode), + streamroot, newstream, progressp); + + /* visit right child */ + xfunc_predmig((JoinPath) get_innerjoinpath(pathnode), + streamroot, newstream, progressp); } - - /* remove this node */ - if (get_upstream(newstream)) - set_downstream((Stream)get_upstream(newstream), (StreamPtr)NULL); - pfree(newstream); + + /* remove this node */ + if (get_upstream(newstream)) + set_downstream((Stream) get_upstream(newstream), (StreamPtr) NULL); + pfree(newstream); } /* ** xfunc_series_llel ** A flavor of Monma and Sidney's Series-Parallel algorithm. - ** Traverse stream downwards. When you find a node with restrictions on it, + ** Traverse stream downwards. When you find a node with restrictions on it, ** call xfunc_llel_chains on the substream from root to that node. */ -static bool xfunc_series_llel(Stream stream) +static bool +xfunc_series_llel(Stream stream) { - Stream temp, next; - bool progress = false; - - for (temp = stream; temp != (Stream)NULL; temp = next) + Stream temp, + next; + bool progress = false; + + for (temp = stream; temp != (Stream) NULL; temp = next) { - next = (Stream)xfunc_get_downjoin(temp); - /* - ** if there are restrictions/secondary join clauses above this - ** node, call xfunc_llel_chains - */ - if (get_upstream(temp) && is_clause((Stream)get_upstream(temp))) - if (xfunc_llel_chains(stream, temp)) - progress = true; + next = (Stream) xfunc_get_downjoin(temp); + + /* + * * if there are restrictions/secondary join clauses above this * + * node, call xfunc_llel_chains + */ + if (get_upstream(temp) && is_clause((Stream) get_upstream(temp))) + if (xfunc_llel_chains(stream, temp)) + progress = true; } - return(progress); + return (progress); } /* ** xfunc_llel_chains ** A flavor of Monma and Sidney's Parallel Chains algorithm. ** Given a stream which has been well-ordered except for its lowermost - ** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate. + ** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate. ** What that means here is to form groups in the chain above the lowest - ** join node above bottom inclusive, and then take all the restrictions + ** join node above bottom inclusive, and then take all the restrictions ** following bottom, and try to pull them up as far as possible. */ -static bool xfunc_llel_chains(Stream root, Stream bottom) +static bool +xfunc_llel_chains(Stream root, Stream bottom) { - bool progress = false; - Stream origstream; - Stream tmpstream, pathstream; - Stream rootcopy = root; - - Assert(xfunc_check_stream(root)); - - /* xfunc_prdmig_pullup will need an unmodified copy of the stream */ - origstream = (Stream)copyObject((Node)root); - - /* form groups among ill-ordered nodes */ - xfunc_form_groups(root, bottom); - - /* sort chain by rank */ - Assert(xfunc_in_stream(bottom, root)); - rootcopy = xfunc_stream_qsort(root, bottom); - - /* - ** traverse sorted stream -- if any restriction has moved above a join, - ** we must pull it up in the plan. That is, make plan tree - ** reflect order of sorted stream. - */ - for (tmpstream = rootcopy, - pathstream = (Stream)xfunc_get_downjoin(rootcopy); - tmpstream != (Stream)NULL && pathstream != (Stream)NULL; - tmpstream = (Stream)get_downstream(tmpstream)) + bool progress = false; + Stream origstream; + Stream tmpstream, + pathstream; + Stream rootcopy = root; + + Assert(xfunc_check_stream(root)); + + /* xfunc_prdmig_pullup will need an unmodified copy of the stream */ + origstream = (Stream) copyObject((Node) root); + + /* form groups among ill-ordered nodes */ + xfunc_form_groups(root, bottom); + + /* sort chain by rank */ + Assert(xfunc_in_stream(bottom, root)); + rootcopy = xfunc_stream_qsort(root, bottom); + + /* + * * traverse sorted stream -- if any restriction has moved above a + * join, * we must pull it up in the plan. That is, make plan tree * + * reflect order of sorted stream. + */ + for (tmpstream = rootcopy, + pathstream = (Stream) xfunc_get_downjoin(rootcopy); + tmpstream != (Stream) NULL && pathstream != (Stream) NULL; + tmpstream = (Stream) get_downstream(tmpstream)) { - if (is_clause(tmpstream) - && get_pathptr(pathstream) != get_pathptr(tmpstream)) + if (is_clause(tmpstream) + && get_pathptr(pathstream) != get_pathptr(tmpstream)) { - /* - ** If restriction moved above a Join after sort, we pull it - ** up in the join plan. - ** If restriction moved down, we ignore it. - ** This is because Joey's Sequoia paper proves that - ** restrictions should never move down. If this - ** one were moved down, it would violate "semantic correctness", - ** i.e. it would be lower than the attributes it references. - */ - Assert(xfunc_num_relids(pathstream)>xfunc_num_relids(tmpstream)); - progress = - xfunc_prdmig_pullup(origstream, tmpstream, - (JoinPath)get_pathptr(pathstream)); + + /* + * * If restriction moved above a Join after sort, we pull it * + * up in the join plan. * If restriction moved down, we + * ignore it. * This is because Joey's Sequoia paper proves + * that * restrictions should never move down. If this * one + * were moved down, it would violate "semantic correctness", * + * i.e. it would be lower than the attributes it references. + */ + Assert(xfunc_num_relids(pathstream) > xfunc_num_relids(tmpstream)); + progress = + xfunc_prdmig_pullup(origstream, tmpstream, + (JoinPath) get_pathptr(pathstream)); } - if (get_downstream(tmpstream)) - pathstream = - (Stream)xfunc_get_downjoin((Stream)get_downstream(tmpstream)); + if (get_downstream(tmpstream)) + pathstream = + (Stream) xfunc_get_downjoin((Stream) get_downstream(tmpstream)); } - - /* free up origstream */ - xfunc_free_stream(origstream); - return(progress); + + /* free up origstream */ + xfunc_free_stream(origstream); + return (progress); } /* ** xfunc_complete_stream -- ** Given a stream composed of join nodes only, make a copy containing the - ** join nodes along with the associated restriction nodes. + ** join nodes along with the associated restriction nodes. */ -static Stream xfunc_complete_stream(Stream stream) +static Stream +xfunc_complete_stream(Stream stream) { - Stream tmpstream, copystream, curstream = (Stream)NULL; - - copystream = (Stream)copyObject((Node)stream); - Assert(xfunc_check_stream(copystream)); - - curstream = copystream; - Assert(!is_clause(curstream)); - - /* curstream = (Stream)xfunc_get_downjoin(curstream); */ - - while(curstream != (Stream)NULL) + Stream tmpstream, + copystream, + curstream = (Stream) NULL; + + copystream = (Stream) copyObject((Node) stream); + Assert(xfunc_check_stream(copystream)); + + curstream = copystream; + Assert(!is_clause(curstream)); + + /* curstream = (Stream)xfunc_get_downjoin(curstream); */ + + while (curstream != (Stream) NULL) { - xfunc_add_clauses(curstream); - curstream = (Stream)xfunc_get_downjoin(curstream); + xfunc_add_clauses(curstream); + curstream = (Stream) xfunc_get_downjoin(curstream); } - - /* find top of stream and return it */ - for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr)NULL; - tmpstream = (Stream)get_upstream(tmpstream)) - /* no body in for loop */; - - return(tmpstream); + + /* find top of stream and return it */ + for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr) NULL; + tmpstream = (Stream) get_upstream(tmpstream)) + /* no body in for loop */ ; + + return (tmpstream); } /* ** xfunc_prdmig_pullup ** pullup a clause in a path above joinpath. Since the JoinPath tree - ** doesn't have upward pointers, it's difficult to deal with. Thus we + ** doesn't have upward pointers, it's difficult to deal with. Thus we ** require the original stream, which maintains pointers to all the path - ** nodes. We use the original stream to find out what joins are + ** nodes. We use the original stream to find out what joins are ** above the clause. */ -static bool +static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath) { - CInfo clauseinfo = get_cinfo(pullme); - bool progress = false; - Stream upjoin, orignode, temp; - int whichchild; - - /* find node in origstream that contains clause */ - for (orignode = origstream; - orignode != (Stream) NULL - && get_cinfo(orignode) != clauseinfo; - orignode = (Stream)get_downstream(orignode)) - /* empty body in for loop */ ; - if (!orignode) - elog(WARN, "Didn't find matching node in original stream"); - - - /* pull up this node as far as it should go */ - for (upjoin = (Stream)xfunc_get_upjoin(orignode); - upjoin != (Stream)NULL - && (JoinPath)get_pathptr((Stream)xfunc_get_downjoin(upjoin)) - != joinpath; - upjoin = (Stream)xfunc_get_upjoin(upjoin)) + CInfo clauseinfo = get_cinfo(pullme); + bool progress = false; + Stream upjoin, + orignode, + temp; + int whichchild; + + /* find node in origstream that contains clause */ + for (orignode = origstream; + orignode != (Stream) NULL + && get_cinfo(orignode) != clauseinfo; + orignode = (Stream) get_downstream(orignode)) + /* empty body in for loop */ ; + if (!orignode) + elog(WARN, "Didn't find matching node in original stream"); + + + /* pull up this node as far as it should go */ + for (upjoin = (Stream) xfunc_get_upjoin(orignode); + upjoin != (Stream) NULL + && (JoinPath) get_pathptr((Stream) xfunc_get_downjoin(upjoin)) + != joinpath; + upjoin = (Stream) xfunc_get_upjoin(upjoin)) { -#ifdef DEBUG - elog(DEBUG, "pulling up in xfunc_predmig_pullup!"); +#ifdef DEBUG + elog(DEBUG, "pulling up in xfunc_predmig_pullup!"); #endif - /* move clause up in path */ - if (get_pathptr((Stream)get_downstream(upjoin)) - == (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin))) - whichchild = OUTER; - else whichchild = INNER; - clauseinfo = xfunc_pullup((Path)get_pathptr((Stream)get_downstream(upjoin)), - (JoinPath)get_pathptr(upjoin), - clauseinfo, - whichchild, - get_clausetype(orignode)); - set_pathptr(pullme, get_pathptr(upjoin)); - /* pullme has been moved into locclauseinfo */ - set_clausetype(pullme, XFUNC_LOCPRD); - - /* - ** xfunc_pullup makes new path nodes for children of - ** get_pathptr(current). We must modify the stream nodes to point - ** to these path nodes - */ - if (whichchild == OUTER) + /* move clause up in path */ + if (get_pathptr((Stream) get_downstream(upjoin)) + == (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))) + whichchild = OUTER; + else + whichchild = INNER; + clauseinfo = xfunc_pullup((Path) get_pathptr((Stream) get_downstream(upjoin)), + (JoinPath) get_pathptr(upjoin), + clauseinfo, + whichchild, + get_clausetype(orignode)); + set_pathptr(pullme, get_pathptr(upjoin)); + /* pullme has been moved into locclauseinfo */ + set_clausetype(pullme, XFUNC_LOCPRD); + + /* + * * xfunc_pullup makes new path nodes for children of * + * get_pathptr(current). We must modify the stream nodes to point * + * to these path nodes + */ + if (whichchild == OUTER) { - for(temp = (Stream)get_downstream(upjoin); is_clause(temp); - temp = (Stream)get_downstream(temp)) + for (temp = (Stream) get_downstream(upjoin); is_clause(temp); + temp = (Stream) get_downstream(temp)) + set_pathptr + (temp, (pathPtr) + get_outerjoinpath((JoinPath) get_pathptr(upjoin))); set_pathptr - (temp, (pathPtr) - get_outerjoinpath((JoinPath)get_pathptr(upjoin))); - set_pathptr - (temp, - (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin))); + (temp, + (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))); } - else + else { - for(temp = (Stream)get_downstream(upjoin); is_clause(temp); - temp = (Stream)get_downstream(temp)) + for (temp = (Stream) get_downstream(upjoin); is_clause(temp); + temp = (Stream) get_downstream(temp)) + set_pathptr + (temp, (pathPtr) + get_innerjoinpath((JoinPath) get_pathptr(upjoin))); set_pathptr - (temp, (pathPtr) - get_innerjoinpath((JoinPath)get_pathptr(upjoin))); - set_pathptr - (temp, (pathPtr) - get_innerjoinpath((JoinPath)get_pathptr(upjoin))); + (temp, (pathPtr) + get_innerjoinpath((JoinPath) get_pathptr(upjoin))); } - progress = true; + progress = true; } - if (!progress) - elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup"); - return(progress); + if (!progress) + elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup"); + return (progress); } /* @@ -386,143 +405,151 @@ xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath) ** equal to the cost of the first plus the selectivity of the first times the ** cost of the second. We define each node to be in a group by itself, ** and then repeatedly find adjacent groups which are ordered by descending - ** rank, and make larger groups. You know that two adjacent nodes are in a - ** group together if the lower has groupup set to true. They will both have + ** rank, and make larger groups. You know that two adjacent nodes are in a + ** group together if the lower has groupup set to true. They will both have ** the same groupcost and groupsel (since they're in the same group!) */ -static void xfunc_form_groups(Query* queryInfo, Stream root, Stream bottom) +static void +xfunc_form_groups(Query * queryInfo, Stream root, Stream bottom) { - Stream temp, parent; - int lowest = xfunc_num_relids((Stream)xfunc_get_upjoin(bottom)); - bool progress; - LispValue primjoin; - int whichchild; - - if (!lowest) return; /* no joins in stream, so no groups */ - - /* initialize groups to be single nodes */ - for (temp = root; - temp != (Stream)NULL && temp != bottom; - temp = (Stream)get_downstream(temp)) + Stream temp, + parent; + int lowest = xfunc_num_relids((Stream) xfunc_get_upjoin(bottom)); + bool progress; + LispValue primjoin; + int whichchild; + + if (!lowest) + return; /* no joins in stream, so no groups */ + + /* initialize groups to be single nodes */ + for (temp = root; + temp != (Stream) NULL && temp != bottom; + temp = (Stream) get_downstream(temp)) { - /* if a Join node */ - if (!is_clause(temp)) + /* if a Join node */ + if (!is_clause(temp)) { - if (get_pathptr((Stream)get_downstream(temp)) - == (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(temp))) - whichchild = OUTER; - else whichchild = INNER; - set_groupcost(temp, - xfunc_join_expense((JoinPath)get_pathptr(temp), - whichchild)); - if (primjoin = xfunc_primary_join((JoinPath)get_pathptr(temp))) + if (get_pathptr((Stream) get_downstream(temp)) + == (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(temp))) + whichchild = OUTER; + else + whichchild = INNER; + set_groupcost(temp, + xfunc_join_expense((JoinPath) get_pathptr(temp), + whichchild)); + if (primjoin = xfunc_primary_join((JoinPath) get_pathptr(temp))) { - set_groupsel(temp, - compute_clause_selec(queryInfo, - primjoin, NIL)); + set_groupsel(temp, + compute_clause_selec(queryInfo, + primjoin, NIL)); } - else + else { - set_groupsel(temp,1.0); + set_groupsel(temp, 1.0); } } - else /* a restriction, or 2-ary join pred */ + else +/* a restriction, or 2-ary join pred */ { - set_groupcost(temp, - xfunc_expense(queryInfo, - get_clause(get_cinfo(temp)))); - set_groupsel(temp, - compute_clause_selec(queryInfo, - get_clause(get_cinfo(temp)), - NIL)); + set_groupcost(temp, + xfunc_expense(queryInfo, + get_clause(get_cinfo(temp)))); + set_groupsel(temp, + compute_clause_selec(queryInfo, + get_clause(get_cinfo(temp)), + NIL)); } - set_groupup(temp,false); + set_groupup(temp, false); } - - /* make passes upwards, forming groups */ - do + + /* make passes upwards, forming groups */ + do { - progress = false; - for (temp = (Stream)get_upstream(bottom); - temp != (Stream)NULL; - temp = (Stream)get_upstream(temp)) + progress = false; + for (temp = (Stream) get_upstream(bottom); + temp != (Stream) NULL; + temp = (Stream) get_upstream(temp)) { - /* check for grouping with node upstream */ - if (!get_groupup(temp) && /* not already grouped */ - (parent = (Stream)get_upstream(temp)) != (Stream)NULL && + /* check for grouping with node upstream */ + if (!get_groupup(temp) && /* not already grouped */ + (parent = (Stream) get_upstream(temp)) != (Stream) NULL && /* temp is a join or temp is the top of a group */ - (is_join((Path)get_pathptr(temp)) || - get_downstream(temp) && - get_groupup((Stream)get_downstream(temp))) && - get_grouprank(parent) < get_grouprank(temp)) + (is_join((Path) get_pathptr(temp)) || + get_downstream(temp) && + get_groupup((Stream) get_downstream(temp))) && + get_grouprank(parent) < get_grouprank(temp)) { - progress = true; /* we formed a new group */ - set_groupup(temp,true); - set_groupcost(temp, - get_groupcost(temp) + - get_groupsel(temp) * get_groupcost(parent)); - set_groupsel(temp,get_groupsel(temp) * get_groupsel(parent)); - - /* fix costs and sels of all members of group */ - xfunc_setup_group(temp, bottom); + progress = true;/* we formed a new group */ + set_groupup(temp, true); + set_groupcost(temp, + get_groupcost(temp) + + get_groupsel(temp) * get_groupcost(parent)); + set_groupsel(temp, get_groupsel(temp) * get_groupsel(parent)); + + /* fix costs and sels of all members of group */ + xfunc_setup_group(temp, bottom); } } - } while(progress); + } while (progress); } -/* ------------------- UTILITY FUNCTIONS ------------------------- */ +/* ------------------- UTILITY FUNCTIONS ------------------------- */ /* ** xfunc_free_stream -- ** walk down a stream and pfree it */ -static void xfunc_free_stream(Stream root) +static void +xfunc_free_stream(Stream root) { - Stream cur, next; - - Assert(xfunc_check_stream(root)); - - if (root != (Stream)NULL) - for (cur = root; cur != (Stream)NULL; cur = next) - { - next = (Stream)get_downstream(cur); - pfree(cur); - } + Stream cur, + next; + + Assert(xfunc_check_stream(root)); + + if (root != (Stream) NULL) + for (cur = root; cur != (Stream) NULL; cur = next) + { + next = (Stream) get_downstream(cur); + pfree(cur); + } } /* ** xfunc_add<_clauses - ** find any clauses above current, and insert them into stream as + ** find any clauses above current, and insert them into stream as ** appropriate. Return uppermost clause inserted, or current if none. */ -static Stream xfunc_add_clauses(Stream current) +static Stream +xfunc_add_clauses(Stream current) { - Stream topnode = current; - LispValue temp; - LispValue primjoin; - - /* first add in the local clauses */ - foreach(temp, get_locclauseinfo((Path)get_pathptr(current))) + Stream topnode = current; + LispValue temp; + LispValue primjoin; + + /* first add in the local clauses */ + foreach(temp, get_locclauseinfo((Path) get_pathptr(current))) { - topnode = - xfunc_streaminsert((CInfo)lfirst(temp), topnode, - XFUNC_LOCPRD); + topnode = + xfunc_streaminsert((CInfo) lfirst(temp), topnode, + XFUNC_LOCPRD); } - - /* and add in the join clauses */ - if (IsA(get_pathptr(current),JoinPath)) + + /* and add in the join clauses */ + if (IsA(get_pathptr(current), JoinPath)) { - primjoin = xfunc_primary_join((JoinPath)get_pathptr(current)); - foreach(temp, get_pathclauseinfo((JoinPath)get_pathptr(current))) + primjoin = xfunc_primary_join((JoinPath) get_pathptr(current)); + foreach(temp, get_pathclauseinfo((JoinPath) get_pathptr(current))) { - if (!equal(get_clause((CInfo)lfirst(temp)), primjoin)) - topnode = - xfunc_streaminsert((CInfo)lfirst(temp), topnode, - XFUNC_JOINPRD); + if (!equal(get_clause((CInfo) lfirst(temp)), primjoin)) + topnode = + xfunc_streaminsert((CInfo) lfirst(temp), topnode, + XFUNC_JOINPRD); } } - return(topnode); + return (topnode); } @@ -531,33 +558,36 @@ static Stream xfunc_add_clauses(Stream current) ** find all elements of stream that are grouped with node and are above ** bottom, and set their groupcost and groupsel to be the same as node's. */ -static void xfunc_setup_group(Stream node, Stream bottom) +static void +xfunc_setup_group(Stream node, Stream bottom) { - Stream temp; - - if (node != bottom) - /* traverse downwards */ - for (temp = (Stream)get_downstream(node); - temp != (Stream)NULL && temp != bottom; - temp = (Stream)get_downstream(temp)) - { - if (!get_groupup(temp)) break; - else - { - set_groupcost(temp, get_groupcost(node)); - set_groupsel(temp, get_groupsel(node)); - } - } - - /* traverse upwards */ - for (temp = (Stream)get_upstream(node); temp != (Stream)NULL; - temp = (Stream)get_upstream(temp)) + Stream temp; + + if (node != bottom) + /* traverse downwards */ + for (temp = (Stream) get_downstream(node); + temp != (Stream) NULL && temp != bottom; + temp = (Stream) get_downstream(temp)) + { + if (!get_groupup(temp)) + break; + else + { + set_groupcost(temp, get_groupcost(node)); + set_groupsel(temp, get_groupsel(node)); + } + } + + /* traverse upwards */ + for (temp = (Stream) get_upstream(node); temp != (Stream) NULL; + temp = (Stream) get_upstream(temp)) { - if (!get_groupup((Stream)get_downstream(temp))) break; - else + if (!get_groupup((Stream) get_downstream(temp))) + break; + else { - set_groupcost(temp, get_groupcost(node)); - set_groupsel(temp, get_groupsel(node)); + set_groupcost(temp, get_groupcost(node)); + set_groupsel(temp, get_groupsel(node)); } } } @@ -568,70 +598,75 @@ static void xfunc_setup_group(Stream node, Stream bottom) ** Make a new Stream node to hold clause, and insert it above current. ** Return new node. */ -static Stream +static Stream xfunc_streaminsert(CInfo clauseinfo, - Stream current, - int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */ + Stream current, + int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */ { - Stream newstream = RMakeStream(); - set_upstream(newstream, get_upstream(current)); - if (get_upstream(current)) - set_downstream((Stream)(get_upstream(current)), (StreamPtr)newstream); - set_upstream(current, (StreamPtr)newstream); - set_downstream(newstream, (StreamPtr)current); - set_pathptr(newstream, get_pathptr(current)); - set_cinfo(newstream, clauseinfo); - set_clausetype(newstream, clausetype); - return(newstream); + Stream newstream = RMakeStream(); + + set_upstream(newstream, get_upstream(current)); + if (get_upstream(current)) + set_downstream((Stream) (get_upstream(current)), (StreamPtr) newstream); + set_upstream(current, (StreamPtr) newstream); + set_downstream(newstream, (StreamPtr) current); + set_pathptr(newstream, get_pathptr(current)); + set_cinfo(newstream, clauseinfo); + set_clausetype(newstream, clausetype); + return (newstream); } /* ** Given a Stream node, find the number of relids referenced in the pathnode ** associated with the stream node. The number of relids gives a unique - ** ordering on the joins in a stream, which we use to compare the height of + ** ordering on the joins in a stream, which we use to compare the height of ** join nodes. */ -static int xfunc_num_relids(Stream node) +static int +xfunc_num_relids(Stream node) { - if (!node || !IsA(get_pathptr(node),JoinPath)) - return(0); - else return(length - (get_relids(get_parent((JoinPath)get_pathptr(node))))); + if (!node || !IsA(get_pathptr(node), JoinPath)) + return (0); + else + return (length + (get_relids(get_parent((JoinPath) get_pathptr(node))))); } -/* +/* ** xfunc_get_downjoin -- ** Given a stream node, find the next lowest node which points to a ** join predicate or a scan node. */ -static StreamPtr xfunc_get_downjoin(Stream node) +static StreamPtr +xfunc_get_downjoin(Stream node) { - Stream temp; - - if (!is_clause(node)) /* if this is a join */ - node = (Stream)get_downstream(node); - for (temp = node; temp && is_clause(temp); - temp = (Stream)get_downstream(temp)) - /* empty body in for loop */ ; - - return((StreamPtr)temp); + Stream temp; + + if (!is_clause(node)) /* if this is a join */ + node = (Stream) get_downstream(node); + for (temp = node; temp && is_clause(temp); + temp = (Stream) get_downstream(temp)) + /* empty body in for loop */ ; + + return ((StreamPtr) temp); } /* ** xfunc_get_upjoin -- ** same as above, but upwards. */ -static StreamPtr xfunc_get_upjoin(Stream node) +static StreamPtr +xfunc_get_upjoin(Stream node) { - Stream temp; - - if (!is_clause(node)) /* if this is a join */ - node = (Stream)get_upstream(node); - for (temp = node; temp && is_clause(temp); - temp = (Stream)get_upstream(temp)) - /* empty body in for loop */ ; - - return((StreamPtr)temp); + Stream temp; + + if (!is_clause(node)) /* if this is a join */ + node = (Stream) get_upstream(node); + for (temp = node; temp && is_clause(temp); + temp = (Stream) get_upstream(temp)) + /* empty body in for loop */ ; + + return ((StreamPtr) temp); } /* @@ -639,43 +674,46 @@ static StreamPtr xfunc_get_upjoin(Stream node) ** Given a stream, sort by group rank the elements in the stream from the ** node "bottom" up. DESTRUCTIVELY MODIFIES STREAM! Returns new root. */ -static Stream xfunc_stream_qsort(Stream root, Stream bottom) +static Stream +xfunc_stream_qsort(Stream root, Stream bottom) { - int i; - size_t num; - Stream *nodearray, output; - Stream tmp; - - /* find size of list */ - for (num = 0, tmp = root; tmp != bottom; - tmp = (Stream)get_downstream(tmp)) - num ++; - if (num <= 1) return (root); - - /* copy elements of the list into an array */ - nodearray = (Stream *) palloc(num * sizeof(Stream)); - - for (tmp = root, i = 0; tmp != bottom; - tmp = (Stream)get_downstream(tmp), i++) - nodearray[i] = tmp; - - /* sort the array */ - pg_qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare); - - /* paste together the array elements */ - output = nodearray[num - 1]; - set_upstream(output, (StreamPtr)NULL); - for (i = num - 2; i >= 0; i--) + int i; + size_t num; + Stream *nodearray, + output; + Stream tmp; + + /* find size of list */ + for (num = 0, tmp = root; tmp != bottom; + tmp = (Stream) get_downstream(tmp)) + num++; + if (num <= 1) + return (root); + + /* copy elements of the list into an array */ + nodearray = (Stream *) palloc(num * sizeof(Stream)); + + for (tmp = root, i = 0; tmp != bottom; + tmp = (Stream) get_downstream(tmp), i++) + nodearray[i] = tmp; + + /* sort the array */ + pg_qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare); + + /* paste together the array elements */ + output = nodearray[num - 1]; + set_upstream(output, (StreamPtr) NULL); + for (i = num - 2; i >= 0; i--) { - set_downstream(nodearray[i+1], (StreamPtr)nodearray[i]); - set_upstream(nodearray[i], (StreamPtr)nodearray[i+1]); + set_downstream(nodearray[i + 1], (StreamPtr) nodearray[i]); + set_upstream(nodearray[i], (StreamPtr) nodearray[i + 1]); } - set_downstream(nodearray[0], (StreamPtr)bottom); - if (bottom) - set_upstream(bottom, (StreamPtr)nodearray[0]); - - Assert(xfunc_check_stream(output)); - return(output); + set_downstream(nodearray[0], (StreamPtr) bottom); + if (bottom) + set_upstream(bottom, (StreamPtr) nodearray[0]); + + Assert(xfunc_check_stream(output)); + return (output); } /* @@ -684,90 +722,102 @@ static Stream xfunc_stream_qsort(Stream root, Stream bottom) ** Compare nodes by group rank. If group ranks are equal, ensure that ** join nodes appear in same order as in plan tree. */ -static int xfunc_stream_compare(void *arg1, void *arg2) +static int +xfunc_stream_compare(void *arg1, void *arg2) { - Stream stream1 = *(Stream *) arg1; - Stream stream2 = *(Stream *) arg2; - Cost rank1, rank2; - - rank1 = get_grouprank(stream1); - rank2 = get_grouprank(stream2); - - if (rank1 > rank2) return(1); - else if (rank1 < rank2) return(-1); - else + Stream stream1 = *(Stream *) arg1; + Stream stream2 = *(Stream *) arg2; + Cost rank1, + rank2; + + rank1 = get_grouprank(stream1); + rank2 = get_grouprank(stream2); + + if (rank1 > rank2) + return (1); + else if (rank1 < rank2) + return (-1); + else { - if (is_clause(stream1) && is_clause(stream2)) - return(0); /* doesn't matter what order if both are restrictions */ - else if (!is_clause(stream1) && !is_clause(stream2)) + if (is_clause(stream1) && is_clause(stream2)) + return (0); /* doesn't matter what order if both are + * restrictions */ + else if (!is_clause(stream1) && !is_clause(stream2)) { - if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2)) - return(-1); - else return(1); + if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2)) + return (-1); + else + return (1); } - else if (is_clause(stream1) && !is_clause(stream2)) + else if (is_clause(stream1) && !is_clause(stream2)) { - if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2)) - /* stream1 is a restriction over stream2 */ - return(1); - else return(-1); + if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2)) + /* stream1 is a restriction over stream2 */ + return (1); + else + return (-1); } - else if (!is_clause(stream1) && is_clause(stream2)) + else if (!is_clause(stream1) && is_clause(stream2)) { - /* stream2 is a restriction over stream1: never push down */ - return(-1); + /* stream2 is a restriction over stream1: never push down */ + return (-1); } } } -/* ------------------ DEBUGGING ROUTINES ---------------------------- */ +/* ------------------ DEBUGGING ROUTINES ---------------------------- */ /* ** Make sure all pointers in stream make sense. Make sure no joins are ** out of order. */ -static bool xfunc_check_stream(Stream node) +static bool +xfunc_check_stream(Stream node) { - Stream temp; - int numrelids, tmp; - - /* set numrelids higher than max */ - if (!is_clause(node)) - numrelids = xfunc_num_relids(node) + 1; - else if (xfunc_get_downjoin(node)) - numrelids = xfunc_num_relids((Stream)xfunc_get_downjoin(node)) + 1; - else numrelids = 1; - - for (temp = node; get_downstream(temp); temp = (Stream)get_downstream(temp)) + Stream temp; + int numrelids, + tmp; + + /* set numrelids higher than max */ + if (!is_clause(node)) + numrelids = xfunc_num_relids(node) + 1; + else if (xfunc_get_downjoin(node)) + numrelids = xfunc_num_relids((Stream) xfunc_get_downjoin(node)) + 1; + else + numrelids = 1; + + for (temp = node; get_downstream(temp); temp = (Stream) get_downstream(temp)) { - if ((Stream)get_upstream((Stream)get_downstream(temp)) != temp) + if ((Stream) get_upstream((Stream) get_downstream(temp)) != temp) { - elog(WARN, "bad pointers in stream"); - return(false); + elog(WARN, "bad pointers in stream"); + return (false); } - if (!is_clause(temp)) + if (!is_clause(temp)) { - if ((tmp = xfunc_num_relids(temp)) >= numrelids) + if ((tmp = xfunc_num_relids(temp)) >= numrelids) { - elog(WARN, "Joins got reordered!"); - return(false); + elog(WARN, "Joins got reordered!"); + return (false); } - numrelids = tmp; + numrelids = tmp; } } - - return(true); + + return (true); } /* ** xfunc_in_stream ** check if node is in stream */ -static bool xfunc_in_stream(Stream node, Stream stream) +static bool +xfunc_in_stream(Stream node, Stream stream) { - Stream temp; - - for (temp = stream; temp; temp = (Stream)get_downstream(temp)) - if (temp == node) return(1); - return(0); + Stream temp; + + for (temp = stream; temp; temp = (Stream) get_downstream(temp)) + if (temp == node) + return (1); + return (0); } diff --git a/src/backend/optimizer/path/prune.c b/src/backend/optimizer/path/prune.c index 0b154e108fa..4f3ae2d15de 100644 --- a/src/backend/optimizer/path/prune.c +++ b/src/backend/optimizer/path/prune.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * prune.c-- - * Routines to prune redundant paths and relations + * Routines to prune redundant paths and relations * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.3 1997/06/10 07:55:47 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.4 1997/09/07 04:43:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,181 +24,199 @@ #include "utils/elog.h" -static List *prune_joinrel(Rel *rel, List *other_rels); +static List *prune_joinrel(Rel * rel, List * other_rels); -/* +/* * prune-joinrels-- - * Removes any redundant relation entries from a list of rel nodes - * 'rel-list'. - * - * Returns the resulting list. - * + * Removes any redundant relation entries from a list of rel nodes + * 'rel-list'. + * + * Returns the resulting list. + * */ -List *prune_joinrels(List *rel_list) +List * +prune_joinrels(List * rel_list) { - List *temp_list = NIL; - - if (rel_list != NIL) { - temp_list = lcons(lfirst(rel_list), - prune_joinrels(prune_joinrel((Rel*)lfirst(rel_list), - lnext(rel_list)))); - } - return(temp_list); + List *temp_list = NIL; + + if (rel_list != NIL) + { + temp_list = lcons(lfirst(rel_list), + prune_joinrels(prune_joinrel((Rel *) lfirst(rel_list), + lnext(rel_list)))); + } + return (temp_list); } -/* +/* * prune-joinrel-- - * Prunes those relations from 'other-rels' that are redundant with - * 'rel'. A relation is redundant if it is built up of the same - * relations as 'rel'. Paths for the redundant relation are merged into - * the pathlist of 'rel'. - * + * Prunes those relations from 'other-rels' that are redundant with + * 'rel'. A relation is redundant if it is built up of the same + * relations as 'rel'. Paths for the redundant relation are merged into + * the pathlist of 'rel'. + * * Returns a list of non-redundant relations, and sets the pathlist field * of 'rel' appropriately. - * + * */ -static List * -prune_joinrel(Rel *rel, List *other_rels) +static List * +prune_joinrel(Rel * rel, List * other_rels) { - List *i = NIL; - List *t_list = NIL; - List *temp_node = NIL; - Rel *other_rel = (Rel *)NULL; - - foreach(i, other_rels) { - other_rel = (Rel*)lfirst(i); - if(same(rel->relids, other_rel->relids)) { - rel->pathlist = add_pathlist(rel, - rel->pathlist, - other_rel->pathlist); - t_list = nconc(t_list, NIL); /* XXX is this right ? */ - } else { - temp_node = lcons(other_rel, NIL); - t_list = nconc(t_list,temp_node); - } - } - return(t_list); + List *i = NIL; + List *t_list = NIL; + List *temp_node = NIL; + Rel *other_rel = (Rel *) NULL; + + foreach(i, other_rels) + { + other_rel = (Rel *) lfirst(i); + if (same(rel->relids, other_rel->relids)) + { + rel->pathlist = add_pathlist(rel, + rel->pathlist, + other_rel->pathlist); + t_list = nconc(t_list, NIL); /* XXX is this right ? */ + } + else + { + temp_node = lcons(other_rel, NIL); + t_list = nconc(t_list, temp_node); + } + } + return (t_list); } -/* +/* * prune-rel-paths-- - * For each relation entry in 'rel-list' (which corresponds to a join - * relation), set pointers to the unordered path and cheapest paths - * (if the unordered path isn't the cheapest, it is pruned), and - * reset the relation's size field to reflect the join. - * + * For each relation entry in 'rel-list' (which corresponds to a join + * relation), set pointers to the unordered path and cheapest paths + * (if the unordered path isn't the cheapest, it is pruned), and + * reset the relation's size field to reflect the join. + * * Returns nothing of interest. - * + * */ void -prune_rel_paths(List *rel_list) +prune_rel_paths(List * rel_list) { - List *x = NIL; - List *y = NIL; - Path *path = NULL; - Rel *rel = (Rel*)NULL; - JoinPath *cheapest = (JoinPath*)NULL; - - foreach(x, rel_list) { - rel = (Rel*)lfirst(x); - rel->size = 0; - foreach(y, rel->pathlist) { - path = (Path*)lfirst(y); - - if(!path->p_ordering.ord.sortop) { - break; - } + List *x = NIL; + List *y = NIL; + Path *path = NULL; + Rel *rel = (Rel *) NULL; + JoinPath *cheapest = (JoinPath *) NULL; + + foreach(x, rel_list) + { + rel = (Rel *) lfirst(x); + rel->size = 0; + foreach(y, rel->pathlist) + { + path = (Path *) lfirst(y); + + if (!path->p_ordering.ord.sortop) + { + break; + } + } + cheapest = (JoinPath *) prune_rel_path(rel, path); + if (IsA_JoinPath(cheapest)) + { + rel->size = compute_joinrel_size(cheapest); + } + else + elog(WARN, "non JoinPath called"); } - cheapest = (JoinPath*)prune_rel_path(rel, path); - if (IsA_JoinPath(cheapest)) - { - rel->size = compute_joinrel_size(cheapest); - } - else - elog(WARN, "non JoinPath called"); - } } -/* +/* * prune-rel-path-- - * Compares the unordered path for a relation with the cheapest path. If - * the unordered path is not cheapest, it is pruned. - * - * Resets the pointers in 'rel' for unordered and cheapest paths. - * + * Compares the unordered path for a relation with the cheapest path. If + * the unordered path is not cheapest, it is pruned. + * + * Resets the pointers in 'rel' for unordered and cheapest paths. + * * Returns the cheapest path. - * + * */ -Path * -prune_rel_path(Rel *rel, Path *unorderedpath) +Path * +prune_rel_path(Rel * rel, Path * unorderedpath) { - Path *cheapest = set_cheapest(rel, rel->pathlist); - - /* don't prune if not pruneable -- JMH, 11/23/92 */ - if(unorderedpath != cheapest - && rel->pruneable) { - - rel->unorderedpath = (Path *)NULL; - rel->pathlist = lremove(unorderedpath, rel->pathlist); - } else { - rel->unorderedpath = (Path *)unorderedpath; - } - - return(cheapest); + Path *cheapest = set_cheapest(rel, rel->pathlist); + + /* don't prune if not pruneable -- JMH, 11/23/92 */ + if (unorderedpath != cheapest + && rel->pruneable) + { + + rel->unorderedpath = (Path *) NULL; + rel->pathlist = lremove(unorderedpath, rel->pathlist); + } + else + { + rel->unorderedpath = (Path *) unorderedpath; + } + + return (cheapest); } /* * merge-joinrels-- - * Given two lists of rel nodes that are already - * pruned, merge them into one pruned rel node list + * Given two lists of rel nodes that are already + * pruned, merge them into one pruned rel node list * * 'rel-list1' and * 'rel-list2' are the rel node lists * * Returns one pruned rel node list */ -List * -merge_joinrels(List *rel_list1, List *rel_list2) +List * +merge_joinrels(List * rel_list1, List * rel_list2) { - List *xrel = NIL; - - foreach(xrel,rel_list1) { - Rel *rel = (Rel*)lfirst(xrel); - rel_list2 = prune_joinrel(rel,rel_list2); - } - return(append(rel_list1, rel_list2)); + List *xrel = NIL; + + foreach(xrel, rel_list1) + { + Rel *rel = (Rel *) lfirst(xrel); + + rel_list2 = prune_joinrel(rel, rel_list2); + } + return (append(rel_list1, rel_list2)); } /* * prune_oldrels-- - * If all the joininfo's in a rel node are inactive, - * that means that this node has been joined into - * other nodes in all possible ways, therefore - * this node can be discarded. If not, it will cause - * extra complexity of the optimizer. + * If all the joininfo's in a rel node are inactive, + * that means that this node has been joined into + * other nodes in all possible ways, therefore + * this node can be discarded. If not, it will cause + * extra complexity of the optimizer. * * old_rels is a list of rel nodes - * + * * Returns a new list of rel nodes */ -List *prune_oldrels(List *old_rels) +List * +prune_oldrels(List * old_rels) { - Rel *rel; - List *joininfo_list, *xjoininfo; - - if(old_rels == NIL) - return(NIL); - - rel = (Rel*)lfirst(old_rels); - joininfo_list = rel->joininfo; - if(joininfo_list == NIL) - return (lcons(rel, prune_oldrels(lnext(old_rels)))); - - foreach(xjoininfo, joininfo_list) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - if(!joininfo->inactive) - return (lcons(rel, prune_oldrels(lnext(old_rels)))); - } - return(prune_oldrels(lnext(old_rels))); + Rel *rel; + List *joininfo_list, + *xjoininfo; + + if (old_rels == NIL) + return (NIL); + + rel = (Rel *) lfirst(old_rels); + joininfo_list = rel->joininfo; + if (joininfo_list == NIL) + return (lcons(rel, prune_oldrels(lnext(old_rels)))); + + foreach(xjoininfo, joininfo_list) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + if (!joininfo->inactive) + return (lcons(rel, prune_oldrels(lnext(old_rels)))); + } + return (prune_oldrels(lnext(old_rels))); } diff --git a/src/backend/optimizer/path/xfunc.c b/src/backend/optimizer/path/xfunc.c index 3e3ee650f94..36135d4a823 100644 --- a/src/backend/optimizer/path/xfunc.c +++ b/src/backend/optimizer/path/xfunc.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * xfunc.c-- - * Utility routines to handle expensive function optimization. - * Includes xfunc_trypullup(), which attempts early pullup of predicates - * to allow for maximal pruning. - * + * Utility routines to handle expensive function optimization. + * Includes xfunc_trypullup(), which attempts early pullup of predicates + * to allow for maximal pruning. + * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.3 1997/02/14 04:15:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.4 1997/09/07 04:43:50 momjian Exp $ * *------------------------------------------------------------------------- */ -#include <math.h> /* for MAXFLOAT on most systems */ +#include <math.h> /* for MAXFLOAT on most systems */ -#include <values.h> /* for MAXFLOAT on SunOS */ +#include <values.h> /* for MAXFLOAT on SunOS */ #include <string.h> #include "postgres.h" @@ -40,82 +40,96 @@ #include "lib/lispsort.h" #include "access/heapam.h" #include "tcop/dest.h" -#include "storage/buf_internals.h" /* for NBuffers */ -#include "optimizer/tlist.h" /* for get_expr */ +#include "storage/buf_internals.h" /* for NBuffers */ +#include "optimizer/tlist.h" /* for get_expr */ #define ever ; 1 ; /* local funcs */ -static int xfunc_card_unreferenced(Query *queryInfo, - Expr *clause, Relid referenced); */ +static int +xfunc_card_unreferenced(Query * queryInfo, + Expr * clause, Relid referenced); + +*/ /* ** xfunc_trypullup -- -** Preliminary pullup of predicates, to allow for maximal pruning. +** Preliminary pullup of predicates, to allow for maximal pruning. ** Given a relation, check each of its paths and see if you can ** pullup clauses from its inner and outer. */ -void xfunc_trypullup(Rel rel) +void +xfunc_trypullup(Rel rel) { - LispValue y; /* list ptr */ - CInfo maxcinfo; /* The CInfo to pull up, as calculated by - xfunc_shouldpull() */ - JoinPath curpath; /* current path in list */ - int progress; /* has progress been made this time through? */ - int clausetype; - - do { - progress = false; /* no progress yet in this iteration */ - foreach(y, get_pathlist(rel)) { - curpath = (JoinPath)lfirst(y); - - /* - ** for each operand, attempt to pullup predicates until first - ** failure. - */ - for(ever) { - /* No, the following should NOT be '==' !! */ - if (clausetype = - xfunc_shouldpull((Path)get_innerjoinpath(curpath), - curpath, INNER, &maxcinfo)) { - - xfunc_pullup((Path)get_innerjoinpath(curpath), - curpath, maxcinfo, INNER, clausetype); - progress = true; - }else - break; - } - for(ever) { - - /* No, the following should NOT be '==' !! */ - if (clausetype = - xfunc_shouldpull((Path)get_outerjoinpath(curpath), - curpath, OUTER, &maxcinfo)) { - - xfunc_pullup((Path)get_outerjoinpath(curpath), - curpath, maxcinfo, OUTER, clausetype); - progress = true; - }else - break; - } - - /* - ** make sure the unpruneable flag bubbles up, i.e. - ** if anywhere below us in the path pruneable is false, - ** then pruneable should be false here - */ - if (get_pruneable(get_parent(curpath)) && - (!get_pruneable(get_parent - ((Path)get_innerjoinpath(curpath))) || - !get_pruneable(get_parent((Path) - get_outerjoinpath(curpath))))) { - - set_pruneable(get_parent(curpath),false); - progress = true; - } - } - } while(progress); + LispValue y; /* list ptr */ + CInfo maxcinfo; /* The CInfo to pull up, as calculated by + * xfunc_shouldpull() */ + JoinPath curpath; /* current path in list */ + int progress; /* has progress been made this time + * through? */ + int clausetype; + + do + { + progress = false; /* no progress yet in this iteration */ + foreach(y, get_pathlist(rel)) + { + curpath = (JoinPath) lfirst(y); + + /* + * * for each operand, attempt to pullup predicates until + * first * failure. + */ + for (ever) + { + /* No, the following should NOT be '==' !! */ + if (clausetype = + xfunc_shouldpull((Path) get_innerjoinpath(curpath), + curpath, INNER, &maxcinfo)) + { + + xfunc_pullup((Path) get_innerjoinpath(curpath), + curpath, maxcinfo, INNER, clausetype); + progress = true; + } + else + break; + } + for (ever) + { + + /* No, the following should NOT be '==' !! */ + if (clausetype = + xfunc_shouldpull((Path) get_outerjoinpath(curpath), + curpath, OUTER, &maxcinfo)) + { + + xfunc_pullup((Path) get_outerjoinpath(curpath), + curpath, maxcinfo, OUTER, clausetype); + progress = true; + } + else + break; + } + + /* + * * make sure the unpruneable flag bubbles up, i.e. * if + * anywhere below us in the path pruneable is false, * then + * pruneable should be false here + */ + if (get_pruneable(get_parent(curpath)) && + (!get_pruneable(get_parent + ((Path) get_innerjoinpath(curpath))) || + !get_pruneable(get_parent((Path) + get_outerjoinpath(curpath))))) + { + + set_pruneable(get_parent(curpath), false); + progress = true; + } + } + } while (progress); } /* @@ -128,108 +142,123 @@ void xfunc_trypullup(Rel rel) ** we'd better set the unpruneable flag. -- JMH, 11/11/92 ** ** Returns: 0 if nothing left to pullup - ** XFUNC_LOCPRD if a local predicate is to be pulled up - ** XFUNC_JOINPRD if a secondary join predicate is to be pulled up + ** XFUNC_LOCPRD if a local predicate is to be pulled up + ** XFUNC_JOINPRD if a secondary join predicate is to be pulled up */ -int xfunc_shouldpull(Query* queryInfo, - Path childpath, - JoinPath parentpath, - int whichchild, - CInfo *maxcinfopt) /* Out: pointer to clause to pullup */ +int +xfunc_shouldpull(Query * queryInfo, + Path childpath, + JoinPath parentpath, + int whichchild, + CInfo * maxcinfopt) /* Out: pointer to clause to + * pullup */ { - LispValue clauselist, tmplist; /* lists of clauses */ - CInfo maxcinfo; /* clause to pullup */ - LispValue primjoinclause /* primary join clause */ + LispValue clauselist, + tmplist; /* lists of clauses */ + CInfo maxcinfo; /* clause to pullup */ + LispValue primjoinclause /* primary join clause */ = xfunc_primary_join(parentpath); - Cost tmprank, maxrank = (-1 * MAXFLOAT); /* ranks of clauses */ - Cost joinselec = 0; /* selectivity of the join predicate */ - Cost joincost = 0; /* join cost + primjoinclause cost */ - int retval = XFUNC_LOCPRD; - - clauselist = get_locclauseinfo(childpath); - - if (clauselist != LispNil) { - /* find local predicate with maximum rank */ - for (tmplist = clauselist, - maxcinfo = (CInfo) lfirst(tmplist), - maxrank = xfunc_rank(get_clause(maxcinfo)); - tmplist != LispNil; - tmplist = lnext(tmplist)) { - - if ((tmprank = xfunc_rank(get_clause((CInfo)lfirst(tmplist)))) - > maxrank) { - maxcinfo = (CInfo) lfirst(tmplist); - maxrank = tmprank; - } + Cost tmprank, + maxrank = (-1 * MAXFLOAT); /* ranks of clauses */ + Cost joinselec = 0; /* selectivity of the join + * predicate */ + Cost joincost = 0; /* join cost + primjoinclause cost */ + int retval = XFUNC_LOCPRD; + + clauselist = get_locclauseinfo(childpath); + + if (clauselist != LispNil) + { + /* find local predicate with maximum rank */ + for (tmplist = clauselist, + maxcinfo = (CInfo) lfirst(tmplist), + maxrank = xfunc_rank(get_clause(maxcinfo)); + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + + if ((tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) + > maxrank) + { + maxcinfo = (CInfo) lfirst(tmplist); + maxrank = tmprank; + } + } } - } - - /* - ** If child is a join path, and there are multiple join clauses, - ** see if any join clause has even higher rank than the highest - ** local predicate - */ - if (is_join(childpath) && xfunc_num_join_clauses((JoinPath)childpath) > 1) - for (tmplist = get_pathclauseinfo((JoinPath)childpath); - tmplist != LispNil; - tmplist = lnext(tmplist)) { - - if (tmplist != LispNil && - (tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) - > maxrank) { - maxcinfo = (CInfo) lfirst(tmplist); - maxrank = tmprank; - retval = XFUNC_JOINPRD; - } - } - if (maxrank == (-1 * MAXFLOAT)) /* no expensive clauses */ - return(0); - - /* - ** Pullup over join if clause is higher rank than join, or if - ** join is nested loop and current path is inner child (note that - ** restrictions on the inner of a nested loop don't buy you anything -- - ** you still have to scan the entire inner relation each time). - ** Note that the cost of a secondary join clause is only what's - ** calculated by xfunc_expense(), since the actual joining - ** (i.e. the usual path_cost) is paid for by the primary join clause. - */ - if (primjoinclause != LispNil) { - joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil); - joincost = xfunc_join_expense(parentpath, whichchild); - - if (XfuncMode == XFUNC_PULLALL || - (XfuncMode != XFUNC_WAIT && - ((joincost != 0 && - (maxrank = xfunc_rank(get_clause(maxcinfo))) > - ((joinselec - 1.0) / joincost)) - || (joincost == 0 && joinselec < 1) - || (!is_join(childpath) - && (whichchild == INNER) - && IsA(parentpath,JoinPath) - && !IsA(parentpath,HashPath) - && !IsA(parentpath,MergePath))))) { - - *maxcinfopt = maxcinfo; - return(retval); - - }else if (maxrank != -(MAXFLOAT)) { - /* - ** we've left an expensive restriction below a join. Since - ** we may pullup this restriction in predmig.c, we'd best - ** set the Rel of this join to be unpruneable - */ - set_pruneable(get_parent(parentpath), false); - /* and fall through */ + + /* + * * If child is a join path, and there are multiple join clauses, * + * see if any join clause has even higher rank than the highest * + * local predicate + */ + if (is_join(childpath) && xfunc_num_join_clauses((JoinPath) childpath) > 1) + for (tmplist = get_pathclauseinfo((JoinPath) childpath); + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + + if (tmplist != LispNil && + (tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) + > maxrank) + { + maxcinfo = (CInfo) lfirst(tmplist); + maxrank = tmprank; + retval = XFUNC_JOINPRD; + } + } + if (maxrank == (-1 * MAXFLOAT)) /* no expensive clauses */ + return (0); + + /* + * * Pullup over join if clause is higher rank than join, or if * join + * is nested loop and current path is inner child (note that * + * restrictions on the inner of a nested loop don't buy you anything + * -- * you still have to scan the entire inner relation each time). * + * Note that the cost of a secondary join clause is only what's * + * calculated by xfunc_expense(), since the actual joining * (i.e. the + * usual path_cost) is paid for by the primary join clause. + */ + if (primjoinclause != LispNil) + { + joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil); + joincost = xfunc_join_expense(parentpath, whichchild); + + if (XfuncMode == XFUNC_PULLALL || + (XfuncMode != XFUNC_WAIT && + ((joincost != 0 && + (maxrank = xfunc_rank(get_clause(maxcinfo))) > + ((joinselec - 1.0) / joincost)) + || (joincost == 0 && joinselec < 1) + || (!is_join(childpath) + && (whichchild == INNER) + && IsA(parentpath, JoinPath) + && !IsA(parentpath, HashPath) + && !IsA(parentpath, MergePath))))) + { + + *maxcinfopt = maxcinfo; + return (retval); + + } + else if (maxrank != -(MAXFLOAT)) + { + + /* + * * we've left an expensive restriction below a join. Since * + * we may pullup this restriction in predmig.c, we'd best * + * set the Rel of this join to be unpruneable + */ + set_pruneable(get_parent(parentpath), false); + /* and fall through */ + } } - } - return(0); + return (0); } /* ** xfunc_pullup -- - ** move clause from child pathnode to parent pathnode. This operation + ** move clause from child pathnode to parent pathnode. This operation ** makes the child pathnode produce a larger relation than it used to. ** This means that we must construct a new Rel just for the childpath, ** although this Rel will not be added to the list of Rels to be joined up @@ -238,101 +267,111 @@ int xfunc_shouldpull(Query* queryInfo, ** ** Now returns a pointer to the new pulled-up CInfo. -- JMH, 11/18/92 */ -CInfo xfunc_pullup(Query* queryInfo, - Path childpath, - JoinPath parentpath, - CInfo cinfo, /* clause to pull up */ - int whichchild,/* whether child is INNER or OUTER of join */ - int clausetype)/* whether clause to pull is join or local */ +CInfo +xfunc_pullup(Query * queryInfo, + Path childpath, + JoinPath parentpath, + CInfo cinfo, /* clause to pull up */ + int whichchild, /* whether child is INNER or OUTER of join */ + int clausetype) /* whether clause to pull is join or local */ { - Path newkid; - Rel newrel; - Cost pulled_selec; - Cost cost; - CInfo newinfo; - - /* remove clause from childpath */ - newkid = (Path)copyObject((Node)childpath); - if (clausetype == XFUNC_LOCPRD) { - set_locclauseinfo(newkid, - xfunc_LispRemove((LispValue)cinfo, - (List)get_locclauseinfo(newkid))); - }else { - set_pathclauseinfo - ((JoinPath)newkid, - xfunc_LispRemove((LispValue)cinfo, - (List)get_pathclauseinfo((JoinPath)newkid))); - } - - /* - ** give the new child path its own Rel node that reflects the - ** lack of the pulled-up predicate - */ - pulled_selec = compute_clause_selec(queryInfo, - get_clause(cinfo), LispNil); - xfunc_copyrel(get_parent(newkid), &newrel); - set_parent(newkid, newrel); - set_pathlist(newrel, lcons(newkid, NIL)); - set_unorderedpath(newrel, (PathPtr)newkid); - set_cheapestpath(newrel, (PathPtr)newkid); - set_size(newrel, - (Count)((Cost)get_size(get_parent(childpath)) / pulled_selec)); - - /* - ** fix up path cost of newkid. To do this we subtract away all the - ** xfunc_costs of childpath, then recompute the xfunc_costs of newkid - */ - cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath); - Assert(cost >= 0); - set_path_cost(newkid, cost); - cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid); - set_path_cost(newkid, cost); - - /* - ** We copy the cinfo, since it may appear in other plans, and we're going - ** to munge it. -- JMH, 7/22/92 - */ - newinfo = (CInfo)copyObject((Node)cinfo); - - /* - ** Fix all vars in the clause - ** to point to the right varno and varattno in parentpath - */ - xfunc_fixvars(get_clause(newinfo), newrel, whichchild); - - /* add clause to parentpath, and fix up its cost. */ - set_locclauseinfo(parentpath, - lispCons((LispValue)newinfo, - (LispValue)get_locclauseinfo(parentpath))); - /* put new childpath into the path tree */ - if (whichchild == INNER) { - set_innerjoinpath(parentpath, (pathPtr)newkid); - }else { - set_outerjoinpath(parentpath, (pathPtr)newkid); - } - - /* - ** recompute parentpath cost from scratch -- the cost - ** of the join method has changed - */ - cost = xfunc_total_path_cost(parentpath); - set_path_cost(parentpath, cost); - - return(newinfo); + Path newkid; + Rel newrel; + Cost pulled_selec; + Cost cost; + CInfo newinfo; + + /* remove clause from childpath */ + newkid = (Path) copyObject((Node) childpath); + if (clausetype == XFUNC_LOCPRD) + { + set_locclauseinfo(newkid, + xfunc_LispRemove((LispValue) cinfo, + (List) get_locclauseinfo(newkid))); + } + else + { + set_pathclauseinfo + ((JoinPath) newkid, + xfunc_LispRemove((LispValue) cinfo, + (List) get_pathclauseinfo((JoinPath) newkid))); + } + + /* + * * give the new child path its own Rel node that reflects the * lack + * of the pulled-up predicate + */ + pulled_selec = compute_clause_selec(queryInfo, + get_clause(cinfo), LispNil); + xfunc_copyrel(get_parent(newkid), &newrel); + set_parent(newkid, newrel); + set_pathlist(newrel, lcons(newkid, NIL)); + set_unorderedpath(newrel, (PathPtr) newkid); + set_cheapestpath(newrel, (PathPtr) newkid); + set_size(newrel, + (Count) ((Cost) get_size(get_parent(childpath)) / pulled_selec)); + + /* + * * fix up path cost of newkid. To do this we subtract away all the * + * xfunc_costs of childpath, then recompute the xfunc_costs of newkid + */ + cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath); + Assert(cost >= 0); + set_path_cost(newkid, cost); + cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid); + set_path_cost(newkid, cost); + + /* + * * We copy the cinfo, since it may appear in other plans, and we're + * going * to munge it. -- JMH, 7/22/92 + */ + newinfo = (CInfo) copyObject((Node) cinfo); + + /* + * * Fix all vars in the clause * to point to the right varno and + * varattno in parentpath + */ + xfunc_fixvars(get_clause(newinfo), newrel, whichchild); + + /* add clause to parentpath, and fix up its cost. */ + set_locclauseinfo(parentpath, + lispCons((LispValue) newinfo, + (LispValue) get_locclauseinfo(parentpath))); + /* put new childpath into the path tree */ + if (whichchild == INNER) + { + set_innerjoinpath(parentpath, (pathPtr) newkid); + } + else + { + set_outerjoinpath(parentpath, (pathPtr) newkid); + } + + /* + * * recompute parentpath cost from scratch -- the cost * of the join + * method has changed + */ + cost = xfunc_total_path_cost(parentpath); + set_path_cost(parentpath, cost); + + return (newinfo); } /* - ** calculate (selectivity-1)/cost. + ** calculate (selectivity-1)/cost. */ -Cost xfunc_rank(Query *queryInfo,LispValue clause) +Cost +xfunc_rank(Query * queryInfo, LispValue clause) { - Cost selec = compute_clause_selec(queryInfo, clause, LispNil); - Cost cost = xfunc_expense(queryInfo,clause); - - if (cost == 0) - if (selec > 1) return(MAXFLOAT); - else return(-(MAXFLOAT)); - return((selec - 1)/cost); + Cost selec = compute_clause_selec(queryInfo, clause, LispNil); + Cost cost = xfunc_expense(queryInfo, clause); + + if (cost == 0) + if (selec > 1) + return (MAXFLOAT); + else + return (-(MAXFLOAT)); + return ((selec - 1) / cost); } /* @@ -340,91 +379,99 @@ Cost xfunc_rank(Query *queryInfo,LispValue clause) ** by the cardinalities of all the base relations of the query that are *not* ** referenced in the clause. */ -Cost xfunc_expense(Query* queryInfo, clause) - LispValue clause; +Cost +xfunc_expense(Query * queryInfo, clause) +LispValue clause; { - Cost cost = xfunc_local_expense(clause); - - if (cost) + Cost cost = xfunc_local_expense(clause); + + if (cost) { - Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil); - if (card) - cost /= card; + Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil); + + if (card) + cost /= card; } - - return(cost); + + return (cost); } /* ** xfunc_join_expense -- ** Find global expense of a join clause */ -Cost xfunc_join_expense(Query *queryInfo, JoinPath path, int whichchild) +Cost +xfunc_join_expense(Query * queryInfo, JoinPath path, int whichchild) { - LispValue primjoinclause = xfunc_primary_join(path); - - /* - ** the second argument to xfunc_card_unreferenced reflects all the - ** relations involved in the join clause, i.e. all the relids in the Rel - ** of the join clause - */ - Count card = 0; - Cost cost = xfunc_expense_per_tuple(path, whichchild); - - card = xfunc_card_unreferenced(queryInfo, - primjoinclause, - get_relids(get_parent(path))); - if (primjoinclause) - cost += xfunc_local_expense(primjoinclause); - - if (card) cost /= card; - - return(cost); + LispValue primjoinclause = xfunc_primary_join(path); + + /* + * * the second argument to xfunc_card_unreferenced reflects all the * + * relations involved in the join clause, i.e. all the relids in the + * Rel * of the join clause + */ + Count card = 0; + Cost cost = xfunc_expense_per_tuple(path, whichchild); + + card = xfunc_card_unreferenced(queryInfo, + primjoinclause, + get_relids(get_parent(path))); + if (primjoinclause) + cost += xfunc_local_expense(primjoinclause); + + if (card) + cost /= card; + + return (cost); } /* ** Recursively find the per-tuple expense of a clause. See ** xfunc_func_expense for more discussion. */ -Cost xfunc_local_expense(LispValue clause) +Cost +xfunc_local_expense(LispValue clause) { - Cost cost = 0; /* running expense */ - LispValue tmpclause; - - /* First handle the base case */ - if (IsA(clause,Const) || IsA(clause,Var) || IsA(clause,Param)) - return(0); - /* now other stuff */ - else if (IsA(clause,Iter)) - /* Too low. Should multiply by the expected number of iterations. */ - return(xfunc_local_expense(get_iterexpr((Iter)clause))); - else if (IsA(clause,ArrayRef)) - return(xfunc_local_expense(get_refexpr((ArrayRef)clause))); - else if (fast_is_clause(clause)) - return(xfunc_func_expense((LispValue)get_op(clause), - (LispValue)get_opargs(clause))); - else if (fast_is_funcclause(clause)) - return(xfunc_func_expense((LispValue)get_function(clause), - (LispValue)get_funcargs(clause))); - else if (fast_not_clause(clause)) - return(xfunc_local_expense(lsecond(clause))); - else if (fast_or_clause(clause)) { - /* find cost of evaluating each disjunct */ - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - cost += xfunc_local_expense(lfirst(tmpclause)); - return(cost); - }else { - elog(WARN, "Clause node of undetermined type"); - return(-1); - } + Cost cost = 0; /* running expense */ + LispValue tmpclause; + + /* First handle the base case */ + if (IsA(clause, Const) || IsA(clause, Var) || IsA(clause, Param)) + return (0); + /* now other stuff */ + else if (IsA(clause, Iter)) + /* Too low. Should multiply by the expected number of iterations. */ + return (xfunc_local_expense(get_iterexpr((Iter) clause))); + else if (IsA(clause, ArrayRef)) + return (xfunc_local_expense(get_refexpr((ArrayRef) clause))); + else if (fast_is_clause(clause)) + return (xfunc_func_expense((LispValue) get_op(clause), + (LispValue) get_opargs(clause))); + else if (fast_is_funcclause(clause)) + return (xfunc_func_expense((LispValue) get_function(clause), + (LispValue) get_funcargs(clause))); + else if (fast_not_clause(clause)) + return (xfunc_local_expense(lsecond(clause))); + else if (fast_or_clause(clause)) + { + /* find cost of evaluating each disjunct */ + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + cost += xfunc_local_expense(lfirst(tmpclause)); + return (cost); + } + else + { + elog(WARN, "Clause node of undetermined type"); + return (-1); + } } /* ** xfunc_func_expense -- ** given a Func or Oper and its args, find its expense. ** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric - ** than the one here. We can ignore the expected number of tuples for + ** than the one here. We can ignore the expected number of tuples for ** our calculations; we just need the per-tuple expense. But he also ** proposes components to take into account the costs of accessing disk and ** archive. We didn't adopt that scheme here; eventually the vacuum @@ -434,268 +481,323 @@ Cost xfunc_local_expense(LispValue clause) ** accessing secondary or tertiary storage, since we don't have sufficient ** stats to do it right. */ -Cost xfunc_func_expense(LispValue node, LispValue args) +Cost +xfunc_func_expense(LispValue node, LispValue args) { - HeapTuple tupl; /* the pg_proc tuple for each function */ - Form_pg_proc proc; /* a data structure to hold the pg_proc tuple */ - int width = 0; /* byte width of the field referenced by each clause */ - RegProcedure funcid; /* ID of function associate with node */ - Cost cost = 0; /* running expense */ - LispValue tmpclause; - LispValue operand; /* one operand of an operator */ - - if (IsA(node,Oper)) { - /* don't trust the opid in the Oper node. Use the opno. */ - if (!(funcid = get_opcode(get_opno((Oper)node)))) - elog(WARN, "Oper's function is undefined"); - }else { - funcid = get_funcid((Func)node); - } - - /* look up tuple in cache */ - tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for procedure %d", funcid); - proc = (Form_pg_proc) GETSTRUCT(tupl); - - /* - ** if it's a Postquel function, its cost is stored in the - ** associated plan. - */ - if (proc->prolang == SQLlanguageId) { - LispValue tmpplan; - List planlist; - - if (IsA(node,Oper) || get_func_planlist((Func)node) == LispNil) { - Oid *argOidVect; /* vector of argtypes */ - char *pq_src; /* text of PQ function */ - int nargs; /* num args to PQ function */ - QueryTreeList *queryTree_list; /* dummy variable */ - - /* - ** plan the function, storing it in the Func node for later - ** use by the executor. - */ - pq_src = (char *) textout(&(proc->prosrc)); - nargs = proc->pronargs; - if (nargs > 0) - argOidVect = proc->proargtypes; - planlist = (List)pg_plan(pq_src, argOidVect, nargs, - &parseTree_list, None); - if (IsA(node,Func)) - set_func_planlist((Func)node, planlist); - - }else {/* plan has been cached inside the Func node already */ - planlist = get_func_planlist((Func)node); + HeapTuple tupl; /* the pg_proc tuple for each function */ + Form_pg_proc proc; /* a data structure to hold the pg_proc + * tuple */ + int width = 0; /* byte width of the field referenced by + * each clause */ + RegProcedure funcid; /* ID of function associate with node */ + Cost cost = 0; /* running expense */ + LispValue tmpclause; + LispValue operand; /* one operand of an operator */ + + if (IsA(node, Oper)) + { + /* don't trust the opid in the Oper node. Use the opno. */ + if (!(funcid = get_opcode(get_opno((Oper) node)))) + elog(WARN, "Oper's function is undefined"); } - - /* - ** Return the sum of the costs of the plans (the PQ function - ** may have many queries in its body). - */ - foreach(tmpplan, planlist) - cost += get_cost((Plan)lfirst(tmpplan)); - return(cost); - }else { /* it's a C function */ + else + { + funcid = get_funcid((Func) node); + } + + /* look up tuple in cache */ + tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for procedure %d", funcid); + proc = (Form_pg_proc) GETSTRUCT(tupl); + /* - ** find the cost of evaluating the function's arguments - ** and the width of the operands + * * if it's a Postquel function, its cost is stored in the * + * associated plan. */ - for (tmpclause = args; tmpclause != LispNil; - tmpclause = lnext(tmpclause)) { - - if ((operand = lfirst(tmpclause)) != LispNil) { - cost += xfunc_local_expense(operand); - width += xfunc_width(operand); - } + if (proc->prolang == SQLlanguageId) + { + LispValue tmpplan; + List planlist; + + if (IsA(node, Oper) || get_func_planlist((Func) node) == LispNil) + { + Oid *argOidVect; /* vector of argtypes */ + char *pq_src; /* text of PQ function */ + int nargs; /* num args to PQ function */ + QueryTreeList *queryTree_list; /* dummy variable */ + + /* + * * plan the function, storing it in the Func node for later * + * use by the executor. + */ + pq_src = (char *) textout(&(proc->prosrc)); + nargs = proc->pronargs; + if (nargs > 0) + argOidVect = proc->proargtypes; + planlist = (List) pg_plan(pq_src, argOidVect, nargs, + &parseTree_list, None); + if (IsA(node, Func)) + set_func_planlist((Func) node, planlist); + + } + else + { /* plan has been cached inside the Func + * node already */ + planlist = get_func_planlist((Func) node); + } + + /* + * * Return the sum of the costs of the plans (the PQ function * + * may have many queries in its body). + */ + foreach(tmpplan, planlist) + cost += get_cost((Plan) lfirst(tmpplan)); + return (cost); + } + else + { /* it's a C function */ + + /* + * * find the cost of evaluating the function's arguments * and + * the width of the operands + */ + for (tmpclause = args; tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + { + + if ((operand = lfirst(tmpclause)) != LispNil) + { + cost += xfunc_local_expense(operand); + width += xfunc_width(operand); + } + } + + /* + * * when stats become available, add in cost of accessing + * secondary * and tertiary storage here. + */ + return (cost + + (Cost) proc->propercall_cpu + + (Cost) proc->properbyte_cpu * (Cost) proc->probyte_pct / 100.00 * + (Cost) width + + /* + * Pct_of_obj_in_mem DISK_COST * proc->probyte_pct/100.00 * width + * Pct_of_obj_on_disk + ARCH_COST * proc->probyte_pct/100.00 * + * width Pct_of_obj_on_arch + */ + ); } - - /* - ** when stats become available, add in cost of accessing secondary - ** and tertiary storage here. - */ - return(cost + - (Cost)proc->propercall_cpu + - (Cost)proc->properbyte_cpu * (Cost)proc->probyte_pct/100.00 * - (Cost)width - /* - * Pct_of_obj_in_mem - DISK_COST * proc->probyte_pct/100.00 * width - * Pct_of_obj_on_disk + - ARCH_COST * proc->probyte_pct/100.00 * width - * Pct_of_obj_on_arch - */ - ); - } } -/* +/* ** xfunc_width -- ** recursively find the width of a expression */ -int xfunc_width(LispValue clause) +int +xfunc_width(LispValue clause) { - Relation rd; /* Relation Descriptor */ - HeapTuple tupl; /* structure to hold a cached tuple */ - TypeTupleForm type; /* structure to hold a type tuple */ - int retval = 0; - - if (IsA(clause,Const)) { - /* base case: width is the width of this constant */ - retval = get_constlen((Const) clause); - goto exit; - }else if (IsA(clause,ArrayRef)) { - /* base case: width is width of the refelem within the array */ - retval = get_refelemlength((ArrayRef)clause); - goto exit; - }else if (IsA(clause,Var)) { - /* base case: width is width of this attribute */ - tupl = SearchSysCacheTuple(TYPOID, - PointerGetDatum(get_vartype((Var)clause)), - 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for type %d", - get_vartype((Var)clause)); - type = (TypeTupleForm) GETSTRUCT(tupl); - if (get_varattno((Var)clause) == 0) { - /* clause is a tuple. Get its width */ - rd = heap_open(type->typrelid); - retval = xfunc_tuple_width(rd); - heap_close(rd); - }else { - /* attribute is a base type */ - retval = type->typlen; + Relation rd; /* Relation Descriptor */ + HeapTuple tupl; /* structure to hold a cached tuple */ + TypeTupleForm type; /* structure to hold a type tuple */ + int retval = 0; + + if (IsA(clause, Const)) + { + /* base case: width is the width of this constant */ + retval = get_constlen((Const) clause); + goto exit; } - goto exit; - }else if (IsA(clause,Param)) { - if (typeid_get_relid(get_paramtype((Param)clause))) { - /* Param node returns a tuple. Find its width */ - rd = heap_open(typeid_get_relid(get_paramtype((Param)clause))); - retval = xfunc_tuple_width(rd); - heap_close(rd); - }else if (get_param_tlist((Param)clause) != LispNil) { - /* Param node projects a complex type */ - Assert(length(get_param_tlist((Param)clause)) == 1); /* sanity */ - retval = - xfunc_width((LispValue) - get_expr(lfirst(get_param_tlist((Param)clause)))); - }else { - /* Param node returns a base type */ - retval = tlen(get_id_type(get_paramtype((Param)clause))); + else if (IsA(clause, ArrayRef)) + { + /* base case: width is width of the refelem within the array */ + retval = get_refelemlength((ArrayRef) clause); + goto exit; } - goto exit; - }else if (IsA(clause,Iter)) { - /* - ** An Iter returns a setof things, so return the width of a single - ** thing. - ** Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET FIXED, - ** SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! - ** This whole Iter business is bogus, anyway. - */ - retval = xfunc_width(get_iterexpr((Iter)clause)); - goto exit; - }else if (fast_is_clause(clause)) { - /* - ** get function associated with this Oper, and treat this as - ** a Func - */ - tupl = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(get_opno((Oper)get_op(clause))), - 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for procedure %d", - get_opno((Oper)get_op(clause))); - return(xfunc_func_width - ((RegProcedure)(((OperatorTupleForm)(GETSTRUCT(tupl)))->oprcode), - (LispValue)get_opargs(clause))); - }else if (fast_is_funcclause(clause)) { - Func func = (Func)get_function(clause); - if (get_func_tlist(func) != LispNil) { - /* this function has a projection on it. Get the length - of the projected attribute */ - Assert(length(get_func_tlist(func)) == 1); /* sanity */ - retval = - xfunc_width((LispValue) - get_expr(lfirst(get_func_tlist(func)))); - goto exit; - }else { - return(xfunc_func_width((RegProcedure)get_funcid(func), - (LispValue)get_funcargs(clause))); + else if (IsA(clause, Var)) + { + /* base case: width is width of this attribute */ + tupl = SearchSysCacheTuple(TYPOID, + PointerGetDatum(get_vartype((Var) clause)), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for type %d", + get_vartype((Var) clause)); + type = (TypeTupleForm) GETSTRUCT(tupl); + if (get_varattno((Var) clause) == 0) + { + /* clause is a tuple. Get its width */ + rd = heap_open(type->typrelid); + retval = xfunc_tuple_width(rd); + heap_close(rd); + } + else + { + /* attribute is a base type */ + retval = type->typlen; + } + goto exit; + } + else if (IsA(clause, Param)) + { + if (typeid_get_relid(get_paramtype((Param) clause))) + { + /* Param node returns a tuple. Find its width */ + rd = heap_open(typeid_get_relid(get_paramtype((Param) clause))); + retval = xfunc_tuple_width(rd); + heap_close(rd); + } + else if (get_param_tlist((Param) clause) != LispNil) + { + /* Param node projects a complex type */ + Assert(length(get_param_tlist((Param) clause)) == 1); /* sanity */ + retval = + xfunc_width((LispValue) + get_expr(lfirst(get_param_tlist((Param) clause)))); + } + else + { + /* Param node returns a base type */ + retval = tlen(get_id_type(get_paramtype((Param) clause))); + } + goto exit; + } + else if (IsA(clause, Iter)) + { + + /* + * * An Iter returns a setof things, so return the width of a + * single * thing. * Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET + * FIXED, * SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! * + * This whole Iter business is bogus, anyway. + */ + retval = xfunc_width(get_iterexpr((Iter) clause)); + goto exit; + } + else if (fast_is_clause(clause)) + { + + /* + * * get function associated with this Oper, and treat this as * a + * Func + */ + tupl = SearchSysCacheTuple(OPROID, + ObjectIdGetDatum(get_opno((Oper) get_op(clause))), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for procedure %d", + get_opno((Oper) get_op(clause))); + return (xfunc_func_width + ((RegProcedure) (((OperatorTupleForm) (GETSTRUCT(tupl)))->oprcode), + (LispValue) get_opargs(clause))); + } + else if (fast_is_funcclause(clause)) + { + Func func = (Func) get_function(clause); + + if (get_func_tlist(func) != LispNil) + { + + /* + * this function has a projection on it. Get the length of + * the projected attribute + */ + Assert(length(get_func_tlist(func)) == 1); /* sanity */ + retval = + xfunc_width((LispValue) + get_expr(lfirst(get_func_tlist(func)))); + goto exit; + } + else + { + return (xfunc_func_width((RegProcedure) get_funcid(func), + (LispValue) get_funcargs(clause))); + } + } + else + { + elog(WARN, "Clause node of undetermined type"); + return (-1); } - }else { - elog(WARN, "Clause node of undetermined type"); - return(-1); - } - - exit: - if (retval == -1) - retval = VARLEN_DEFAULT; - return(retval); + +exit: + if (retval == -1) + retval = VARLEN_DEFAULT; + return (retval); } /* ** xfunc_card_unreferenced: - ** find all relations not referenced in clause, and multiply their - ** cardinalities. Ignore relation of cardinality 0. + ** find all relations not referenced in clause, and multiply their + ** cardinalities. Ignore relation of cardinality 0. ** User may pass in referenced list, if they know it (useful ** for joins). */ -static Count -xfunc_card_unreferenced(Query *queryInfo, - LispValue clause, Relid referenced) +static Count +xfunc_card_unreferenced(Query * queryInfo, + LispValue clause, Relid referenced) { - Relid unreferenced, allrelids = LispNil; - LispValue temp; - - /* find all relids of base relations referenced in query */ - foreach (temp,queryInfo->base_relation_list_) + Relid unreferenced, + allrelids = LispNil; + LispValue temp; + + /* find all relids of base relations referenced in query */ + foreach(temp, queryInfo->base_relation_list_) { - Assert(lnext(get_relids((Rel)lfirst(temp))) == LispNil); - allrelids = lappend(allrelids, - lfirst(get_relids((Rel)lfirst(temp)))); + Assert(lnext(get_relids((Rel) lfirst(temp))) == LispNil); + allrelids = lappend(allrelids, + lfirst(get_relids((Rel) lfirst(temp)))); } - - /* find all relids referenced in query but not in clause */ - if (!referenced) - referenced = xfunc_find_references(clause); - unreferenced = set_difference(allrelids, referenced); - - return(xfunc_card_product(unreferenced)); + + /* find all relids referenced in query but not in clause */ + if (!referenced) + referenced = xfunc_find_references(clause); + unreferenced = set_difference(allrelids, referenced); + + return (xfunc_card_product(unreferenced)); } /* - ** xfunc_card_product + ** xfunc_card_product ** multiple together cardinalities of a list relations. */ -Count xfunc_card_product(Query *queryInfo, Relid relids) +Count +xfunc_card_product(Query * queryInfo, Relid relids) { - LispValue cinfonode; - LispValue temp; - Rel currel; - Cost tuples; - Count retval = 0; - - foreach(temp,relids) { - currel = get_rel(lfirst(temp)); - tuples = get_tuples(currel); - - if (tuples) { /* not of cardinality 0 */ - /* factor in the selectivity of all zero-cost clauses */ - foreach (cinfonode, get_clauseinfo(currel)) { - if (!xfunc_expense(queryInfo,get_clause((CInfo)lfirst(cinfonode)))) - tuples *= - compute_clause_selec(queryInfo, - get_clause((CInfo)lfirst(cinfonode)), - LispNil); - } - - if (retval == 0) retval = tuples; - else retval *= tuples; + LispValue cinfonode; + LispValue temp; + Rel currel; + Cost tuples; + Count retval = 0; + + foreach(temp, relids) + { + currel = get_rel(lfirst(temp)); + tuples = get_tuples(currel); + + if (tuples) + { /* not of cardinality 0 */ + /* factor in the selectivity of all zero-cost clauses */ + foreach(cinfonode, get_clauseinfo(currel)) + { + if (!xfunc_expense(queryInfo, get_clause((CInfo) lfirst(cinfonode)))) + tuples *= + compute_clause_selec(queryInfo, + get_clause((CInfo) lfirst(cinfonode)), + LispNil); + } + + if (retval == 0) + retval = tuples; + else + retval *= tuples; + } } - } - if (retval == 0) retval = 1; /* saves caller from dividing by zero */ - return(retval); + if (retval == 0) + retval = 1; /* saves caller from dividing by zero */ + return (retval); } @@ -703,48 +805,60 @@ Count xfunc_card_product(Query *queryInfo, Relid relids) ** xfunc_find_references: ** Traverse a clause and find all relids referenced in the clause. */ -List xfunc_find_references(LispValue clause) +List +xfunc_find_references(LispValue clause) { - List retval = (List)LispNil; - LispValue tmpclause; - - /* Base cases */ - if (IsA(clause,Var)) - return(lispCons(lfirst(get_varid((Var)clause)), LispNil)); - else if (IsA(clause,Const) || IsA(clause,Param)) - return((List)LispNil); - - /* recursion */ - else if (IsA(clause,Iter)) - /* Too low. Should multiply by the expected number of iterations. maybe */ - return(xfunc_find_references(get_iterexpr((Iter)clause))); - else if (IsA(clause,ArrayRef)) - return(xfunc_find_references(get_refexpr((ArrayRef)clause))); - else if (fast_is_clause(clause)) { - /* string together result of all operands of Oper */ - for (tmpclause = (LispValue)get_opargs(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return(retval); - }else if (fast_is_funcclause(clause)) { - /* string together result of all args of Func */ - for (tmpclause = (LispValue)get_funcargs(clause); - tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return(retval); - }else if (fast_not_clause(clause)) - return(xfunc_find_references(lsecond(clause))); - else if (fast_or_clause(clause)) { - /* string together result of all operands of OR */ - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return(retval); - }else { - elog(WARN, "Clause node of undetermined type"); - return((List)LispNil); - } + List retval = (List) LispNil; + LispValue tmpclause; + + /* Base cases */ + if (IsA(clause, Var)) + return (lispCons(lfirst(get_varid((Var) clause)), LispNil)); + else if (IsA(clause, Const) || IsA(clause, Param)) + return ((List) LispNil); + + /* recursion */ + else if (IsA(clause, Iter)) + + /* + * Too low. Should multiply by the expected number of iterations. + * maybe + */ + return (xfunc_find_references(get_iterexpr((Iter) clause))); + else if (IsA(clause, ArrayRef)) + return (xfunc_find_references(get_refexpr((ArrayRef) clause))); + else if (fast_is_clause(clause)) + { + /* string together result of all operands of Oper */ + for (tmpclause = (LispValue) get_opargs(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return (retval); + } + else if (fast_is_funcclause(clause)) + { + /* string together result of all args of Func */ + for (tmpclause = (LispValue) get_funcargs(clause); + tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return (retval); + } + else if (fast_not_clause(clause)) + return (xfunc_find_references(lsecond(clause))); + else if (fast_or_clause(clause)) + { + /* string together result of all operands of OR */ + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return (retval); + } + else + { + elog(WARN, "Clause node of undetermined type"); + return ((List) LispNil); + } } /* @@ -753,212 +867,219 @@ List xfunc_find_references(LispValue clause) ** min rank Hash or Merge clause, while for Nested Loop it's the ** min rank pathclause */ -LispValue xfunc_primary_join(JoinPath pathnode) +LispValue +xfunc_primary_join(JoinPath pathnode) { - LispValue joinclauselist = get_pathclauseinfo(pathnode); - CInfo mincinfo; - LispValue tmplist; - LispValue minclause = LispNil; - Cost minrank, tmprank; - - if (IsA(pathnode,MergePath)) + LispValue joinclauselist = get_pathclauseinfo(pathnode); + CInfo mincinfo; + LispValue tmplist; + LispValue minclause = LispNil; + Cost minrank, + tmprank; + + if (IsA(pathnode, MergePath)) { - for(tmplist = get_path_mergeclauses((MergePath)pathnode), - minclause = lfirst(tmplist), - minrank = xfunc_rank(minclause); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(lfirst(tmplist))) - < minrank) - { - minrank = tmprank; - minclause = lfirst(tmplist); - } - return(minclause); + for (tmplist = get_path_mergeclauses((MergePath) pathnode), + minclause = lfirst(tmplist), + minrank = xfunc_rank(minclause); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(lfirst(tmplist))) + < minrank) + { + minrank = tmprank; + minclause = lfirst(tmplist); + } + return (minclause); } - else if (IsA(pathnode,HashPath)) + else if (IsA(pathnode, HashPath)) { - for(tmplist = get_path_hashclauses((HashPath)pathnode), - minclause = lfirst(tmplist), - minrank = xfunc_rank(minclause); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(lfirst(tmplist))) - < minrank) - { - minrank = tmprank; - minclause = lfirst(tmplist); - } - return(minclause); + for (tmplist = get_path_hashclauses((HashPath) pathnode), + minclause = lfirst(tmplist), + minrank = xfunc_rank(minclause); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(lfirst(tmplist))) + < minrank) + { + minrank = tmprank; + minclause = lfirst(tmplist); + } + return (minclause); } - - /* if we drop through, it's nested loop join */ - if (joinclauselist == LispNil) - return(LispNil); - - for(tmplist = joinclauselist, mincinfo = (CInfo) lfirst(joinclauselist), - minrank = xfunc_rank(get_clause((CInfo) lfirst(tmplist))); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) - < minrank) - { - minrank = tmprank; - mincinfo = (CInfo) lfirst(tmplist); - } - return((LispValue)get_clause(mincinfo)); + + /* if we drop through, it's nested loop join */ + if (joinclauselist == LispNil) + return (LispNil); + + for (tmplist = joinclauselist, mincinfo = (CInfo) lfirst(joinclauselist), + minrank = xfunc_rank(get_clause((CInfo) lfirst(tmplist))); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) + < minrank) + { + minrank = tmprank; + mincinfo = (CInfo) lfirst(tmplist); + } + return ((LispValue) get_clause(mincinfo)); } /* ** xfunc_get_path_cost ** get the expensive function costs of the path */ -Cost xfunc_get_path_cost(Query *queryInfo, Path pathnode) +Cost +xfunc_get_path_cost(Query * queryInfo, Path pathnode) { - Cost cost = 0; - LispValue tmplist; - Cost selec = 1.0; - - /* - ** first add in the expensive local function costs. - ** We ensure that the clauses are sorted by rank, so that we - ** know (via selectivities) the number of tuples that will be checked - ** by each function. If we're not doing any optimization of expensive - ** functions, we don't sort. - */ - if (XfuncMode != XFUNC_OFF) - set_locclauseinfo(pathnode, lisp_qsort(get_locclauseinfo(pathnode), - xfunc_cinfo_compare)); - for(tmplist = get_locclauseinfo(pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + Cost cost = 0; + LispValue tmplist; + Cost selec = 1.0; + + /* + * * first add in the expensive local function costs. * We ensure that + * the clauses are sorted by rank, so that we * know (via + * selectivities) the number of tuples that will be checked * by each + * function. If we're not doing any optimization of expensive * + * functions, we don't sort. + */ + if (XfuncMode != XFUNC_OFF) + set_locclauseinfo(pathnode, lisp_qsort(get_locclauseinfo(pathnode), + xfunc_cinfo_compare)); + for (tmplist = get_locclauseinfo(pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(get_clause((CInfo)lfirst(tmplist))) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - get_clause((CInfo)lfirst(tmplist)), - LispNil); + cost += (Cost) (xfunc_local_expense(get_clause((CInfo) lfirst(tmplist))) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + get_clause((CInfo) lfirst(tmplist)), + LispNil); } - - /* - ** Now add in any node-specific expensive function costs. - ** Again, we must ensure that the clauses are sorted by rank. - */ - if (IsA(pathnode,JoinPath)) + + /* + * * Now add in any node-specific expensive function costs. * Again, + * we must ensure that the clauses are sorted by rank. + */ + if (IsA(pathnode, JoinPath)) { - if (XfuncMode != XFUNC_OFF) - set_pathclauseinfo((JoinPath)pathnode, lisp_qsort - (get_pathclauseinfo((JoinPath)pathnode), - xfunc_cinfo_compare)); - for(tmplist = get_pathclauseinfo((JoinPath)pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + if (XfuncMode != XFUNC_OFF) + set_pathclauseinfo((JoinPath) pathnode, lisp_qsort + (get_pathclauseinfo((JoinPath) pathnode), + xfunc_cinfo_compare)); + for (tmplist = get_pathclauseinfo((JoinPath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(get_clause((CInfo)lfirst(tmplist))) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - get_clause((CInfo)lfirst(tmplist)), - LispNil); + cost += (Cost) (xfunc_local_expense(get_clause((CInfo) lfirst(tmplist))) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + get_clause((CInfo) lfirst(tmplist)), + LispNil); } } - if (IsA(pathnode,HashPath)) + if (IsA(pathnode, HashPath)) { - if (XfuncMode != XFUNC_OFF) - set_path_hashclauses - ((HashPath)pathnode, - lisp_qsort(get_path_hashclauses((HashPath)pathnode), - xfunc_clause_compare)); - for(tmplist = get_path_hashclauses((HashPath)pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + if (XfuncMode != XFUNC_OFF) + set_path_hashclauses + ((HashPath) pathnode, + lisp_qsort(get_path_hashclauses((HashPath) pathnode), + xfunc_clause_compare)); + for (tmplist = get_path_hashclauses((HashPath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(lfirst(tmplist)) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - lfirst(tmplist), LispNil); + cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + lfirst(tmplist), LispNil); } } - if (IsA(pathnode,MergePath)) + if (IsA(pathnode, MergePath)) { - if (XfuncMode != XFUNC_OFF) - set_path_mergeclauses - ((MergePath)pathnode, - lisp_qsort(get_path_mergeclauses((MergePath)pathnode), - xfunc_clause_compare)); - for(tmplist = get_path_mergeclauses((MergePath)pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + if (XfuncMode != XFUNC_OFF) + set_path_mergeclauses + ((MergePath) pathnode, + lisp_qsort(get_path_mergeclauses((MergePath) pathnode), + xfunc_clause_compare)); + for (tmplist = get_path_mergeclauses((MergePath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(lfirst(tmplist)) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - lfirst(tmplist), LispNil); + cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + lfirst(tmplist), LispNil); } } - Assert(cost >= 0); - return(cost); + Assert(cost >= 0); + return (cost); } /* - ** Recalculate the cost of a path node. This includes the basic cost of the + ** Recalculate the cost of a path node. This includes the basic cost of the ** node, as well as the cost of its expensive functions. ** We need to do this to the parent after pulling a clause from a child into a ** parent. Thus we should only be calling this function on JoinPaths. */ -Cost xfunc_total_path_cost(JoinPath pathnode) +Cost +xfunc_total_path_cost(JoinPath pathnode) { - Cost cost = xfunc_get_path_cost((Path)pathnode); - - Assert(IsA(pathnode,JoinPath)); - if (IsA(pathnode,MergePath)) + Cost cost = xfunc_get_path_cost((Path) pathnode); + + Assert(IsA(pathnode, JoinPath)); + if (IsA(pathnode, MergePath)) { - MergePath mrgnode = (MergePath)pathnode; - cost += cost_mergesort(get_path_cost((Path)get_outerjoinpath(mrgnode)), - get_path_cost((Path)get_innerjoinpath(mrgnode)), - get_outersortkeys(mrgnode), - get_innersortkeys(mrgnode), - get_tuples(get_parent((Path)get_outerjoinpath - (mrgnode))), - get_tuples(get_parent((Path)get_innerjoinpath - (mrgnode))), - get_width(get_parent((Path)get_outerjoinpath - (mrgnode))), - get_width(get_parent((Path)get_innerjoinpath - (mrgnode)))); - Assert(cost >= 0); - return(cost); + MergePath mrgnode = (MergePath) pathnode; + + cost += cost_mergesort(get_path_cost((Path) get_outerjoinpath(mrgnode)), + get_path_cost((Path) get_innerjoinpath(mrgnode)), + get_outersortkeys(mrgnode), + get_innersortkeys(mrgnode), + get_tuples(get_parent((Path) get_outerjoinpath + (mrgnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (mrgnode))), + get_width(get_parent((Path) get_outerjoinpath + (mrgnode))), + get_width(get_parent((Path) get_innerjoinpath + (mrgnode)))); + Assert(cost >= 0); + return (cost); } - else if (IsA(pathnode,HashPath)) + else if (IsA(pathnode, HashPath)) { - HashPath hashnode = (HashPath)pathnode; - cost += cost_hashjoin(get_path_cost((Path)get_outerjoinpath(hashnode)), - get_path_cost((Path)get_innerjoinpath(hashnode)), - get_outerhashkeys(hashnode), - get_innerhashkeys(hashnode), - get_tuples(get_parent((Path)get_outerjoinpath - (hashnode))), - get_tuples(get_parent((Path)get_innerjoinpath - (hashnode))), - get_width(get_parent((Path)get_outerjoinpath - (hashnode))), - get_width(get_parent((Path)get_innerjoinpath - (hashnode)))); - Assert (cost >= 0); - return(cost); + HashPath hashnode = (HashPath) pathnode; + + cost += cost_hashjoin(get_path_cost((Path) get_outerjoinpath(hashnode)), + get_path_cost((Path) get_innerjoinpath(hashnode)), + get_outerhashkeys(hashnode), + get_innerhashkeys(hashnode), + get_tuples(get_parent((Path) get_outerjoinpath + (hashnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (hashnode))), + get_width(get_parent((Path) get_outerjoinpath + (hashnode))), + get_width(get_parent((Path) get_innerjoinpath + (hashnode)))); + Assert(cost >= 0); + return (cost); } - else /* Nested Loop Join */ + else +/* Nested Loop Join */ { - cost += cost_nestloop(get_path_cost((Path)get_outerjoinpath(pathnode)), - get_path_cost((Path)get_innerjoinpath(pathnode)), - get_tuples(get_parent((Path)get_outerjoinpath - (pathnode))), - get_tuples(get_parent((Path)get_innerjoinpath - (pathnode))), - get_pages(get_parent((Path)get_outerjoinpath - (pathnode))), - IsA(get_innerjoinpath(pathnode),IndexPath)); - Assert(cost >= 0); - return(cost); + cost += cost_nestloop(get_path_cost((Path) get_outerjoinpath(pathnode)), + get_path_cost((Path) get_innerjoinpath(pathnode)), + get_tuples(get_parent((Path) get_outerjoinpath + (pathnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (pathnode))), + get_pages(get_parent((Path) get_outerjoinpath + (pathnode))), + IsA(get_innerjoinpath(pathnode), IndexPath)); + Assert(cost >= 0); + return (cost); } } @@ -967,7 +1088,7 @@ Cost xfunc_total_path_cost(JoinPath pathnode) ** xfunc_expense_per_tuple -- ** return the expense of the join *per-tuple* of the input relation. ** The cost model here is that a join costs - ** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n + ** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n ** ** We treat the l and m terms by considering them to be like restrictions ** constrained to be right under the join. Thus the cost per inner and @@ -975,138 +1096,146 @@ Cost xfunc_total_path_cost(JoinPath pathnode) ** ** The cost per tuple of outer is k + l/referenced(inner). Cost per tuple ** of inner is k + m/referenced(outer). - ** The constants k, l, m and n depend on the join method. Measures here are + ** The constants k, l, m and n depend on the join method. Measures here are ** based on the costs in costsize.c, with fudging for HashJoin and Sorts to ** make it fit our model (the 'q' in HashJoin results in a ** card(outer)/card(inner) term, and sorting results in a log term. - + */ -Cost xfunc_expense_per_tuple(JoinPath joinnode, int whichchild) +Cost +xfunc_expense_per_tuple(JoinPath joinnode, int whichchild) { - Rel outerrel = get_parent((Path)get_outerjoinpath(joinnode)); - Rel innerrel = get_parent((Path)get_innerjoinpath(joinnode)); - Count outerwidth = get_width(outerrel); - Count outers_per_page = ceil(BLCKSZ/(outerwidth + sizeof(HeapTupleData))); - - if (IsA(joinnode,HashPath)) + Rel outerrel = get_parent((Path) get_outerjoinpath(joinnode)); + Rel innerrel = get_parent((Path) get_innerjoinpath(joinnode)); + Count outerwidth = get_width(outerrel); + Count outers_per_page = ceil(BLCKSZ / (outerwidth + sizeof(HeapTupleData))); + + if (IsA(joinnode, HashPath)) { - if (whichchild == INNER) - return((1 + _CPU_PAGE_WEIGHT_)*outers_per_page/NBuffers); - else - return(((1 + _CPU_PAGE_WEIGHT_)*outers_per_page/NBuffers) - + _CPU_PAGE_WEIGHT_ - / xfunc_card_product(get_relids(innerrel))); + if (whichchild == INNER) + return ((1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers); + else + return (((1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers) + + _CPU_PAGE_WEIGHT_ + / xfunc_card_product(get_relids(innerrel))); } - else if (IsA(joinnode,MergePath)) + else if (IsA(joinnode, MergePath)) { - /* assumes sort exists, and costs one (I/O + CPU) per tuple */ - if (whichchild == INNER) - return((2*_CPU_PAGE_WEIGHT_ + 1) - / xfunc_card_product(get_relids(outerrel))); - else - return((2*_CPU_PAGE_WEIGHT_ + 1) - / xfunc_card_product(get_relids(innerrel))); + /* assumes sort exists, and costs one (I/O + CPU) per tuple */ + if (whichchild == INNER) + return ((2 * _CPU_PAGE_WEIGHT_ + 1) + / xfunc_card_product(get_relids(outerrel))); + else + return ((2 * _CPU_PAGE_WEIGHT_ + 1) + / xfunc_card_product(get_relids(innerrel))); } - else /* nestloop */ + else +/* nestloop */ { - Assert(IsA(joinnode,JoinPath)); - return(_CPU_PAGE_WEIGHT_); + Assert(IsA(joinnode, JoinPath)); + return (_CPU_PAGE_WEIGHT_); } } /* ** xfunc_fixvars -- - ** After pulling up a clause, we must walk its expression tree, fixing Var + ** After pulling up a clause, we must walk its expression tree, fixing Var ** nodes to point to the correct varno (either INNER or OUTER, depending - ** on which child the clause was pulled from), and the right varattno in the + ** on which child the clause was pulled from), and the right varattno in the ** target list of the child's former relation. If the target list of the ** child Rel does not contain the attribute we need, we add it. */ -void xfunc_fixvars(LispValue clause, /* clause being pulled up */ - Rel rel, /* rel it's being pulled from */ - int varno) /* whether rel is INNER or OUTER of join */ +void +xfunc_fixvars(LispValue clause, /* clause being pulled up */ + Rel rel, /* rel it's being pulled from */ + int varno) /* whether rel is INNER or OUTER of join */ { - LispValue tmpclause; /* temporary variable */ - TargetEntry *tle; /* tlist member corresponding to var */ - - - if (IsA(clause,Const) || IsA(clause,Param)) return; - else if (IsA(clause,Var)) + LispValue tmpclause; /* temporary variable */ + TargetEntry *tle; /* tlist member corresponding to var */ + + + if (IsA(clause, Const) || IsA(clause, Param)) + return; + else if (IsA(clause, Var)) { - /* here's the meat */ - tle = tlistentry_member((Var)clause, get_targetlist(rel)); - if (tle == LispNil) + /* here's the meat */ + tle = tlistentry_member((Var) clause, get_targetlist(rel)); + if (tle == LispNil) { - /* - ** The attribute we need is not in the target list, - ** so we have to add it. - ** - */ - add_tl_element(rel, (Var)clause); - tle = tlistentry_member((Var)clause, get_targetlist(rel)); + + /* + * * The attribute we need is not in the target list, * so we + * have to add it. * + * + */ + add_tl_element(rel, (Var) clause); + tle = tlistentry_member((Var) clause, get_targetlist(rel)); } - set_varno(((Var)clause), varno); - set_varattno(((Var)clause), get_resno(get_resdom(get_entry(tle)))); + set_varno(((Var) clause), varno); + set_varattno(((Var) clause), get_resno(get_resdom(get_entry(tle)))); } - else if (IsA(clause,Iter)) - xfunc_fixvars(get_iterexpr((Iter)clause), rel, varno); - else if (fast_is_clause(clause)) + else if (IsA(clause, Iter)) + xfunc_fixvars(get_iterexpr((Iter) clause), rel, varno); + else if (fast_is_clause(clause)) { - xfunc_fixvars(lfirst(lnext(clause)), rel, varno); - xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno); + xfunc_fixvars(lfirst(lnext(clause)), rel, varno); + xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno); } - else if (fast_is_funcclause(clause)) - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - xfunc_fixvars(lfirst(tmpclause), rel, varno); - else if (fast_not_clause(clause)) - xfunc_fixvars(lsecond(clause), rel, varno); - else if (fast_or_clause(clause)) - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - xfunc_fixvars(lfirst(tmpclause), rel, varno); - else + else if (fast_is_funcclause(clause)) + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + xfunc_fixvars(lfirst(tmpclause), rel, varno); + else if (fast_not_clause(clause)) + xfunc_fixvars(lsecond(clause), rel, varno); + else if (fast_or_clause(clause)) + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + xfunc_fixvars(lfirst(tmpclause), rel, varno); + else { - elog(WARN, "Clause node of undetermined type"); + elog(WARN, "Clause node of undetermined type"); } } /* ** Comparison function for lisp_qsort() on a list of CInfo's. - ** arg1 and arg2 should really be of type (CInfo *). + ** arg1 and arg2 should really be of type (CInfo *). */ -int xfunc_cinfo_compare(void *arg1, void *arg2) +int +xfunc_cinfo_compare(void *arg1, void *arg2) { - CInfo info1 = *(CInfo *) arg1; - CInfo info2 = *(CInfo *) arg2; - - LispValue clause1 = (LispValue) get_clause(info1), - clause2 = (LispValue) get_clause(info2); - - return(xfunc_clause_compare((void *) &clause1, (void *) &clause2)); + CInfo info1 = *(CInfo *) arg1; + CInfo info2 = *(CInfo *) arg2; + + LispValue clause1 = (LispValue) get_clause(info1), + clause2 = (LispValue) get_clause(info2); + + return (xfunc_clause_compare((void *) &clause1, (void *) &clause2)); } /* - ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two + ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two ** clauses based on expense/(1 - selectivity) ** arg1 and arg2 are really pointers to clauses. */ -int xfunc_clause_compare(void *arg1, void *arg2) +int +xfunc_clause_compare(void *arg1, void *arg2) { - LispValue clause1 = *(LispValue *) arg1; - LispValue clause2 = *(LispValue *) arg2; - Cost rank1, /* total xfunc rank of clause1 */ - rank2; /* total xfunc rank of clause2 */ - - rank1 = xfunc_rank(clause1); - rank2 = xfunc_rank(clause2); - - if ( rank1 < rank2) - return(-1); - else if (rank1 == rank2) - return(0); - else return(1); + LispValue clause1 = *(LispValue *) arg1; + LispValue clause2 = *(LispValue *) arg2; + Cost rank1, /* total xfunc rank of clause1 */ + rank2; /* total xfunc rank of clause2 */ + + rank1 = xfunc_rank(clause1); + rank2 = xfunc_rank(clause2); + + if (rank1 < rank2) + return (-1); + else if (rank1 == rank2) + return (0); + else + return (1); } /* @@ -1115,58 +1244,62 @@ int xfunc_clause_compare(void *arg1, void *arg2) ** (this assumes the predicates have been converted to Conjunctive NF) ** Modifies the clause list! */ -void xfunc_disjunct_sort(LispValue clause_list) +void +xfunc_disjunct_sort(LispValue clause_list) { - LispValue temp; - - foreach(temp, clause_list) - if(or_clause(lfirst(temp))) - lnext(lfirst(temp)) = - lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare); + LispValue temp; + + foreach(temp, clause_list) + if (or_clause(lfirst(temp))) + lnext(lfirst(temp)) = + lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare); } /* - ** xfunc_disjunct_compare: comparison function for qsort() that compares two + ** xfunc_disjunct_compare: comparison function for qsort() that compares two ** disjuncts based on cost/selec. ** arg1 and arg2 are really pointers to disjuncts */ -int xfunc_disjunct_compare(Query* queryInfo, void *arg1, void *arg2) +int +xfunc_disjunct_compare(Query * queryInfo, void *arg1, void *arg2) { - LispValue disjunct1 = *(LispValue *) arg1; - LispValue disjunct2 = *(LispValue *) arg2; - Cost cost1, /* total cost of disjunct1 */ - cost2, /* total cost of disjunct2 */ - selec1, - selec2; - Cost rank1, rank2; - - cost1 = xfunc_expense(queryInfo, disjunct1); - cost2 = xfunc_expense(queryInfo, disjunct2); - selec1 = compute_clause_selec(queryInfo, - disjunct1, LispNil); - selec2 = compute_clause_selec(queryInfo, - disjunct2, LispNil); - - if (selec1 == 0) - rank1 = MAXFLOAT; - else if (cost1 == 0) - rank1 = 0; - else - rank1 = cost1/selec1; - - if (selec2 == 0) - rank2 = MAXFLOAT; - else if (cost2 == 0) - rank2 = 0; - else - rank2 = cost2/selec2; - - if ( rank1 < rank2) - return(-1); - else if (rank1 == rank2) - return(0); - else return(1); + LispValue disjunct1 = *(LispValue *) arg1; + LispValue disjunct2 = *(LispValue *) arg2; + Cost cost1, /* total cost of disjunct1 */ + cost2, /* total cost of disjunct2 */ + selec1, + selec2; + Cost rank1, + rank2; + + cost1 = xfunc_expense(queryInfo, disjunct1); + cost2 = xfunc_expense(queryInfo, disjunct2); + selec1 = compute_clause_selec(queryInfo, + disjunct1, LispNil); + selec2 = compute_clause_selec(queryInfo, + disjunct2, LispNil); + + if (selec1 == 0) + rank1 = MAXFLOAT; + else if (cost1 == 0) + rank1 = 0; + else + rank1 = cost1 / selec1; + + if (selec2 == 0) + rank2 = MAXFLOAT; + else if (cost2 == 0) + rank2 = 0; + else + rank2 = cost2 / selec2; + + if (rank1 < rank2) + return (-1); + else if (rank1 == rank2) + return (0); + else + return (1); } /* ------------------------ UTILITY FUNCTIONS ------------------------------- */ @@ -1174,182 +1307,197 @@ int xfunc_disjunct_compare(Query* queryInfo, void *arg1, void *arg2) ** xfunc_func_width -- ** Given a function OID and operands, find the width of the return value. */ -int xfunc_func_width(RegProcedure funcid, LispValue args) +int +xfunc_func_width(RegProcedure funcid, LispValue args) { - Relation rd; /* Relation Descriptor */ - HeapTuple tupl; /* structure to hold a cached tuple */ - Form_pg_proc proc; /* structure to hold the pg_proc tuple */ - TypeTupleForm type; /* structure to hold the pg_type tuple */ - LispValue tmpclause; - int retval; - - /* lookup function and find its return type */ - Assert(RegProcedureIsValid(funcid)); - tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for procedure %d", funcid); - proc = (Form_pg_proc) GETSTRUCT(tupl); - - /* if function returns a tuple, get the width of that */ - if (typeid_get_relid(proc->prorettype)) + Relation rd; /* Relation Descriptor */ + HeapTuple tupl; /* structure to hold a cached tuple */ + Form_pg_proc proc; /* structure to hold the pg_proc tuple */ + TypeTupleForm type; /* structure to hold the pg_type tuple */ + LispValue tmpclause; + int retval; + + /* lookup function and find its return type */ + Assert(RegProcedureIsValid(funcid)); + tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for procedure %d", funcid); + proc = (Form_pg_proc) GETSTRUCT(tupl); + + /* if function returns a tuple, get the width of that */ + if (typeid_get_relid(proc->prorettype)) { - rd = heap_open(typeid_get_relid(proc->prorettype)); - retval = xfunc_tuple_width(rd); - heap_close(rd); - goto exit; + rd = heap_open(typeid_get_relid(proc->prorettype)); + retval = xfunc_tuple_width(rd); + heap_close(rd); + goto exit; } - else /* function returns a base type */ + else +/* function returns a base type */ { - tupl = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(proc->prorettype), - 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for type %d", proc->prorettype); - type = (TypeTupleForm) GETSTRUCT(tupl); - /* if the type length is known, return that */ - if (type->typlen != -1) + tupl = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(proc->prorettype), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for type %d", proc->prorettype); + type = (TypeTupleForm) GETSTRUCT(tupl); + /* if the type length is known, return that */ + if (type->typlen != -1) { - retval = type->typlen; - goto exit; + retval = type->typlen; + goto exit; } - else /* estimate the return size */ + else +/* estimate the return size */ { - /* find width of the function's arguments */ - for (tmpclause = args; tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval += xfunc_width(lfirst(tmpclause)); - /* multiply by outin_ratio */ - retval = (int)(proc->prooutin_ratio/100.0 * retval); - goto exit; + /* find width of the function's arguments */ + for (tmpclause = args; tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval += xfunc_width(lfirst(tmpclause)); + /* multiply by outin_ratio */ + retval = (int) (proc->prooutin_ratio / 100.0 * retval); + goto exit; } } - exit: - return(retval); +exit: + return (retval); } /* ** xfunc_tuple_width -- - ** Return the sum of the lengths of all the attributes of a given relation + ** Return the sum of the lengths of all the attributes of a given relation */ -int xfunc_tuple_width(Relation rd) +int +xfunc_tuple_width(Relation rd) { - int i; - int retval = 0; - TupleDesc tdesc = RelationGetTupleDescriptor(rd); - - for (i = 0; i < tdesc->natts; i++) + int i; + int retval = 0; + TupleDesc tdesc = RelationGetTupleDescriptor(rd); + + for (i = 0; i < tdesc->natts; i++) { - if (tdesc->attrs[i]->attlen != -1) - retval += tdesc->attrs[i]->attlen; - else retval += VARLEN_DEFAULT; + if (tdesc->attrs[i]->attlen != -1) + retval += tdesc->attrs[i]->attlen; + else + retval += VARLEN_DEFAULT; } - - return(retval); + + return (retval); } /* ** xfunc_num_join_clauses -- ** Find the number of join clauses associated with this join path */ -int xfunc_num_join_clauses(JoinPath path) +int +xfunc_num_join_clauses(JoinPath path) { - int num = length(get_pathclauseinfo(path)); - if (IsA(path,MergePath)) - return(num + length(get_path_mergeclauses((MergePath)path))); - else if (IsA(path,HashPath)) - return(num + length(get_path_hashclauses((HashPath)path))); - else return(num); + int num = length(get_pathclauseinfo(path)); + + if (IsA(path, MergePath)) + return (num + length(get_path_mergeclauses((MergePath) path))); + else if (IsA(path, HashPath)) + return (num + length(get_path_hashclauses((HashPath) path))); + else + return (num); } /* ** xfunc_LispRemove -- ** Just like LispRemove, but it whines if the item to be removed ain't there */ -LispValue xfunc_LispRemove(LispValue foo, List bar) +LispValue +xfunc_LispRemove(LispValue foo, List bar) { - LispValue temp = LispNil; - LispValue result = LispNil; - int sanity = false; - - for (temp = bar; !null(temp); temp = lnext(temp)) - if (! equal((Node)(foo),(Node)(lfirst(temp))) ) - { - result = lappend(result,lfirst(temp)); - } - else sanity = true; /* found a matching item to remove! */ - - if (!sanity) - elog(WARN, "xfunc_LispRemove: didn't find a match!"); - - return(result); + LispValue temp = LispNil; + LispValue result = LispNil; + int sanity = false; + + for (temp = bar; !null(temp); temp = lnext(temp)) + if (!equal((Node) (foo), (Node) (lfirst(temp)))) + { + result = lappend(result, lfirst(temp)); + } + else + sanity = true; /* found a matching item to remove! */ + + if (!sanity) + elog(WARN, "xfunc_LispRemove: didn't find a match!"); + + return (result); } #define Node_Copy(a, b, c, d) \ - if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) { \ - return false; \ - } + if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) { \ + return false; \ + } /* ** xfunc_copyrel -- ** Just like _copyRel, but doesn't copy the paths */ -bool xfunc_copyrel(Rel from, Rel *to) +bool +xfunc_copyrel(Rel from, Rel * to) { - Rel newnode; - Pointer (*alloc)() = palloc; - - /* COPY_CHECKARGS() */ - if (to == NULL) - { - return false; - } - - /* COPY_CHECKNULL() */ - if (from == NULL) + Rel newnode; + + Pointer(*alloc) () = palloc; + + /* COPY_CHECKARGS() */ + if (to == NULL) + { + return false; + } + + /* COPY_CHECKNULL() */ + if (from == NULL) { - (*to) = NULL; - return true; - } - - /* COPY_NEW(c) */ - newnode = (Rel)(*alloc)(classSize(Rel)); - if (newnode == NULL) - { - return false; - } - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyNodeFields((Node)from, (Node)newnode, alloc); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, alloc, relids); - - newnode->indexed = from->indexed; - newnode->pages = from->pages; - newnode->tuples = from->tuples; - newnode->size = from->size; - newnode->width = from->width; - - Node_Copy(from, newnode, alloc, targetlist); - /* No!!!! Node_Copy(from, newnode, alloc, pathlist); - Node_Copy(from, newnode, alloc, unorderedpath); - Node_Copy(from, newnode, alloc, cheapestpath); */ -#if 0 /* can't use Node_copy now. 2/95 -ay */ - Node_Copy(from, newnode, alloc, classlist); - Node_Copy(from, newnode, alloc, indexkeys); - Node_Copy(from, newnode, alloc, ordering); + (*to) = NULL; + return true; + } + + /* COPY_NEW(c) */ + newnode = (Rel) (*alloc) (classSize(Rel)); + if (newnode == NULL) + { + return false; + } + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyNodeFields((Node) from, (Node) newnode, alloc); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, alloc, relids); + + newnode->indexed = from->indexed; + newnode->pages = from->pages; + newnode->tuples = from->tuples; + newnode->size = from->size; + newnode->width = from->width; + + Node_Copy(from, newnode, alloc, targetlist); + + /* + * No!!!! Node_Copy(from, newnode, alloc, pathlist); + * Node_Copy(from, newnode, alloc, unorderedpath); Node_Copy(from, + * newnode, alloc, cheapestpath); + */ +#if 0 /* can't use Node_copy now. 2/95 -ay */ + Node_Copy(from, newnode, alloc, classlist); + Node_Copy(from, newnode, alloc, indexkeys); + Node_Copy(from, newnode, alloc, ordering); #endif - Node_Copy(from, newnode, alloc, clauseinfo); - Node_Copy(from, newnode, alloc, joininfo); - Node_Copy(from, newnode, alloc, innerjoin); - Node_Copy(from, newnode, alloc, superrels); - - (*to) = newnode; - return true; + Node_Copy(from, newnode, alloc, clauseinfo); + Node_Copy(from, newnode, alloc, joininfo); + Node_Copy(from, newnode, alloc, innerjoin); + Node_Copy(from, newnode, alloc, superrels); + + (*to) = newnode; + return true; } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 7637d15f200..bdceec18be3 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * createplan.c-- - * Routines to create the desired plan for processing a query + * Routines to create the desired plan for processing a query * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.11 1997/04/24 15:59:58 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.12 1997/09/07 04:43:57 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -41,766 +41,836 @@ #include "optimizer/internal.h" -#define TEMP_SORT 1 +#define TEMP_SORT 1 #define TEMP_MATERIAL 2 -static List *switch_outer(List *clauses); -static Scan *create_scan_node(Path *best_path, List *tlist); -static Join *create_join_node(JoinPath *best_path, List *tlist); -static SeqScan *create_seqscan_node(Path *best_path, List *tlist, - List *scan_clauses); -static IndexScan *create_indexscan_node(IndexPath *best_path, List *tlist, - List *scan_clauses); -static NestLoop *create_nestloop_node(JoinPath *best_path, List *tlist, - List *clauses, Plan *outer_node, List *outer_tlist, - Plan *inner_node, List *inner_tlist); -static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist, - List *clauses, Plan *outer_node, List *outer_tlist, - Plan *inner_node, List *inner_tlist); -static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist, - List *clauses, Plan *outer_node, List *outer_tlist, - Plan *inner_node, List *inner_tlist); -static Node *fix_indxqual_references(Node *clause, Path *index_path); -static Temp *make_temp(List *tlist, List *keys, Oid *operators, - Plan *plan_node, int temptype); -static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, - List *indxid, List *indxqual); -static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree, - Plan *righttree); -static HashJoin *make_hashjoin(List *tlist, List *qpqual, - List *hashclauses, Plan *lefttree, Plan *righttree); -static Hash *make_hash(List *tlist, Var *hashkey, Plan *lefttree); -static MergeJoin *make_mergesort(List * tlist, List *qpqual, - List *mergeclauses, Oid opcode, Oid *rightorder, - Oid *leftorder, Plan *righttree, Plan *lefttree); -static Material *make_material(List *tlist, Oid tempid, Plan *lefttree, - int keycount); - -/* +static List *switch_outer(List * clauses); +static Scan *create_scan_node(Path * best_path, List * tlist); +static Join *create_join_node(JoinPath * best_path, List * tlist); +static SeqScan * +create_seqscan_node(Path * best_path, List * tlist, + List * scan_clauses); +static IndexScan * +create_indexscan_node(IndexPath * best_path, List * tlist, + List * scan_clauses); +static NestLoop * +create_nestloop_node(JoinPath * best_path, List * tlist, + List * clauses, Plan * outer_node, List * outer_tlist, + Plan * inner_node, List * inner_tlist); +static MergeJoin * +create_mergejoin_node(MergePath * best_path, List * tlist, + List * clauses, Plan * outer_node, List * outer_tlist, + Plan * inner_node, List * inner_tlist); +static HashJoin * +create_hashjoin_node(HashPath * best_path, List * tlist, + List * clauses, Plan * outer_node, List * outer_tlist, + Plan * inner_node, List * inner_tlist); +static Node *fix_indxqual_references(Node * clause, Path * index_path); +static Temp * +make_temp(List * tlist, List * keys, Oid * operators, + Plan * plan_node, int temptype); +static IndexScan * +make_indexscan(List * qptlist, List * qpqual, Index scanrelid, + List * indxid, List * indxqual); +static NestLoop * +make_nestloop(List * qptlist, List * qpqual, Plan * lefttree, + Plan * righttree); +static HashJoin * +make_hashjoin(List * tlist, List * qpqual, + List * hashclauses, Plan * lefttree, Plan * righttree); +static Hash *make_hash(List * tlist, Var * hashkey, Plan * lefttree); +static MergeJoin * +make_mergesort(List * tlist, List * qpqual, + List * mergeclauses, Oid opcode, Oid * rightorder, + Oid * leftorder, Plan * righttree, Plan * lefttree); +static Material * +make_material(List * tlist, Oid tempid, Plan * lefttree, + int keycount); + +/* * create_plan-- - * Creates the access plan for a query by tracing backwards through the - * desired chain of pathnodes, starting at the node 'best-path'. For - * every pathnode found: - * (1) Create a corresponding plan node containing appropriate id, - * target list, and qualification information. - * (2) Modify ALL clauses so that attributes are referenced using - * relative values. - * (3) Target lists are not modified, but will be in another routine. - * - * best-path is the best access path + * Creates the access plan for a query by tracing backwards through the + * desired chain of pathnodes, starting at the node 'best-path'. For + * every pathnode found: + * (1) Create a corresponding plan node containing appropriate id, + * target list, and qualification information. + * (2) Modify ALL clauses so that attributes are referenced using + * relative values. + * (3) Target lists are not modified, but will be in another routine. + * + * best-path is the best access path * - * Returns the optimal(?) access plan. + * Returns the optimal(?) access plan. */ -Plan * -create_plan(Path *best_path) +Plan * +create_plan(Path * best_path) { - List *tlist; - Plan *plan_node = (Plan*)NULL; - Rel *parent_rel; - int size; - int width; - int pages; - int tuples; - - parent_rel = best_path->parent; - tlist = get_actual_tlist(parent_rel->targetlist); - size = parent_rel->size; - width = parent_rel->width; - pages = parent_rel->pages; - tuples = parent_rel->tuples; - - switch(best_path->pathtype) { - case T_IndexScan : - case T_SeqScan : - plan_node = (Plan*)create_scan_node(best_path, tlist); - break; - case T_HashJoin : - case T_MergeJoin : - case T_NestLoop: - plan_node = (Plan*)create_join_node((JoinPath*)best_path, tlist); - break; - default: - /* do nothing */ - break; - } - - plan_node->plan_size = size; - plan_node->plan_width = width; - if (pages == 0) pages = 1; - plan_node->plan_tupperpage = tuples/pages; - -#if 0 /* fix xfunc */ - /* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */ - if (XfuncMode != XFUNC_OFF) + List *tlist; + Plan *plan_node = (Plan *) NULL; + Rel *parent_rel; + int size; + int width; + int pages; + int tuples; + + parent_rel = best_path->parent; + tlist = get_actual_tlist(parent_rel->targetlist); + size = parent_rel->size; + width = parent_rel->width; + pages = parent_rel->pages; + tuples = parent_rel->tuples; + + switch (best_path->pathtype) { - set_qpqual((Plan) plan_node, - lisp_qsort( get_qpqual((Plan) plan_node), - xfunc_clause_compare)); - if (XfuncMode != XFUNC_NOR) - /* sort the disjuncts within each clause by cost -- JMH 3/4/92 */ - xfunc_disjunct_sort(plan_node->qpqual); + case T_IndexScan: + case T_SeqScan: + plan_node = (Plan *) create_scan_node(best_path, tlist); + break; + case T_HashJoin: + case T_MergeJoin: + case T_NestLoop: + plan_node = (Plan *) create_join_node((JoinPath *) best_path, tlist); + break; + default: + /* do nothing */ + break; + } + + plan_node->plan_size = size; + plan_node->plan_width = width; + if (pages == 0) + pages = 1; + plan_node->plan_tupperpage = tuples / pages; + +#if 0 /* fix xfunc */ + /* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */ + if (XfuncMode != XFUNC_OFF) + { + set_qpqual((Plan) plan_node, + lisp_qsort(get_qpqual((Plan) plan_node), + xfunc_clause_compare)); + if (XfuncMode != XFUNC_NOR) + /* sort the disjuncts within each clause by cost -- JMH 3/4/92 */ + xfunc_disjunct_sort(plan_node->qpqual); } #endif - - return(plan_node); + + return (plan_node); } -/* +/* * create_scan_node-- - * Create a scan path for the parent relation of 'best-path'. - * - * tlist is the targetlist for the base relation scanned by 'best-path' - * - * Returns the scan node. + * Create a scan path for the parent relation of 'best-path'. + * + * tlist is the targetlist for the base relation scanned by 'best-path' + * + * Returns the scan node. */ -static Scan * -create_scan_node(Path *best_path, List *tlist) +static Scan * +create_scan_node(Path * best_path, List * tlist) { - Scan *node = NULL ; - List *scan_clauses; - - /* - * Extract the relevant clauses from the parent relation and replace the - * operator OIDs with the corresponding regproc ids. - * - * now that local predicate clauses are copied into paths in - * find_rel_paths() and then (possibly) pulled up in xfunc_trypullup(), - * we get the relevant clauses from the path itself, not its parent - * relation. --- JMH, 6/15/92 - */ - scan_clauses = fix_opids(get_actual_clauses(best_path->locclauseinfo)); - - switch(best_path->pathtype) { - case T_SeqScan : - node = (Scan*)create_seqscan_node(best_path, tlist, scan_clauses); - break; - - case T_IndexScan: - node = (Scan*)create_indexscan_node((IndexPath*)best_path, - tlist, - scan_clauses); - break; - - default : - elog(WARN, "create_scan_node: unknown node type", - best_path->pathtype); - break; - } - - return node; + Scan *node = NULL; + List *scan_clauses; + + /* + * Extract the relevant clauses from the parent relation and replace + * the operator OIDs with the corresponding regproc ids. + * + * now that local predicate clauses are copied into paths in + * find_rel_paths() and then (possibly) pulled up in + * xfunc_trypullup(), we get the relevant clauses from the path + * itself, not its parent relation. --- JMH, 6/15/92 + */ + scan_clauses = fix_opids(get_actual_clauses(best_path->locclauseinfo)); + + switch (best_path->pathtype) + { + case T_SeqScan: + node = (Scan *) create_seqscan_node(best_path, tlist, scan_clauses); + break; + + case T_IndexScan: + node = (Scan *) create_indexscan_node((IndexPath *) best_path, + tlist, + scan_clauses); + break; + + default: + elog(WARN, "create_scan_node: unknown node type", + best_path->pathtype); + break; + } + + return node; } -/* +/* * create_join_node -- - * Create a join path for 'best-path' and(recursively) paths for its - * inner and outer paths. - * - * 'tlist' is the targetlist for the join relation corresponding to - * 'best-path' - * - * Returns the join node. + * Create a join path for 'best-path' and(recursively) paths for its + * inner and outer paths. + * + * 'tlist' is the targetlist for the join relation corresponding to + * 'best-path' + * + * Returns the join node. */ -static Join * -create_join_node(JoinPath *best_path, List *tlist) +static Join * +create_join_node(JoinPath * best_path, List * tlist) { - Plan *outer_node; - List *outer_tlist; - Plan *inner_node; - List *inner_tlist; - List *clauses; - Join *retval = NULL; - - outer_node = create_plan((Path*)best_path->outerjoinpath); - outer_tlist = outer_node->targetlist; - - inner_node = create_plan((Path*)best_path->innerjoinpath); - inner_tlist = inner_node->targetlist; - - clauses = get_actual_clauses(best_path->pathclauseinfo); - - switch(best_path->path.pathtype) { - case T_MergeJoin: - retval = (Join*)create_mergejoin_node((MergePath*)best_path, - tlist, - clauses, - outer_node, - outer_tlist, - inner_node, - inner_tlist); - break; - case T_HashJoin: - retval = (Join*)create_hashjoin_node((HashPath*)best_path, - tlist, - clauses, - outer_node, - outer_tlist, - inner_node, - inner_tlist); - break; - case T_NestLoop: - retval = (Join*)create_nestloop_node((JoinPath*)best_path, - tlist, - clauses, - outer_node, - outer_tlist, - inner_node, - inner_tlist); - break; - default: - /* do nothing */ - elog(WARN, "create_join_node: unknown node type", - best_path->path.pathtype); - } + Plan *outer_node; + List *outer_tlist; + Plan *inner_node; + List *inner_tlist; + List *clauses; + Join *retval = NULL; + + outer_node = create_plan((Path *) best_path->outerjoinpath); + outer_tlist = outer_node->targetlist; + + inner_node = create_plan((Path *) best_path->innerjoinpath); + inner_tlist = inner_node->targetlist; + + clauses = get_actual_clauses(best_path->pathclauseinfo); + + switch (best_path->path.pathtype) + { + case T_MergeJoin: + retval = (Join *) create_mergejoin_node((MergePath *) best_path, + tlist, + clauses, + outer_node, + outer_tlist, + inner_node, + inner_tlist); + break; + case T_HashJoin: + retval = (Join *) create_hashjoin_node((HashPath *) best_path, + tlist, + clauses, + outer_node, + outer_tlist, + inner_node, + inner_tlist); + break; + case T_NestLoop: + retval = (Join *) create_nestloop_node((JoinPath *) best_path, + tlist, + clauses, + outer_node, + outer_tlist, + inner_node, + inner_tlist); + break; + default: + /* do nothing */ + elog(WARN, "create_join_node: unknown node type", + best_path->path.pathtype); + } #if 0 - /* - ** Expensive function pullups may have pulled local predicates - ** into this path node. Put them in the qpqual of the plan node. - ** -- JMH, 6/15/92 - */ - if (get_locclauseinfo(best_path) != NIL) - set_qpqual((Plan)retval, - nconc(get_qpqual((Plan) retval), - fix_opids(get_actual_clauses - (get_locclauseinfo(best_path))))); + + /* + * * Expensive function pullups may have pulled local predicates * + * into this path node. Put them in the qpqual of the plan node. * + * -- JMH, 6/15/92 + */ + if (get_locclauseinfo(best_path) != NIL) + set_qpqual((Plan) retval, + nconc(get_qpqual((Plan) retval), + fix_opids(get_actual_clauses + (get_locclauseinfo(best_path))))); #endif - return(retval); + return (retval); } /***************************************************************************** * - * BASE-RELATION SCAN METHODS + * BASE-RELATION SCAN METHODS * *****************************************************************************/ - -/* + +/* * create_seqscan_node-- - * Returns a seqscan node for the base relation scanned by 'best-path' - * with restriction clauses 'scan-clauses' and targetlist 'tlist'. + * Returns a seqscan node for the base relation scanned by 'best-path' + * with restriction clauses 'scan-clauses' and targetlist 'tlist'. */ static SeqScan * -create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) +create_seqscan_node(Path * best_path, List * tlist, List * scan_clauses) { - SeqScan *scan_node = (SeqScan*)NULL; - Index scan_relid = -1; - List *temp; - - temp = best_path->parent->relids; - if(temp == NULL) - elog(WARN,"scanrelid is empty"); - else - scan_relid = (Index)lfirsti(temp); /* ??? who takes care of lnext? - ay */ - scan_node = make_seqscan(tlist, - scan_clauses, - scan_relid, - (Plan*)NULL); - - scan_node->plan.cost = best_path->path_cost; - - return(scan_node); + SeqScan *scan_node = (SeqScan *) NULL; + Index scan_relid = -1; + List *temp; + + temp = best_path->parent->relids; + if (temp == NULL) + elog(WARN, "scanrelid is empty"); + else + scan_relid = (Index) lfirsti(temp); /* ??? who takes care of + * lnext? - ay */ + scan_node = make_seqscan(tlist, + scan_clauses, + scan_relid, + (Plan *) NULL); + + scan_node->plan.cost = best_path->path_cost; + + return (scan_node); } -/* +/* * create_indexscan_node-- - * Returns a indexscan node for the base relation scanned by 'best-path' - * with restriction clauses 'scan-clauses' and targetlist 'tlist'. + * Returns a indexscan node for the base relation scanned by 'best-path' + * with restriction clauses 'scan-clauses' and targetlist 'tlist'. */ static IndexScan * -create_indexscan_node(IndexPath *best_path, - List *tlist, - List *scan_clauses) +create_indexscan_node(IndexPath * best_path, + List * tlist, + List * scan_clauses) { - /* - * Extract the(first if conjunct, only if disjunct) clause from the - * clauseinfo list. - */ - Expr *index_clause = (Expr*)NULL; - List *indxqual = NIL; - List *qpqual = NIL; - List *fixed_indxqual = NIL; - List *ixid; - IndexScan *scan_node = (IndexScan*)NULL; - bool lossy = FALSE; - HeapTuple indexTuple; - IndexTupleForm index; - - /* - * If an 'or' clause is to be used with this index, the indxqual - * field will contain a list of the 'or' clause arguments, e.g., the - * clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the - * indxqual will simply contain one conjunctive qualification: ((a)). - */ - if (best_path->indexqual != NULL) - /* added call to fix_opids, JMH 6/23/92 */ - index_clause = (Expr*) - lfirst(fix_opids(get_actual_clauses(best_path->indexqual))); - - if (or_clause((Node*)index_clause)) { - List *temp = NIL; - - foreach(temp, index_clause->args) - indxqual = lappend(indxqual, lcons(lfirst(temp), NIL)); - } else { - indxqual = lcons(get_actual_clauses(best_path->indexqual), - NIL); - } - - /* check and see if any indices are lossy */ - foreach (ixid, best_path->indexid) { - indexTuple = SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(lfirsti(ixid)), - 0,0,0); - if (!HeapTupleIsValid(indexTuple)) - elog(WARN, "create_plan: index %d not found", - lfirsti(ixid)); - index = (IndexTupleForm)GETSTRUCT(indexTuple); - if (index->indislossy) - lossy = TRUE; - } - - - /* - * The qpqual field contains all restrictions not automatically handled - * by the index. Note that for non-lossy indices, the predicates - * in the indxqual are handled by the index, while for lossy indices - * the indxqual predicates need to be double-checked after the - * index fetches the best-guess tuples. - */ - if(or_clause((Node*)index_clause)) { - qpqual = set_difference(scan_clauses, - lcons(index_clause,NIL)); - - if (lossy) - qpqual = nconc(qpqual, - lcons((List *)copyObject(index_clause),NIL)); - } - else { - qpqual = set_difference(scan_clauses, lfirst(indxqual)); - if (lossy) - qpqual = nconc(qpqual, - (List *)copyObject(lfirst(indxqual))); - } - - fixed_indxqual = - (List*)fix_indxqual_references((Node*)indxqual,(Path*)best_path); - - scan_node = - make_indexscan(tlist, - qpqual, - lfirsti(best_path->path.parent->relids), - best_path->indexid, - fixed_indxqual); - - scan_node->scan.plan.cost = best_path->path.path_cost; - - return(scan_node); + + /* + * Extract the(first if conjunct, only if disjunct) clause from the + * clauseinfo list. + */ + Expr *index_clause = (Expr *) NULL; + List *indxqual = NIL; + List *qpqual = NIL; + List *fixed_indxqual = NIL; + List *ixid; + IndexScan *scan_node = (IndexScan *) NULL; + bool lossy = FALSE; + HeapTuple indexTuple; + IndexTupleForm index; + + /* + * If an 'or' clause is to be used with this index, the indxqual field + * will contain a list of the 'or' clause arguments, e.g., the + * clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the + * indxqual will simply contain one conjunctive qualification: ((a)). + */ + if (best_path->indexqual != NULL) + /* added call to fix_opids, JMH 6/23/92 */ + index_clause = (Expr *) + lfirst(fix_opids(get_actual_clauses(best_path->indexqual))); + + if (or_clause((Node *) index_clause)) + { + List *temp = NIL; + + foreach(temp, index_clause->args) + indxqual = lappend(indxqual, lcons(lfirst(temp), NIL)); + } + else + { + indxqual = lcons(get_actual_clauses(best_path->indexqual), + NIL); + } + + /* check and see if any indices are lossy */ + foreach(ixid, best_path->indexid) + { + indexTuple = SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(lfirsti(ixid)), + 0, 0, 0); + if (!HeapTupleIsValid(indexTuple)) + elog(WARN, "create_plan: index %d not found", + lfirsti(ixid)); + index = (IndexTupleForm) GETSTRUCT(indexTuple); + if (index->indislossy) + lossy = TRUE; + } + + + /* + * The qpqual field contains all restrictions not automatically + * handled by the index. Note that for non-lossy indices, the + * predicates in the indxqual are handled by the index, while for + * lossy indices the indxqual predicates need to be double-checked + * after the index fetches the best-guess tuples. + */ + if (or_clause((Node *) index_clause)) + { + qpqual = set_difference(scan_clauses, + lcons(index_clause, NIL)); + + if (lossy) + qpqual = nconc(qpqual, + lcons((List *) copyObject(index_clause), NIL)); + } + else + { + qpqual = set_difference(scan_clauses, lfirst(indxqual)); + if (lossy) + qpqual = nconc(qpqual, + (List *) copyObject(lfirst(indxqual))); + } + + fixed_indxqual = + (List *) fix_indxqual_references((Node *) indxqual, (Path *) best_path); + + scan_node = + make_indexscan(tlist, + qpqual, + lfirsti(best_path->path.parent->relids), + best_path->indexid, + fixed_indxqual); + + scan_node->scan.plan.cost = best_path->path.path_cost; + + return (scan_node); } /***************************************************************************** * - * JOIN METHODS + * JOIN METHODS * *****************************************************************************/ static NestLoop * -create_nestloop_node(JoinPath *best_path, - List *tlist, - List *clauses, - Plan *outer_node, - List *outer_tlist, - Plan *inner_node, - List *inner_tlist) +create_nestloop_node(JoinPath * best_path, + List * tlist, + List * clauses, + Plan * outer_node, + List * outer_tlist, + Plan * inner_node, + List * inner_tlist) { - NestLoop *join_node = (NestLoop*)NULL; + NestLoop *join_node = (NestLoop *) NULL; - if (IsA(inner_node,IndexScan)) { - /* An index is being used to reduce the number of tuples scanned in - * the inner relation. There will never be more than one index used - * in the inner scan path, so we need only consider the first set of - * qualifications in indxqual. - * - * But there may be more than one clauses in this "first set" - * in the case of multi-column indices. - vadim 03/18/97 - */ + if (IsA(inner_node, IndexScan)) + { - List *inner_indxqual = lfirst(((IndexScan*)inner_node)->indxqual); - List *inner_qual; - bool found = false; + /* + * An index is being used to reduce the number of tuples scanned + * in the inner relation. There will never be more than one index + * used in the inner scan path, so we need only consider the first + * set of qualifications in indxqual. + * + * But there may be more than one clauses in this "first set" in the + * case of multi-column indices. - vadim 03/18/97 + */ + + List *inner_indxqual = lfirst(((IndexScan *) inner_node)->indxqual); + List *inner_qual; + bool found = false; + + foreach(inner_qual, inner_indxqual) + { + if (!qual_clause_p((Node *) lfirst(inner_qual))) + { + found = true; + break; + } + } - foreach (inner_qual, inner_indxqual) - { - if ( !qual_clause_p ((Node*)lfirst(inner_qual)) ) - { - found = true; - break; - } + /* + * If we have in fact found a join index qualification, remove + * these index clauses from the nestloop's join clauses and reset + * the inner(index) scan's qualification so that the var nodes + * refer to the proper outer join relation attributes. + * + * XXX Re-moving index clauses doesn't work properly: 1. + * fix_indxqual_references may change varattno-s in + * inner_indxqual; 2. clauses may be commuted I havn't time to fix + * it at the moment. - vadim 04/24/97 + */ + if (found) + { + List *new_inner_qual = NIL; + + clauses = set_difference(clauses, inner_indxqual); /* XXX */ + new_inner_qual = + index_outerjoin_references(inner_indxqual, + outer_node->targetlist, + ((Scan *) inner_node)->scanrelid); + ((IndexScan *) inner_node)->indxqual = + lcons(new_inner_qual, NIL); + } } - - /* If we have in fact found a join index qualification, remove these - * index clauses from the nestloop's join clauses and reset the - * inner(index) scan's qualification so that the var nodes refer to - * the proper outer join relation attributes. - * - * XXX Re-moving index clauses doesn't work properly: - * 1. fix_indxqual_references may change varattno-s in - * inner_indxqual; - * 2. clauses may be commuted - * I havn't time to fix it at the moment. - vadim 04/24/97 - */ - if ( found ) + else if (IsA_Join(inner_node)) { - List *new_inner_qual = NIL; - - clauses = set_difference(clauses,inner_indxqual); /* XXX */ - new_inner_qual = - index_outerjoin_references(inner_indxqual, - outer_node->targetlist, - ((Scan*)inner_node)->scanrelid); - ((IndexScan*)inner_node)->indxqual = - lcons(new_inner_qual,NIL); + inner_node = (Plan *) make_temp(inner_tlist, + NIL, + NULL, + inner_node, + TEMP_MATERIAL); } - }else if (IsA_Join(inner_node)) { - inner_node = (Plan*)make_temp(inner_tlist, - NIL, - NULL, - inner_node, - TEMP_MATERIAL); - } - - join_node = make_nestloop(tlist, - join_references(clauses, - outer_tlist, - inner_tlist), - outer_node, - inner_node); - - join_node->join.cost = best_path->path.path_cost; - - return(join_node); + + join_node = make_nestloop(tlist, + join_references(clauses, + outer_tlist, + inner_tlist), + outer_node, + inner_node); + + join_node->join.cost = best_path->path.path_cost; + + return (join_node); } static MergeJoin * -create_mergejoin_node(MergePath *best_path, - List *tlist, - List *clauses, - Plan *outer_node, - List *outer_tlist, - Plan *inner_node, - List *inner_tlist) +create_mergejoin_node(MergePath * best_path, + List * tlist, + List * clauses, + Plan * outer_node, + List * outer_tlist, + Plan * inner_node, + List * inner_tlist) { - List *qpqual, *mergeclauses; - RegProcedure opcode; - Oid *outer_order, *inner_order; - MergeJoin *join_node; - - - /* Separate the mergeclauses from the other join qualification - * clauses and set those clauses to contain references to lower - * attributes. - */ - qpqual = join_references(set_difference(clauses, - best_path->path_mergeclauses), - outer_tlist, - inner_tlist); - - /* Now set the references in the mergeclauses and rearrange them so - * that the outer variable is always on the left. - */ - mergeclauses = switch_outer(join_references(best_path->path_mergeclauses, - outer_tlist, - inner_tlist)); - - opcode = - get_opcode((best_path->jpath.path.p_ordering.ord.merge)->join_operator); - - outer_order = (Oid *)palloc(sizeof(Oid)*2); - outer_order[0] = - (best_path->jpath.path.p_ordering.ord.merge)->left_operator; - outer_order[1] = 0; - - inner_order = (Oid *)palloc(sizeof(Oid)*2); - inner_order[0] = - (best_path->jpath.path.p_ordering.ord.merge)->right_operator; - inner_order[1] = 0; - - /* Create explicit sort paths for the outer and inner join paths if - * necessary. The sort cost was already accounted for in the path. - */ - if (best_path->outersortkeys) { - Temp *sorted_outer_node = make_temp(outer_tlist, - best_path->outersortkeys, - outer_order, - outer_node, - TEMP_SORT); - sorted_outer_node->plan.cost = outer_node->cost; - outer_node = (Plan*)sorted_outer_node; - } - - if (best_path->innersortkeys) { - Temp *sorted_inner_node = make_temp(inner_tlist, - best_path->innersortkeys, - inner_order, - inner_node, - TEMP_SORT); - sorted_inner_node->plan.cost = outer_node->cost; - inner_node = (Plan*)sorted_inner_node; - } - - join_node = make_mergesort(tlist, - qpqual, - mergeclauses, - opcode, - inner_order, - outer_order, - inner_node, - outer_node); - - join_node->join.cost = best_path->jpath.path.path_cost; - - return(join_node); + List *qpqual, + *mergeclauses; + RegProcedure opcode; + Oid *outer_order, + *inner_order; + MergeJoin *join_node; + + + /* + * Separate the mergeclauses from the other join qualification clauses + * and set those clauses to contain references to lower attributes. + */ + qpqual = join_references(set_difference(clauses, + best_path->path_mergeclauses), + outer_tlist, + inner_tlist); + + /* + * Now set the references in the mergeclauses and rearrange them so + * that the outer variable is always on the left. + */ + mergeclauses = switch_outer(join_references(best_path->path_mergeclauses, + outer_tlist, + inner_tlist)); + + opcode = + get_opcode((best_path->jpath.path.p_ordering.ord.merge)->join_operator); + + outer_order = (Oid *) palloc(sizeof(Oid) * 2); + outer_order[0] = + (best_path->jpath.path.p_ordering.ord.merge)->left_operator; + outer_order[1] = 0; + + inner_order = (Oid *) palloc(sizeof(Oid) * 2); + inner_order[0] = + (best_path->jpath.path.p_ordering.ord.merge)->right_operator; + inner_order[1] = 0; + + /* + * Create explicit sort paths for the outer and inner join paths if + * necessary. The sort cost was already accounted for in the path. + */ + if (best_path->outersortkeys) + { + Temp *sorted_outer_node = make_temp(outer_tlist, + best_path->outersortkeys, + outer_order, + outer_node, + TEMP_SORT); + + sorted_outer_node->plan.cost = outer_node->cost; + outer_node = (Plan *) sorted_outer_node; + } + + if (best_path->innersortkeys) + { + Temp *sorted_inner_node = make_temp(inner_tlist, + best_path->innersortkeys, + inner_order, + inner_node, + TEMP_SORT); + + sorted_inner_node->plan.cost = outer_node->cost; + inner_node = (Plan *) sorted_inner_node; + } + + join_node = make_mergesort(tlist, + qpqual, + mergeclauses, + opcode, + inner_order, + outer_order, + inner_node, + outer_node); + + join_node->join.cost = best_path->jpath.path.path_cost; + + return (join_node); } -/* - * create_hashjoin_node-- XXX HASH - * - * Returns a new hashjoin node. - * - * XXX hash join ops are totally bogus -- how the hell do we choose - * these?? at runtime? what about a hash index? +/* + * create_hashjoin_node-- XXX HASH + * + * Returns a new hashjoin node. + * + * XXX hash join ops are totally bogus -- how the hell do we choose + * these?? at runtime? what about a hash index? */ static HashJoin * -create_hashjoin_node(HashPath *best_path, - List *tlist, - List *clauses, - Plan *outer_node, - List *outer_tlist, - Plan *inner_node, - List *inner_tlist) +create_hashjoin_node(HashPath * best_path, + List * tlist, + List * clauses, + Plan * outer_node, + List * outer_tlist, + Plan * inner_node, + List * inner_tlist) { - List *qpqual; - List *hashclauses; - HashJoin *join_node; - Hash *hash_node; - Var *innerhashkey; - - /* Separate the hashclauses from the other join qualification clauses - * and set those clauses to contain references to lower attributes. - */ - qpqual = - join_references(set_difference(clauses, - best_path->path_hashclauses), - outer_tlist, - inner_tlist); - - /* Now set the references in the hashclauses and rearrange them so - * that the outer variable is always on the left. - */ - hashclauses = - switch_outer(join_references(best_path->path_hashclauses, - outer_tlist, - inner_tlist)); - - innerhashkey = get_rightop(lfirst(hashclauses)); - - hash_node = make_hash(inner_tlist, innerhashkey, inner_node); - join_node = make_hashjoin(tlist, - qpqual, - hashclauses, - outer_node, - (Plan*)hash_node); - join_node->join.cost = best_path->jpath.path.path_cost; - - return(join_node); + List *qpqual; + List *hashclauses; + HashJoin *join_node; + Hash *hash_node; + Var *innerhashkey; + + /* + * Separate the hashclauses from the other join qualification clauses + * and set those clauses to contain references to lower attributes. + */ + qpqual = + join_references(set_difference(clauses, + best_path->path_hashclauses), + outer_tlist, + inner_tlist); + + /* + * Now set the references in the hashclauses and rearrange them so + * that the outer variable is always on the left. + */ + hashclauses = + switch_outer(join_references(best_path->path_hashclauses, + outer_tlist, + inner_tlist)); + + innerhashkey = get_rightop(lfirst(hashclauses)); + + hash_node = make_hash(inner_tlist, innerhashkey, inner_node); + join_node = make_hashjoin(tlist, + qpqual, + hashclauses, + outer_node, + (Plan *) hash_node); + join_node->join.cost = best_path->jpath.path.path_cost; + + return (join_node); } /***************************************************************************** * - * SUPPORTING ROUTINES + * SUPPORTING ROUTINES * *****************************************************************************/ -static Node * -fix_indxqual_references(Node *clause, Path *index_path) +static Node * +fix_indxqual_references(Node * clause, Path * index_path) { - Node *newclause; - - if (IsA(clause,Var)) { - if (lfirsti(index_path->parent->relids) == ((Var*)clause)->varno) { - int pos = 0; - int varatt = ((Var*)clause)->varattno; - int *indexkeys = ((IndexPath*)index_path)->indexkeys; - - if (indexkeys) { - while (indexkeys[pos] != 0) { - if(varatt == indexkeys[pos]) { - break; - } - pos++; + Node *newclause; + + if (IsA(clause, Var)) + { + if (lfirsti(index_path->parent->relids) == ((Var *) clause)->varno) + { + int pos = 0; + int varatt = ((Var *) clause)->varattno; + int *indexkeys = ((IndexPath *) index_path)->indexkeys; + + if (indexkeys) + { + while (indexkeys[pos] != 0) + { + if (varatt == indexkeys[pos]) + { + break; + } + pos++; + } + } + newclause = copyObject((Node *) clause); + ((Var *) newclause)->varattno = pos + 1; + return (newclause); + } + else + { + return (clause); } - } - newclause = copyObject((Node*)clause); - ((Var*)newclause)->varattno = pos + 1; - return (newclause); - } else { - return (clause); } - } else if(IsA(clause,Const)) { - return(clause); + else if (IsA(clause, Const)) + { + return (clause); #ifdef INDEXSCAN_PATCH - } else if(IsA(clause,Param)) { - /* Function parameter used as index scan arg. DZ - 27-8-1996 */ - return(clause); + } + else if (IsA(clause, Param)) + { + /* Function parameter used as index scan arg. DZ - 27-8-1996 */ + return (clause); #endif - } else if(is_opclause(clause) && - is_funcclause((Node*)get_leftop((Expr*)clause)) && - ((Func*)((Expr*)get_leftop((Expr*)clause))->oper)->funcisindex){ - Var *newvar = - makeVar((Index)lfirsti(index_path->parent->relids), - 1, /* func indices have one key */ - ((Func*)((Expr*)clause)->oper)->functype, - (Index)lfirsti(index_path->parent->relids), - 0); - - return - ((Node*)make_opclause((Oper*)((Expr*)clause)->oper, - newvar, - get_rightop((Expr*)clause))); - - } else if (IsA(clause,Expr)) { - Expr *expr = (Expr*)clause; - List *new_subclauses = NIL; - Node *subclause = NULL; - List *i = NIL; - - foreach(i, expr->args) { - subclause = lfirst(i); - if(subclause) - new_subclauses = - lappend(new_subclauses, - fix_indxqual_references(subclause, - index_path)); + } + else if (is_opclause(clause) && + is_funcclause((Node *) get_leftop((Expr *) clause)) && + ((Func *) ((Expr *) get_leftop((Expr *) clause))->oper)->funcisindex) + { + Var *newvar = + makeVar((Index) lfirsti(index_path->parent->relids), + 1, /* func indices have one key */ + ((Func *) ((Expr *) clause)->oper)->functype, + (Index) lfirsti(index_path->parent->relids), + 0); + + return + ((Node *) make_opclause((Oper *) ((Expr *) clause)->oper, + newvar, + get_rightop((Expr *) clause))); } - - /* XXX new_subclauses should be a list of the form: - * ( (var var) (var const) ...) ? - */ - if(new_subclauses) { - return (Node*) - make_clause(expr->opType, expr->oper, new_subclauses); - } else { - return(clause); - } - } else { - List *oldclauses = (List*)clause; - List *new_subclauses = NIL; - Node *subclause = NULL; - List *i = NIL; - - foreach(i, oldclauses) { - subclause = lfirst(i); - if(subclause) - new_subclauses = - lappend(new_subclauses, - fix_indxqual_references(subclause, - index_path)); + else if (IsA(clause, Expr)) + { + Expr *expr = (Expr *) clause; + List *new_subclauses = NIL; + Node *subclause = NULL; + List *i = NIL; + + foreach(i, expr->args) + { + subclause = lfirst(i); + if (subclause) + new_subclauses = + lappend(new_subclauses, + fix_indxqual_references(subclause, + index_path)); + + } + + /* + * XXX new_subclauses should be a list of the form: ( (var var) + * (var const) ...) ? + */ + if (new_subclauses) + { + return (Node *) + make_clause(expr->opType, expr->oper, new_subclauses); + } + else + { + return (clause); + } + } + else + { + List *oldclauses = (List *) clause; + List *new_subclauses = NIL; + Node *subclause = NULL; + List *i = NIL; + + foreach(i, oldclauses) + { + subclause = lfirst(i); + if (subclause) + new_subclauses = + lappend(new_subclauses, + fix_indxqual_references(subclause, + index_path)); + + } + /* + * XXX new_subclauses should be a list of the form: ( (var var) + * (var const) ...) ? + */ + if (new_subclauses) + { + return (Node *) new_subclauses; + } + else + { + return (clause); + } } - - /* XXX new_subclauses should be a list of the form: - * ( (var var) (var const) ...) ? - */ - if(new_subclauses) { - return (Node*)new_subclauses; - } else { - return (clause); - } - } } -/* +/* * switch_outer-- - * Given a list of merge clauses, rearranges the elements within the - * clauses so the outer join variable is on the left and the inner is on - * the right. - * - * Returns the rearranged list ? - * - * XXX Shouldn't the operator be commuted?! + * Given a list of merge clauses, rearranges the elements within the + * clauses so the outer join variable is on the left and the inner is on + * the right. + * + * Returns the rearranged list ? + * + * XXX Shouldn't the operator be commuted?! */ -static List * -switch_outer(List *clauses) +static List * +switch_outer(List * clauses) { - List *t_list = NIL; - Expr *temp = NULL; - List *i = NIL; - Expr *clause; - Node *op; - - foreach(i,clauses) { - clause = lfirst(i); - op = (Node*)get_rightop(clause); - if ( IsA (op, ArrayRef) ) - op = ((ArrayRef*)op)->refexpr; - Assert ( IsA (op, Var) ); - if ( var_is_outer ((Var*)op) ) + List *t_list = NIL; + Expr *temp = NULL; + List *i = NIL; + Expr *clause; + Node *op; + + foreach(i, clauses) { - temp = make_clause(clause->opType, clause->oper, - lcons(get_rightop(clause), - lcons(get_leftop(clause), - NIL))); - t_list = lappend(t_list,temp); - } - else - t_list = lappend(t_list,clause); - } - return(t_list); + clause = lfirst(i); + op = (Node *) get_rightop(clause); + if (IsA(op, ArrayRef)) + op = ((ArrayRef *) op)->refexpr; + Assert(IsA(op, Var)); + if (var_is_outer((Var *) op)) + { + temp = make_clause(clause->opType, clause->oper, + lcons(get_rightop(clause), + lcons(get_leftop(clause), + NIL))); + t_list = lappend(t_list, temp); + } + else + t_list = lappend(t_list, clause); + } + return (t_list); } -/* +/* * set-temp-tlist-operators-- - * Sets the key and keyop fields of resdom nodes in a target list. - * - * 'tlist' is the target list - * 'pathkeys' is a list of N keys in the form((key1) (key2)...(keyn)), - * corresponding to vars in the target list that are to - * be sorted or hashed - * 'operators' is the corresponding list of N sort or hash operators - * 'keyno' is the first key number - * XXX - keyno ? doesn't exist - jeff - * - * Returns the modified target list. + * Sets the key and keyop fields of resdom nodes in a target list. + * + * 'tlist' is the target list + * 'pathkeys' is a list of N keys in the form((key1) (key2)...(keyn)), + * corresponding to vars in the target list that are to + * be sorted or hashed + * 'operators' is the corresponding list of N sort or hash operators + * 'keyno' is the first key number + * XXX - keyno ? doesn't exist - jeff + * + * Returns the modified target list. */ -static List * -set_temp_tlist_operators(List *tlist, List *pathkeys, Oid *operators) +static List * +set_temp_tlist_operators(List * tlist, List * pathkeys, Oid * operators) { - Node *keys = NULL; - int keyno = 1; - Resdom *resdom = (Resdom*)NULL ; - List *i = NIL; - - foreach(i, pathkeys) { - keys = lfirst((List*)lfirst(i)); - resdom = tlist_member((Var*)keys, tlist); - if (resdom) { - - /* Order the resdom keys and replace the operator OID for each - * key with the regproc OID. - * - * XXX Note that the optimizer only generates merge joins - * with 1 operator (see create_mergejoin_node) - ay 2/95 - */ - resdom->reskey = keyno; - resdom->reskeyop = get_opcode(operators[0]); + Node *keys = NULL; + int keyno = 1; + Resdom *resdom = (Resdom *) NULL; + List *i = NIL; + + foreach(i, pathkeys) + { + keys = lfirst((List *) lfirst(i)); + resdom = tlist_member((Var *) keys, tlist); + if (resdom) + { + + /* + * Order the resdom keys and replace the operator OID for each + * key with the regproc OID. + * + * XXX Note that the optimizer only generates merge joins with 1 + * operator (see create_mergejoin_node) - ay 2/95 + */ + resdom->reskey = keyno; + resdom->reskeyop = get_opcode(operators[0]); + } + keyno += 1; } - keyno += 1; - } - return(tlist); + return (tlist); } /***************************************************************************** @@ -808,355 +878,362 @@ set_temp_tlist_operators(List *tlist, List *pathkeys, Oid *operators) * *****************************************************************************/ -/* +/* * make_temp-- - * Create plan nodes to sort or materialize relations into temporaries. The - * result returned for a sort will look like (SEQSCAN(SORT(plan-node))) - * or (SEQSCAN(MATERIAL(plan-node))) - * - * 'tlist' is the target list of the scan to be sorted or hashed - * 'keys' is the list of keys which the sort or hash will be done on - * 'operators' is the operators with which the sort or hash is to be done - * (a list of operator OIDs) - * 'plan-node' is the node which yields tuples for the sort - * 'temptype' indicates which operation(sort or hash) to perform + * Create plan nodes to sort or materialize relations into temporaries. The + * result returned for a sort will look like (SEQSCAN(SORT(plan-node))) + * or (SEQSCAN(MATERIAL(plan-node))) + * + * 'tlist' is the target list of the scan to be sorted or hashed + * 'keys' is the list of keys which the sort or hash will be done on + * 'operators' is the operators with which the sort or hash is to be done + * (a list of operator OIDs) + * 'plan-node' is the node which yields tuples for the sort + * 'temptype' indicates which operation(sort or hash) to perform */ -static Temp * -make_temp(List *tlist, - List *keys, - Oid *operators, - Plan *plan_node, - int temptype) +static Temp * +make_temp(List * tlist, + List * keys, + Oid * operators, + Plan * plan_node, + int temptype) { - List *temp_tlist; - Temp *retval = NULL; - - /* Create a new target list for the temporary, with keys set. */ - temp_tlist = set_temp_tlist_operators(new_unsorted_tlist(tlist), - keys, - operators); - switch(temptype) { - case TEMP_SORT : - retval = (Temp*)make_seqscan(tlist, - NIL, - _TEMP_RELATION_ID_, - (Plan*)make_sort(temp_tlist, - _TEMP_RELATION_ID_, - plan_node, - length(keys))); - break; - - case TEMP_MATERIAL : - retval = (Temp*)make_seqscan(tlist, - NIL, - _TEMP_RELATION_ID_, - (Plan*)make_material(temp_tlist, - _TEMP_RELATION_ID_, - plan_node, - length(keys))); - break; - - default: - elog(WARN,"make_temp: unknown temp type %d", temptype); - - } - return(retval); + List *temp_tlist; + Temp *retval = NULL; + + /* Create a new target list for the temporary, with keys set. */ + temp_tlist = set_temp_tlist_operators(new_unsorted_tlist(tlist), + keys, + operators); + switch (temptype) + { + case TEMP_SORT: + retval = (Temp *) make_seqscan(tlist, + NIL, + _TEMP_RELATION_ID_, + (Plan *) make_sort(temp_tlist, + _TEMP_RELATION_ID_, + plan_node, + length(keys))); + break; + + case TEMP_MATERIAL: + retval = (Temp *) make_seqscan(tlist, + NIL, + _TEMP_RELATION_ID_, + (Plan *) make_material(temp_tlist, + _TEMP_RELATION_ID_, + plan_node, + length(keys))); + break; + + default: + elog(WARN, "make_temp: unknown temp type %d", temptype); + + } + return (retval); } -SeqScan * -make_seqscan(List *qptlist, - List *qpqual, - Index scanrelid, - Plan *lefttree) +SeqScan * +make_seqscan(List * qptlist, + List * qpqual, + Index scanrelid, + Plan * lefttree) { - SeqScan *node = makeNode(SeqScan); - Plan *plan = &node->plan; - - plan->cost = 0.0; - plan->state = (EState *)NULL; - plan->targetlist = qptlist; - plan->qual = qpqual; - plan->lefttree = lefttree; - plan->righttree = NULL; - node->scanrelid = scanrelid; - node->scanstate = (CommonScanState *)NULL; - - return(node); + SeqScan *node = makeNode(SeqScan); + Plan *plan = &node->plan; + + plan->cost = 0.0; + plan->state = (EState *) NULL; + plan->targetlist = qptlist; + plan->qual = qpqual; + plan->lefttree = lefttree; + plan->righttree = NULL; + node->scanrelid = scanrelid; + node->scanstate = (CommonScanState *) NULL; + + return (node); } static IndexScan * -make_indexscan(List *qptlist, - List *qpqual, - Index scanrelid, - List *indxid, - List *indxqual) +make_indexscan(List * qptlist, + List * qpqual, + Index scanrelid, + List * indxid, + List * indxqual) { - IndexScan *node = makeNode(IndexScan); - Plan *plan = &node->scan.plan; - - plan->cost = 0.0; - plan->state = (EState *)NULL; - plan->targetlist = qptlist; - plan->qual = qpqual; - plan->lefttree = NULL; - plan->righttree = NULL; - node->scan.scanrelid = scanrelid; - node->indxid = indxid; - node->indxqual = indxqual; - node->scan.scanstate = (CommonScanState *)NULL; - - return(node); + IndexScan *node = makeNode(IndexScan); + Plan *plan = &node->scan.plan; + + plan->cost = 0.0; + plan->state = (EState *) NULL; + plan->targetlist = qptlist; + plan->qual = qpqual; + plan->lefttree = NULL; + plan->righttree = NULL; + node->scan.scanrelid = scanrelid; + node->indxid = indxid; + node->indxqual = indxqual; + node->scan.scanstate = (CommonScanState *) NULL; + + return (node); } static NestLoop * -make_nestloop(List *qptlist, - List *qpqual, - Plan *lefttree, - Plan *righttree) +make_nestloop(List * qptlist, + List * qpqual, + Plan * lefttree, + Plan * righttree) { - NestLoop *node = makeNode(NestLoop); - Plan *plan = &node->join; - - plan->cost = 0.0; - plan->state = (EState *)NULL; - plan->targetlist = qptlist; - plan->qual = qpqual; - plan->lefttree = lefttree; - plan->righttree = righttree; - node->nlstate = (NestLoopState*)NULL; - - return(node); + NestLoop *node = makeNode(NestLoop); + Plan *plan = &node->join; + + plan->cost = 0.0; + plan->state = (EState *) NULL; + plan->targetlist = qptlist; + plan->qual = qpqual; + plan->lefttree = lefttree; + plan->righttree = righttree; + node->nlstate = (NestLoopState *) NULL; + + return (node); } static HashJoin * -make_hashjoin(List *tlist, - List *qpqual, - List *hashclauses, - Plan *lefttree, - Plan *righttree) +make_hashjoin(List * tlist, + List * qpqual, + List * hashclauses, + Plan * lefttree, + Plan * righttree) { - HashJoin *node = makeNode(HashJoin); - Plan *plan = &node->join; - - plan->cost = 0.0; - plan->state = (EState *)NULL; - plan->targetlist = tlist; - plan->qual = qpqual; - plan->lefttree = lefttree; - plan->righttree = righttree; - node->hashclauses = hashclauses; - node->hashjointable = NULL; - node->hashjointablekey = 0; - node->hashjointablesize = 0; - node->hashdone = false; - - return(node); + HashJoin *node = makeNode(HashJoin); + Plan *plan = &node->join; + + plan->cost = 0.0; + plan->state = (EState *) NULL; + plan->targetlist = tlist; + plan->qual = qpqual; + plan->lefttree = lefttree; + plan->righttree = righttree; + node->hashclauses = hashclauses; + node->hashjoin |