/* ---------------- * nocachegetattr * * This only gets called from fastgetattr() macro, in cases where * we can't use a cacheoffset and the value is not null. * * This caches attribute offsets in the attribute descriptor. * * An alternative 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 * perform 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 * * NOTE: if you need to change this code, see also heap_deform_tuple. * Also see nocache_index_getattr, which is the same code for index * tuples. * ---------------- */ Datum nocachegetattr(HeapTuple tuple, int attnum, TupleDesc tupleDesc) { HeapTupleHeader tup = tuple->t_data; Form_pg_attribute *att = tupleDesc->attrs; char *tp; /* ptr to data part of tuple */ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ bool slow = false; /* do we have to walk attrs? */ int off; /* current offset within data */ Assert(!is_heaptuple_memtuple(tuple)); /* ---------------- * Three cases: * * 1: No nulls and no variable-width attributes. * 2: Has a null or a var-width AFTER att. * 3: Has nulls or var-widths BEFORE att. * ---------------- */ #ifdef IN_MACRO /* This is handled in the macro */ Assert(attnum > 0); if (isnull) *isnull = false; #endif attnum--; if (HeapTupleNoNulls(tuple)) { #ifdef IN_MACRO /* This is handled in the macro */ if (att[attnum]->attcacheoff >= 0) { return fetchatt(att[attnum], (char *) tup + tup->t_hoff + att[attnum]->attcacheoff); } #endif } else { /* * there's a null somewhere in the tuple * * check to see if desired att is null */ #ifdef IN_MACRO /* This is handled in the macro */ if (att_isnull(attnum, bp)) { if (isnull) *isnull = true; return (Datum) NULL; } #endif /* * Now check to see if any preceding bits are null... */ { int byte = attnum >> 3; int finalbit = attnum & 0x07; /* check for nulls "before" final bit of last byte */ if ((~bp[byte]) & ((1 << finalbit) - 1)) slow = true; else { /* check for nulls in any "earlier" bytes */ int i; for (i = 0; i < byte; i++) { if (bp[i] != 0xFF) { slow = true; break; } } } } } tp = (char *) tup + tup->t_hoff; if (!slow) { /* * If we get here, there are no nulls up to and including the target * attribute. If we have a cached offset, we can use it. */ if (att[attnum]->attcacheoff >= 0) { return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } /* * Otherwise, check for non-fixed-length attrs up to and including * target. If there aren't any, it's safe to cheaply initialize the * cached offsets for these attrs. */ if (HeapTupleHasVarWidth(tuple)) { int j; for (j = 0; j <= attnum; j++) { if (att[j]->attlen <= 0) { slow = true; break; } } } } if (!slow) { int natts = tupleDesc->natts; int j = 1; /* * If we get here, we have a tuple with no nulls or var-widths up to * and including the target attribute, so we can use the cached offset * ... only we don't have it yet, or we'd not have got here. Since * it's cheap to compute offsets for fixed-width columns, we take the * opportunity to initialize the cached offsets for *all* the leading * fixed-width columns, in hope of avoiding future visits to this * routine. */ att[0]->attcacheoff = 0; /* we might have set some offsets in the slow path previously */ while (j < natts && att[j]->attcacheoff > 0) j++; off = att[j - 1]->attcacheoff + att[j - 1]->attlen; for (; j < natts; j++) { if (att[j]->attlen <= 0) break; off = att_align_nominal(off, att[j]->attalign); att[j]->attcacheoff = off; off += att[j]->attlen; } Assert(j > attnum); off = att[attnum]->attcacheoff; } else { bool usecache = true; int i; /* this is always true */ att[0]->attcacheoff = 0; /* * Now we know that we have to walk the tuple CAREFULLY. But we still * might be able to cache some offsets for next time. * * Note - This loop is a little tricky. For each non-null attribute, * we have to first account for alignment padding before the attr, * then advance over the attr based on its length. Nulls have no * storage and no alignment padding either. We can use/set * attcacheoff until we reach either a null or a var-width attribute. */ off = 0; for (i = 0;; i++) /* loop exit is at "break" */ { if (HeapTupleHasNulls(tuple) && att_isnull(i, bp)) { usecache = false; continue; /* this cannot be the target att */ } /* If we know the next offset, we can skip the rest */ if (usecache && att[i]->attcacheoff >= 0) off = att[i]->attcacheoff; else if (att[i]->attlen == -1) { /* * We can only cache the offset for a varlena attribute if the * offset is already suitably aligned, so that there would be * no pad bytes in any case: then the offset will be valid for * either an aligned or unaligned value. */ if (usecache && off == att_align_nominal(off, att[i]->attalign)) att[i]->attcacheoff = off; else { off = att_align_pointer(off, att[i]->attalign, -1, tp + off); usecache = false; } } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, att[i]->attalign); if (usecache) att[i]->attcacheoff = off; } if (i == attnum) break; off = att_addlength_pointer(off, att[i]->attlen, tp + off); if (usecache && att[i]->attlen <= 0) usecache = false; } } return fetchatt(att[attnum], tp + off); }
/* ---------------- * nocachegetattr * * This only gets called from fastgetattr() macro, in cases where * we can't use a cacheoffset and the value is not null. * * This caches attribute offsets in the attribute descriptor. * * An alternative 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 * perform 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 * * NOTE: if you need to change this code, see also heap_deform_tuple. * Also see nocache_index_getattr, which is the same code for index * tuples. * ---------------- */ Datum nocachegetattr(HeapTuple tuple, int attnum, TupleDesc tupleDesc) { HeapTupleHeader tup = tuple->t_data; Form_pg_attribute *att = tupleDesc->attrs; char *tp; /* ptr to att in tuple */ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ bool slow = false; /* do we have to walk nulls? */ Assert(!is_heaptuple_memtuple(tuple)); /* If any cached offsets are there we can check that they make sense, but * there may not be any at all, so pass -1 for the attnum we know is valid */ #ifdef IN_MACRO /* This is handled in the macro */ Assert(attnum > 0); if (isnull) *isnull = false; #endif attnum--; /* ---------------- * Three cases: * * 1: No nulls and no variable-width attributes. * 2: Has a null or a var-width AFTER att. * 3: Has nulls or var-widths BEFORE att. * ---------------- */ if (HeapTupleNoNulls(tuple)) { #ifdef IN_MACRO /* This is handled in the macro */ if (att[attnum]->attcacheoff != -1) { return fetchatt(att[attnum], (char *) tup + tup->t_hoff + att[attnum]->attcacheoff); } #endif } else { /* * there's a null somewhere in the tuple * * check to see if desired att is null */ #ifdef IN_MACRO /* This is handled in the macro */ if (att_isnull(attnum, bp)) { if (isnull) *isnull = true; return (Datum) NULL; } #endif /* * Now check to see if any preceding bits are null... */ { int byte = attnum >> 3; int finalbit = attnum & 0x07; /* check for nulls "before" final bit of last byte */ if ((~bp[byte]) & ((1 << finalbit) - 1)) slow = true; else { /* check for nulls in any "earlier" bytes */ int i; for (i = 0; i < byte; i++) { if (bp[i] != 0xFF) { slow = true; break; } } } } } tp = (char *) tup + tup->t_hoff; /* * now check for any non-fixed length attrs before our attribute */ if (!slow) { /* * If we get here, there are no nulls up to and including the target * attribute. If we have a cached offset, we can use it. */ if (att[attnum]->attcacheoff >= 0) { return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } /* * Otherwise, check for non-fixed-length attrs up to and including * target. If there aren't any, it's safe to cheaply initialize the * cached offsets for these attrs. */ if (HeapTupleHasVarWidth(tuple)) { int j; /* * In for(), we test <= and not < because we want to see if we can * go past it in initializing offsets. */ for (j = 0; j <= attnum; j++) { if (att[j]->attlen <= 0) { slow = true; break; } } } } /* * If slow is false, and we got here, we know that we have a tuple with no * nulls or var-widths before the target attribute. If possible, we also * want to initialize the remainder of the attribute cached offset values. */ if (!slow) { int j = 1; long off; int natts = HeapTupleHeaderGetNatts(tup); /* * If we get here, we have a tuple with no nulls or var-widths up to * and including the target attribute, so we can use the cached offset * ... only we don't have it yet, or we'd not have got here. Since * it's cheap to compute offsets for fixed-width columns, we take the * opportunity to initialize the cached offsets for *all* the leading * fixed-width columns, in hope of avoiding future visits to this * routine. */ /* this is always true */ att[0]->attcacheoff = 0; while (j < attnum && att[j]->attcacheoff > 0) j++; off = att[j - 1]->attcacheoff + att[j - 1]->attlen; for (; j <= attnum || /* Can we compute more? We will probably need them */ (j < natts && att[j]->attcacheoff == -1 && (HeapTupleNoNulls(tuple) || !att_isnull(j, bp)) && (HeapTupleAllFixed(tuple) || att[j]->attlen > 0)); j++) { /* don't need to worry about shortvarlenas here since we're only * looking at non-varlenas. Note that it's important that we check * that the target attribute itself is a nonvarlena too since we * can't use cached offsets for even the first varlena any more. */ off = att_align(off, att[j]->attalign); att[j]->attcacheoff = off; off = att_addlength(off, att[j]->attlen, PointerGetDatum(tp + off)); } return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } else { bool usecache = true; int off = 0; int i; /* this is always true */ att[0]->attcacheoff = 0; /* * Now we know that we have to walk the tuple CAREFULLY. * * Note - This loop is a little tricky. For each non-null attribute, * we have to first account for alignment padding before the attr, * then advance over the attr based on its length. Nulls have no * storage and no alignment padding either. We can use/set * attcacheoff until we reach either a null or a var-width attribute. */ for (i = 0; i < attnum; i++) { if (HeapTupleHasNulls(tuple) && att_isnull(i, bp)) { usecache = false; continue; } /* If we know the next offset, we can skip the alignment calc */ if (usecache && att[i]->attcacheoff != -1) off = att[i]->attcacheoff; else { /* if it's a varlena it may or may not be aligned, so check for * something that looks like a padding byte before aligning. If * we're already aligned it may be the leading byte of a 4-byte * header but then the att_align is harmless. Don't bother * looking if it's not a varlena though.*/ if (att[i]->attlen != -1 || !tp[off]) off = att_align(off, att[i]->attalign); if (usecache && att[i]->attlen != -1) att[i]->attcacheoff = off; } if (att[i]->attlen < 0) usecache = false; off = att_addlength(off, att[i]->attlen, PointerGetDatum(tp + off)); } if (att[attnum]->attlen != -1 || !tp[off]) off = att_align(off, att[attnum]->attalign); return fetchatt(att[attnum], tp + off); } }