/* * pivot_find() - Searchs an array of labels for a matching value. * * Returns: index of found value, or -1 if not found * * It may eventually do something smarter than a linear scan, but * for now this is sufficient given that we don't know anything about the * order of 'labels'. * */ static int pivot_find(ArrayType *labels, text *attr) { char *labelsp; int i, nelem, asize; int16 typlen; bool typbyval; char typalign; if (ARR_ELEMTYPE(labels) != TEXTOID) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("pivot_accum: labels are not type text"))); /* Text alignment properties */ get_typlenbyvalalign(TEXTOID, &typlen, &typbyval, &typalign); /* Get the size of the input attribute, we'll use this for fast compares */ asize = VARSIZE(attr); /* The labels array is an array of varying length text, scan it adding the length of the previous entry until we are done or we have found a match. */ labelsp = (char *) ARR_DATA_PTR(labels); nelem = ARR_DIMS(labels)[0]; for (i = 0; i < nelem; i++) { int lsize = VARSIZE(labelsp); if (asize == lsize && !memcmp(attr, labelsp, lsize)) return i; /* Found */ labelsp = labelsp + lsize; labelsp = (char*) att_align(labelsp, typalign); } return -1; /* Not found */ }
/* * heap_compute_data_size * Determine size of the data area of a tuple to be constructed */ Size heap_compute_data_size(TupleDesc tupleDesc, Datum *values, bool *isnull) { Size data_length = 0; int i; int numberOfAttributes = tupleDesc->natts; Form_pg_attribute *att = tupleDesc->attrs; for (i = 0; i < numberOfAttributes; i++) { if (isnull[i]) continue; /* we're anticipating converting to a short varlena header even if it's * not currently */ if (att[i]->attlen == -1 && value_type_could_short(values[i], att[i]->atttypid)) { /* no alignment and we will convert to 1-byte header */; data_length += VARSIZE_ANY_EXHDR_D(values[i]) + VARHDRSZ_SHORT; } else { data_length = att_align(data_length, att[i]->attalign); data_length = att_addlength(data_length, att[i]->attlen, values[i]); } } return data_length; }
/* * Sets the binding length according to the following binding's alignment. * Adds the aligned length into the array holding the space saved from null attributes. * Returns true if the binding length is aligned to the following binding's alignment. */ static inline bool add_null_save_aligned(MemTupleAttrBinding *bind, short *null_save_aligned, int i, char next_attr_align) { Assert(bind); Assert(bind->len > 0); Assert(null_save_aligned); Assert(i >= 0); bind->len_aligned = att_align(bind->len, next_attr_align); add_null_save(null_save_aligned, i, bind->len_aligned); return (bind->len == bind->len_aligned); }
Datum nb_classify_final(PG_FUNCTION_ARGS) { nb_classify_state state; int64 *total_data; float8 total; float8 *prior_data; char *class_data; int i, maxi, nclasses; if (PG_NARGS() != 1) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("nb_classify_final called with %d arguments", PG_NARGS()))); } if (PG_ARGISNULL(0)) PG_RETURN_NULL(); get_nb_state(PG_GETARG_HEAPTUPLEHEADER_COPY(0), &state, 0); /* The the prior with maximum likelyhood */ prior_data = (float8*) ARR_DATA_PTR(state.accum); total_data = (int64*) ARR_DATA_PTR(state.total); nclasses = ARR_DIMS(state.classes)[0]; for (total = i = 0; i < nclasses; i++) total += total_data[i]; for (i = 0; i < nclasses; i++) prior_data[i] += log(total_data[i]/total); for (maxi = 0, i = 1; i < nclasses; i++) if (prior_data[i] > prior_data[maxi]) maxi = i; /* * Looking up values in text arrays in a pain. You must start at the * beginning and walk adding the length of the current element until * you get to the index you are interested in. */ class_data = (char*) ARR_DATA_PTR(state.classes); for (i = 0; i < maxi; i++) { class_data += VARSIZE(class_data); class_data = (char*) att_align(class_data, 'i'); } PG_RETURN_TEXT_P(class_data); }
bool array_next(array_iter *iter, Datum *value, bool *isna) { bits8 *nulls; if (iter->index >= iter->max) { *value = (Datum) 0; *isna = true; return false; } if (iter->index < 0) { *value = (Datum) 0; *isna = true; iter->index++; return true; } nulls = ARR_NULLBITMAP(iter->array); if (nulls && !(nulls[iter->index / 8] & (1 << (iter->index % 8)))) { *value = (Datum) 0; *isna = true; iter->index++; return true; } *isna = false; if (iter->typlen > 0) { /* fixed length */ if (iter->typlen <= 8) { switch (iter->typlen) { case 1: *value = Int8GetDatum(*((int8*) iter->ptr)); break; case 2: *value = Int16GetDatum(*((int16*) iter->ptr)); break; case 4: *value = Int32GetDatum(*((int16*) iter->ptr)); break; case 8: *value = Int64GetDatum(*((int16*) iter->ptr)); break; default: elog(ERROR, "unexpected data type"); break; } } else { *value = PointerGetDatum(iter->ptr); } iter->ptr += iter->typlen; } else { /* variable length */ *value = PointerGetDatum(iter->ptr); iter->ptr += VARSIZE(iter->ptr); } iter->ptr = (char*) att_align(iter->ptr, iter->typalign); iter->index++; return true; }
/* ---------------- * 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 * 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 * ---------------- */ Datum nocache_index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) { Form_pg_attribute *att = tupleDesc->attrs; char *tp; /* ptr to att in tuple */ bits8 *bp = NULL; /* ptr to null bitmask in tuple */ bool slow = false; /* do we have to walk nulls? */ int data_off; /* tuple data offset */ (void) isnull; /* not used */ /* * sanity checks */ /* ---------------- * 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(PointerIsValid(isnull)); Assert(attnum > 0); *isnull = false; #endif data_off = IndexInfoFindDataOffset(tup->t_info); attnum--; if (!IndexTupleHasNulls(tup)) { #ifdef IN_MACRO /* This is handled in the macro */ if (att[attnum]->attcacheoff != -1) { return fetchatt(att[attnum], (char *) tup + data_off + att[attnum]->attcacheoff); } #endif } else { /* * there's a null somewhere in the tuple * * check to see if desired att is null */ /* XXX "knows" t_bits are just after fixed tuple header! */ bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData)); #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 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 + data_off; /* * now check for any non-fixed length attrs before our attribute */ if (!slow) { if (att[attnum]->attcacheoff != -1) { return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } else if (IndexTupleHasVarwidths(tup)) { int j; 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; /* * need to set cache for some atts */ att[0]->attcacheoff = 0; while (j < attnum && att[j]->attcacheoff > 0) j++; off = att[j - 1]->attcacheoff + att[j - 1]->attlen; for (; j <= attnum; j++) { off = att_align(off, att[j]->attalign); att[j]->attcacheoff = off; off += att[j]->attlen; } return 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 (IndexTupleHasNulls(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 { off = att_align(off, att[i]->attalign); if (usecache) att[i]->attcacheoff = off; } off = att_addlength(off, att[i]->attlen, tp + off); if (usecache && att[i]->attlen <= 0) usecache = false; } off = att_align(off, att[attnum]->attalign); return fetchatt(att[attnum], tp + off); } }
/* * load_auth_entries: read pg_authid into auth_entry[] * * auth_info_out: pointer to auth_entry * where address to auth_entry[] should be stored * total_roles_out: pointer to int where num of total roles should be stored */ static void load_auth_entries(Relation rel_authid, auth_entry **auth_info_out, int *total_roles_out) { BlockNumber totalblocks; HeapScanDesc scan; HeapTuple tuple; int curr_role = 0; int total_roles = 0; int est_rows; auth_entry *auth_info; /* * Read pg_authid and fill temporary data structures. Note we must read * all roles, even those without rolcanlogin. */ totalblocks = RelationGetNumberOfBlocks(rel_authid); totalblocks = totalblocks ? totalblocks : 1; est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_authid))); auth_info = (auth_entry *) palloc(est_rows * sizeof(auth_entry)); scan = heap_beginscan(rel_authid, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_authid aform = (Form_pg_authid) GETSTRUCT(tuple); HeapTupleHeader tup = tuple->t_data; char *tp; /* ptr to tuple data */ long off; /* offset in tuple data */ bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */ Datum datum; if (curr_role >= est_rows) { est_rows *= 2; auth_info = (auth_entry *) repalloc(auth_info, est_rows * sizeof(auth_entry)); } auth_info[curr_role].roleid = HeapTupleGetOid(tuple); auth_info[curr_role].rolsuper = aform->rolsuper; auth_info[curr_role].rolcanlogin = aform->rolcanlogin; auth_info[curr_role].rolname = pstrdup(NameStr(aform->rolname)); auth_info[curr_role].member_of = NIL; /* * We can't use heap_getattr() here because during startup we will not * have any tupdesc for pg_authid. Fortunately it's not too hard to * work around this. rolpassword is the first possibly-null field so * we can compute its offset directly. */ tp = (char *) tup + tup->t_hoff; off = offsetof(FormData_pg_authid, rolpassword); if (HeapTupleHasNulls(tuple) && att_isnull(Anum_pg_authid_rolpassword - 1, bp)) { /* passwd is null, emit as an empty string */ auth_info[curr_role].rolpassword = pstrdup(""); } else { /* assume passwd is pass-by-ref */ datum = PointerGetDatum(tp + off); /* * The password probably shouldn't ever be out-of-line toasted; if * it is, ignore it, since we can't handle that in startup mode. */ if (VARATT_IS_EXTERNAL(DatumGetPointer(datum))) auth_info[curr_role].rolpassword = pstrdup(""); else auth_info[curr_role].rolpassword = DatumGetCString(DirectFunctionCall1(textout, datum)); /* assume passwd has attlen -1 */ off = att_addlength(off, -1, PointerGetDatum(tp + off)); } if (HeapTupleHasNulls(tuple) && att_isnull(Anum_pg_authid_rolvaliduntil - 1, bp)) { /* rolvaliduntil is null, emit as an empty string */ auth_info[curr_role].rolvaliduntil = pstrdup(""); } else { /* * rolvaliduntil is timestamptz, which we assume is double * alignment and pass-by-value. */ off = att_align(off, 'd'); datum = fetch_att(tp + off, true, sizeof(TimestampTz)); auth_info[curr_role].rolvaliduntil = DatumGetCString(DirectFunctionCall1(timestamptz_out, datum)); } /* * Check for illegal characters in the user name and password. */ if (!name_okay(auth_info[curr_role].rolname)) { ereport(LOG, (errmsg("invalid role name \"%s\"", auth_info[curr_role].rolname))); continue; } if (!name_okay(auth_info[curr_role].rolpassword)) { ereport(LOG, (errmsg("invalid role password \"%s\"", auth_info[curr_role].rolpassword))); continue; } curr_role++; total_roles++; } heap_endscan(scan); *auth_info_out = auth_info; *total_roles_out = total_roles; }
/* Create columns binding, depends on islarge, using 2 or 4 bytes for offset_len */ static void create_col_bind(MemTupleBindingCols *colbind, bool islarge, TupleDesc tupdesc, int col_align) { int i = 0; int physical_col = 0; int pass = 0; uint32 cur_offset = (tupdesc->tdhasoid || col_align == 8) ? 8 : 4; uint32 null_save_entries = compute_null_save_entries(tupdesc->natts); /* alloc null save entries. Zero it */ colbind->null_saves = (short *) palloc0(sizeof(short) * null_save_entries); colbind->null_saves_aligned = (short *) palloc0(sizeof(short) * null_save_entries); colbind->has_null_saves_alignment_mismatch = false; colbind->has_dropped_attr_alignment_mismatch = false; /* alloc bindings, no need to zero because we will fill them out */ colbind->bindings = (MemTupleAttrBinding *) palloc(sizeof(MemTupleAttrBinding) * tupdesc->natts); /* * The length of each binding is determined according to the alignment * of the physically following binding. Use this pointer to keep track * of the previously processed binding. */ MemTupleAttrBinding *previous_bind = NULL; /* * First pass, do 8 bytes aligned, native type. * Sencond pass, do 4 bytes aligned, native type. * Third pass, do 2 bytes aligned, native type. * Finall, do 1 bytes aligned native type. * * depends on islarge, varlena types are either handled in the * second pass (is large, varoffset using 4 bytes), or in the * third pass (not large, varoffset using 2 bytes). */ for(pass =0; pass < 4; ++pass) { for(i=0; i<tupdesc->natts; ++i) { Form_pg_attribute attr = tupdesc->attrs[i]; MemTupleAttrBinding *bind = &colbind->bindings[i]; if(pass == 0 && attr->attlen > 0 && attr->attalign == 'd') { bind->offset = att_align(cur_offset, attr->attalign); bind->len = attr->attlen; add_null_save(colbind->null_saves, physical_col, attr->attlen); if (physical_col) { /* Set the aligned length of the previous binding according to current alignment. */ if (add_null_save_aligned(previous_bind, colbind->null_saves_aligned, physical_col - 1, 'd')) { colbind->has_null_saves_alignment_mismatch = true; if (attr->attisdropped) { colbind->has_dropped_attr_alignment_mismatch = true; } } } bind->flag = attr->attbyval ? MTB_ByVal_Native : MTB_ByVal_Ptr; bind->null_byte = physical_col >> 3; bind->null_mask = 1 << (physical_col-(bind->null_byte << 3)); physical_col += 1; cur_offset = bind->offset + bind->len; previous_bind = bind; } else if (pass == 1 &&( (attr->attlen > 0 && attr->attalign == 'i') || ( islarge && att_bind_as_varoffset(attr)) ) ) { bind->offset = att_align(cur_offset, 'i'); bind->len = attr->attlen > 0 ? attr->attlen : 4; add_null_save(colbind->null_saves, physical_col, bind->len); if (physical_col) { /* Set the aligned length of the previous binding according to current alignment. */ if (add_null_save_aligned(previous_bind, colbind->null_saves_aligned, physical_col - 1, 'i')) { colbind->has_null_saves_alignment_mismatch = true; if (attr->attisdropped) { colbind->has_dropped_attr_alignment_mismatch = true; } } } if(attr->attlen > 0) bind->flag = attr->attbyval ? MTB_ByVal_Native : MTB_ByVal_Ptr; else if(attr->attlen == -1) bind->flag = MTB_ByRef; else { Assert(attr->attlen == -2); bind->flag = MTB_ByRef_CStr; } bind->null_byte = physical_col >> 3; bind->null_mask = 1 << (physical_col-(bind->null_byte << 3)); physical_col += 1; cur_offset = bind->offset + bind->len; previous_bind = bind; }
/* * heap_deform_tuple * Given a tuple, extract data into values/isnull arrays; this is * the inverse of heap_form_tuple. * * Storage for the values/isnull arrays is provided by the caller; * it should be sized according to tupleDesc->natts not tuple->t_natts. * * Note that for pass-by-reference datatypes, the pointer placed * in the Datum will point into the given tuple. * * When all or most of a tuple's fields need to be extracted, * this routine will be significantly quicker than a loop around * heap_getattr; the loop will become O(N^2) as soon as any * noncacheable attribute offsets are involved. */ void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull) { HeapTupleHeader tup = tuple->t_data; bool hasnulls = HeapTupleHasNulls(tuple); Form_pg_attribute *att = tupleDesc->attrs; int tdesc_natts = tupleDesc->natts; int natts; /* number of atts to extract */ int attnum; char *tp; /* ptr to tuple data */ long off; /* offset in tuple data */ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ bool slow = false; /* can we use/set attcacheoff? */ Assert(!is_heaptuple_memtuple(tuple)); natts = HeapTupleHeaderGetNatts(tup); /* * In inheritance situations, it is possible that the given tuple actually * has more fields than the caller is expecting. Don't run off the end of * the caller's arrays. */ natts = Min(natts, tdesc_natts); tp = (char *) tup + tup->t_hoff; off = 0; for (attnum = 0; attnum < natts; attnum++) { Form_pg_attribute thisatt = att[attnum]; if (hasnulls && att_isnull(attnum, bp)) { values[attnum] = (Datum) 0; isnull[attnum] = true; slow = true; /* can't use attcacheoff anymore */ continue; } isnull[attnum] = false; if (!slow && thisatt->attcacheoff >= 0) off = thisatt->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 (thisatt->attlen != -1 || !tp[off]) off = att_align(off, thisatt->attalign); if (!slow && thisatt->attlen != -1) thisatt->attcacheoff = off; } if (!slow && thisatt->attlen < 0) slow = true; values[attnum] = fetchatt(thisatt, tp + off); #ifdef USE_ASSERT_CHECKING /* Ignore attributes with dropped types */ if (thisatt->attlen == -1 && !thisatt->attisdropped) { Assert(VARATT_IS_SHORT_D(values[attnum]) || !VARATT_COULD_SHORT_D(values[attnum]) || thisatt->atttypid == OIDVECTOROID || thisatt->atttypid == INT2VECTOROID || thisatt->atttypid >= FirstNormalObjectId); } #endif off = att_addlength(off, thisatt->attlen, PointerGetDatum(tp + off)); } /* * If tuple doesn't have all the atts indicated by tupleDesc, read the * rest as null */ for (; attnum < tdesc_natts; attnum++) { values[attnum] = (Datum) 0; isnull[attnum] = true; } }
/* ---------------- * 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); } }
/* * slot_deform_tuple * Given a TupleTableSlot, extract data from the slot's physical tuple * into its Datum/isnull arrays. Data is extracted up through the * natts'th column (caller must ensure this is a legal column number). * * This is essentially an incremental version of heap_deform_tuple: * on each call we extract attributes up to the one needed, without * re-computing information about previously extracted attributes. * slot->tts_nvalid is the number of attributes already extracted. */ static void slot_deform_tuple(TupleTableSlot *slot, int natts) { HeapTuple tuple = TupGetHeapTuple(slot); TupleDesc tupleDesc = slot->tts_tupleDescriptor; Datum *values = slot->PRIVATE_tts_values; bool *isnull = slot->PRIVATE_tts_isnull; HeapTupleHeader tup = tuple->t_data; bool hasnulls = HeapTupleHasNulls(tuple); Form_pg_attribute *att = tupleDesc->attrs; int attnum; char *tp; /* ptr to tuple data */ long off; /* offset in tuple data */ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ bool slow; /* can we use/set attcacheoff? */ /* * Check whether the first call for this tuple, and initialize or restore * loop state. */ attnum = slot->PRIVATE_tts_nvalid; if (attnum == 0) { /* Start from the first attribute */ off = 0; slow = false; } else { /* Restore state from previous execution */ off = slot->PRIVATE_tts_off; slow = slot->PRIVATE_tts_slow; } tp = (char *) tup + tup->t_hoff; for (; attnum < natts; attnum++) { Form_pg_attribute thisatt = att[attnum]; if (hasnulls && att_isnull(attnum, bp)) { values[attnum] = (Datum) 0; isnull[attnum] = true; slow = true; /* can't use attcacheoff anymore */ continue; } isnull[attnum] = false; if (!slow && thisatt->attcacheoff >= 0) off = thisatt->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 (thisatt->attlen != -1 || !tp[off]) off = att_align(off, thisatt->attalign); if (!slow && thisatt->attlen != -1) thisatt->attcacheoff = off; } if (!slow && thisatt->attlen < 0) slow = true; values[attnum] = fetchatt(thisatt, tp + off); off = att_addlength(off, thisatt->attlen, PointerGetDatum(tp + off)); } /* * Save state for next execution */ slot->PRIVATE_tts_nvalid = attnum; slot->PRIVATE_tts_off = off; slot->PRIVATE_tts_slow = slow; }