Skip to content

Commit 30651d7

Browse files
reshkeCommitfest Bot
authored andcommitted
Move normalize tuple logic from nbtcheck to verify_common
Preparational patch to reuse index tuple normalize logic in GiST amcheck.
1 parent 2648eab commit 30651d7

File tree

3 files changed

+115
-106
lines changed

3 files changed

+115
-106
lines changed

contrib/amcheck/verify_common.c

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "postgres.h"
1414

1515
#include "access/genam.h"
16+
#include "access/heaptoast.h"
1617
#include "access/table.h"
1718
#include "access/tableam.h"
1819
#include "verify_common.h"
@@ -189,3 +190,114 @@ index_checkable(Relation rel, Oid am_id)
189190

190191
return amcheck_index_mainfork_expected(rel);
191192
}
193+
194+
IndexTuple
195+
amcheck_normalize_tuple(Relation irel, IndexTuple itup)
196+
{
197+
TupleDesc tupleDescriptor = RelationGetDescr(irel);
198+
Datum normalized[INDEX_MAX_KEYS];
199+
bool isnull[INDEX_MAX_KEYS];
200+
bool need_free[INDEX_MAX_KEYS];
201+
bool formnewtup = false;
202+
IndexTuple reformed;
203+
int i;
204+
205+
/* Easy case: It's immediately clear that tuple has no varlena datums */
206+
if (!IndexTupleHasVarwidths(itup))
207+
return itup;
208+
209+
for (i = 0; i < tupleDescriptor->natts; i++)
210+
{
211+
Form_pg_attribute att;
212+
213+
att = TupleDescAttr(tupleDescriptor, i);
214+
215+
/* Assume untoasted/already normalized datum initially */
216+
need_free[i] = false;
217+
normalized[i] = index_getattr(itup, att->attnum,
218+
tupleDescriptor,
219+
&isnull[i]);
220+
if (att->attbyval || att->attlen != -1 || isnull[i])
221+
continue;
222+
223+
/*
224+
* Callers always pass a tuple that could safely be inserted into the
225+
* index without further processing, so an external varlena header
226+
* should never be encountered here
227+
*/
228+
if (VARATT_IS_EXTERNAL(DatumGetPointer(normalized[i])))
229+
ereport(ERROR,
230+
(errcode(ERRCODE_INDEX_CORRUPTED),
231+
errmsg("external varlena datum in tuple that references heap row (%u,%u) in index \"%s\"",
232+
ItemPointerGetBlockNumber(&(itup->t_tid)),
233+
ItemPointerGetOffsetNumber(&(itup->t_tid)),
234+
RelationGetRelationName(irel))));
235+
else if (!VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])) &&
236+
VARSIZE(DatumGetPointer(normalized[i])) > TOAST_INDEX_TARGET &&
237+
(att->attstorage == TYPSTORAGE_EXTENDED ||
238+
att->attstorage == TYPSTORAGE_MAIN))
239+
{
240+
/*
241+
* This value will be compressed by index_form_tuple() with the
242+
* current storage settings. We may be here because this tuple
243+
* was formed with different storage settings. So, force forming.
244+
*/
245+
formnewtup = true;
246+
}
247+
else if (VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])))
248+
{
249+
formnewtup = true;
250+
normalized[i] = PointerGetDatum(PG_DETOAST_DATUM(normalized[i]));
251+
need_free[i] = true;
252+
}
253+
254+
/*
255+
* Short tuples may have 1B or 4B header. Convert 4B header of short
256+
* tuples to 1B
257+
*/
258+
else if (VARATT_CAN_MAKE_SHORT(DatumGetPointer(normalized[i])))
259+
{
260+
/* convert to short varlena */
261+
Size len = VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(normalized[i]));
262+
char *data = palloc(len);
263+
264+
SET_VARSIZE_SHORT(data, len);
265+
memcpy(data + 1, VARDATA(DatumGetPointer(normalized[i])), len - 1);
266+
267+
formnewtup = true;
268+
normalized[i] = PointerGetDatum(data);
269+
need_free[i] = true;
270+
}
271+
}
272+
273+
/*
274+
* Easier case: Tuple has varlena datums, none of which are compressed or
275+
* short with 4B header
276+
*/
277+
if (!formnewtup)
278+
return itup;
279+
280+
/*
281+
* Hard case: Tuple had compressed varlena datums that necessitate
282+
* creating normalized version of the tuple from uncompressed input datums
283+
* (normalized input datums). This is rather naive, but shouldn't be
284+
* necessary too often.
285+
*
286+
* In the heap, tuples may contain short varlena datums with both 1B
287+
* header and 4B headers. But the corresponding index tuple should always
288+
* have such varlena's with 1B headers. So, if there is a short varlena
289+
* with 4B header, we need to convert it for fingerprinting.
290+
*
291+
* Note that we rely on deterministic index_form_tuple() TOAST compression
292+
* of normalized input.
293+
*/
294+
reformed = index_form_tuple(tupleDescriptor, normalized, isnull);
295+
reformed->t_tid = itup->t_tid;
296+
297+
/* Cannot leak memory here */
298+
for (i = 0; i < tupleDescriptor->natts; i++)
299+
if (need_free[i])
300+
pfree(DatumGetPointer(normalized[i]));
301+
302+
return reformed;
303+
}

