/*------------------------------------------------------------------------- * * indextuple.c-- * 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.30 1998/08/27 05:06:54 momjian Exp $ * *------------------------------------------------------------------------- */ #include <postgres.h> #include <access/heapam.h> #include <access/ibit.h> #include <access/itup.h> #include <access/tupmacs.h> #include <catalog/pg_type.h> #ifndef HAVE_MEMMOVE #include <regex/utils.h> #else #include <string.h> #endif /* ---------------------------------------------------------------- * index_ tuple interface routines * ---------------------------------------------------------------- */ /* ---------------- * index_formtuple * ---------------- */ IndexTuple index_formtuple(TupleDesc tupleDescriptor, Datum value[], char null[]) { char *tp; /* tuple pointer */ IndexTuple tuple; /* return tuple */ Size size, hoff; int i; unsigned short infomask = 0; bool hasnull = false; uint16 tupmask = 0; int numberOfAttributes = tupleDescriptor->natts; if (numberOfAttributes > MaxIndexAttributeNumber) elog(ERROR, "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(ERROR, "index_formtuple: data takes %d bytes: too big", size); infomask |= size; /* ---------------- * initialize metadata * ---------------- */ tuple->t_info = infomask; return (tuple); } /* ---------------- * nocache_index_getattr * * This gets called from index_getattr() macro, and only in cases * where we can't use cacheoffset and the value is not null. * * This caches 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. * * 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 * ---------------- */ Datum nocache_index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) { char *tp; /* ptr to att in tuple */ char *bp = NULL; /* ptr to att in tuple */ int slow; /* do we have to walk nulls? */ int data_off; /* tuple data offset */ AttributeTupleForm *att = tupleDesc->attrs; /* ---------------- * sanity checks * ---------------- */ /* ---------------- * 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. * ---------------- */ #ifdef IN_MACRO /* This is handled in the macro */ Assert(PointerIsValid(isnull)); Assert(attnum > 0); *isnull = false; #endif data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup : IndexInfoFindDataOffset(tup->t_info); if (IndexTupleNoNulls(tup)) { attnum--; #ifdef IN_MACRO /* This is handled in the macro */ /* first attribute is always at position zero */ if (attnum == 1) return (Datum) fetchatt(&(att[0]), (char *) tup + data_off); if (att[attnum]->attcacheoff != -1) { return (Datum) fetchatt(&(att[attnum]), (char *) tup + data_off + att[attnum]->attcacheoff); } #endif slow = 0; } else { /* there's a null somewhere in the tuple */ slow = 0; /* ---------------- * check to see if desired att is null * ---------------- */ attnum--; bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are * here! */ #ifdef IN_MACRO /* This is handled in the macro */ if (att_isnull(attnum, bp)) { *isnull = true; return (Datum) NULL; } #endif /* ---------------- * Now check to see if any preceeding bits are null... * ---------------- */ { int i = 0; /* current offset in bp */ int mask; /* bit in byte we're looking at */ char n; /* current byte in bp */ int byte, finalbit; byte = attnum >> 3; finalbit = attnum & 0x07; for (; i <= byte && !slow; i++) { n = bp[i]; if (i < byte) { /* check for nulls in any "earlier" bytes */ if ((~n) != 0) slow = 1; } else { /* check for nulls "before" final bit of last byte */ mask = (1 << finalbit) - 1; if ((~n) & mask) slow = 1; } } } } tp = (char *) tup + data_off; /* now check for any non-fixed length attrs before our attribute */ if (!slow) { if (att[attnum]->attcacheoff != -1) { return (Datum) fetchatt(&(att[attnum]), tp + att[attnum]->attcacheoff); } else if (attnum == 0) return ((Datum) fetchatt(&(att[0]), (char *) tp)); else if (!IndexTupleAllFixed(tup)) { int j = 0; for (j = 0; j < attnum && !slow; j++) if (att[j]->attlen < 1 && !VARLENA_FIXED_SIZE(att[j])) slow = 1; } } /* * 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) { int j = 1; long off; /* * need to set cache for some atts */ att[0]->attcacheoff = 0; while (att[j]->attcacheoff != -1) j++; if (!VARLENA_FIXED_SIZE(att[j - 1])) off = att[j - 1]->attcacheoff + att[j - 1]->attlen; else off = att[j - 1]->attcacheoff + att[j - 1]->atttypmod; 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(ERROR, "nocache_index_getattr: attribute %d has len %d", j, att[j]->attlen); break; } att[j]->attcacheoff = off; /* The only varlena/-1 length value to get here is this */ if (!VARLENA_FIXED_SIZE(att[j])) off += att[j]->attlen; else { Assert(att[j]->atttypmod == VARSIZE(tp + off)); off += att[j]->atttypmod; } } return (Datum) fetchatt(&(att[attnum]), tp + att[attnum]->attcacheoff); } else { bool usecache = true; int off = 0; 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 we know the next offset, we can skip the rest */ if (usecache && att[i]->attcacheoff != -1) off = att[i]->attcacheoff; else { 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(ERROR, "nocachegetiattr2: 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 = off; } switch (att[i]->attlen) { case sizeof(char): off++; break; case sizeof(short): off += sizeof(short); break; case sizeof(int32): off += sizeof(int32); break; case -1: Assert(!VARLENA_FIXED_SIZE(att[i]) || att[i]->atttypmod == VARSIZE(tp + off)); off += VARSIZE(tp + off); if (!VARLENA_FIXED_SIZE(att[i])) usecache = false; 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(ERROR, "nocache_index_getattr: attribute %d has len %d", attnum, att[attnum]->attlen); if (att[attnum]->attalign == 'd') off = DOUBLEALIGN(off); else off = LONGALIGN(off); break; } return (Datum) fetchatt(&att[attnum], tp + off); } } RetrieveIndexResult FormRetrieveIndexResult(ItemPointer indexItemPointer, 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); } /* * Copies source into target. If *target == NULL, we palloc space; otherwise * we assume we have space that is already palloc'ed. */ void 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); }