contrib/amcheck/verify_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,5 @@ extern void amcheck_lock_relation_and_check(Oid indrelid,
2626
Oid am_id,
2727
IndexDoCheckCallback check,
2828
LOCKMODE lockmode, void *state);
29+
30+
extern IndexTuple amcheck_normalize_tuple(Relation irel, IndexTuple itup);

contrib/amcheck/verify_nbtree.c

Lines changed: 1 addition & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,115 +2859,10 @@ bt_tuple_present_callback(Relation index, ItemPointer tid, Datum *values,
28592859
static IndexTuple
28602860
bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup)
28612861
{
2862-
TupleDesc tupleDescriptor = RelationGetDescr(state->rel);
2863-
Datum normalized[INDEX_MAX_KEYS];
2864-
bool isnull[INDEX_MAX_KEYS];
2865-
bool need_free[INDEX_MAX_KEYS];
2866-
bool formnewtup = false;
2867-
IndexTuple reformed;
2868-
int i;
2869-
28702862
/* Caller should only pass "logical" non-pivot tuples here */
28712863
Assert(!BTreeTupleIsPosting(itup) && !BTreeTupleIsPivot(itup));
28722864

2873-
/* Easy case: It's immediately clear that tuple has no varlena datums */
2874-
if (!IndexTupleHasVarwidths(itup))
2875-
return itup;
2876-
2877-
for (i = 0; i < tupleDescriptor->natts; i++)
2878-
{
2879-
Form_pg_attribute att;
2880-
2881-
att = TupleDescAttr(tupleDescriptor, i);
2882-
2883-
/* Assume untoasted/already normalized datum initially */
2884-
need_free[i] = false;
2885-
normalized[i] = index_getattr(itup, att->attnum,
2886-
tupleDescriptor,
2887-
&isnull[i]);
2888-
if (att->attbyval || att->attlen != -1 || isnull[i])
2889-
continue;
2890-
2891-
/*
2892-
* Callers always pass a tuple that could safely be inserted into the
2893-
* index without further processing, so an external varlena header
2894-
* should never be encountered here
2895-
*/
2896-
if (VARATT_IS_EXTERNAL(DatumGetPointer(normalized[i])))
2897-
ereport(ERROR,
2898-
(errcode(ERRCODE_INDEX_CORRUPTED),
2899-
errmsg("external varlena datum in tuple that references heap row (%u,%u) in index \"%s\"",
2900-
ItemPointerGetBlockNumber(&(itup->t_tid)),
2901-
ItemPointerGetOffsetNumber(&(itup->t_tid)),
2902-
RelationGetRelationName(state->rel))));
2903-
else if (!VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])) &&
2904-
VARSIZE(DatumGetPointer(normalized[i])) > TOAST_INDEX_TARGET &&
2905-
(att->attstorage == TYPSTORAGE_EXTENDED ||
2906-
att->attstorage == TYPSTORAGE_MAIN))
2907-
{
2908-
/*
2909-
* This value will be compressed by index_form_tuple() with the
2910-
* current storage settings. We may be here because this tuple
2911-
* was formed with different storage settings. So, force forming.
2912-
*/
2913-
formnewtup = true;
2914-
}
2915-
else if (VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])))
2916-
{
2917-
formnewtup = true;
2918-
normalized[i] = PointerGetDatum(PG_DETOAST_DATUM(normalized[i]));
2919-
need_free[i] = true;
2920-
}
2921-
2922-
/*
2923-
* Short tuples may have 1B or 4B header. Convert 4B header of short
2924-
* tuples to 1B
2925-
*/
2926-
else if (VARATT_CAN_MAKE_SHORT(DatumGetPointer(normalized[i])))
2927-
{
2928-
/* convert to short varlena */
2929-
Size len = VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(normalized[i]));
2930-
char *data = palloc(len);
2931-
2932-
SET_VARSIZE_SHORT(data, len);
2933-
memcpy(data + 1, VARDATA(DatumGetPointer(normalized[i])), len - 1);
2934-
2935-
formnewtup = true;
2936-
normalized[i] = PointerGetDatum(data);
2937-
need_free[i] = true;
2938-
}
2939-
}
2940-
2941-
/*
2942-
* Easier case: Tuple has varlena datums, none of which are compressed or
2943-
* short with 4B header
2944-
*/
2945-
if (!formnewtup)
2946-
return itup;
2947-
2948-
/*
2949-
* Hard case: Tuple had compressed varlena datums that necessitate
2950-
* creating normalized version of the tuple from uncompressed input datums
2951-
* (normalized input datums). This is rather naive, but shouldn't be
2952-
* necessary too often.
2953-
*
2954-
* In the heap, tuples may contain short varlena datums with both 1B
2955-
* header and 4B headers. But the corresponding index tuple should always
2956-
* have such varlena's with 1B headers. So, if there is a short varlena
2957-
* with 4B header, we need to convert it for fingerprinting.
2958-
*
2959-
* Note that we rely on deterministic index_form_tuple() TOAST compression
2960-
* of normalized input.
2961-
*/
2962-
reformed = index_form_tuple(tupleDescriptor, normalized, isnull);
2963-
reformed->t_tid = itup->t_tid;
2964-
2965-
/* Cannot leak memory here */
2966-
for (i = 0; i < tupleDescriptor->natts; i++)
2967-
if (need_free[i])
2968-
pfree(DatumGetPointer(normalized[i]));
2969-
2970-
return reformed;
2865+
return amcheck_normalize_tuple(state->rel, itup);
29712866
}
29722867

29732868
/*

0 commit comments

Comments
 (0)