From e75d99818dd3940be997520e64db8c9e3b207e39 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 21 May 2024 07:05:26 +0200 Subject: Merging upstream version 15.7. Signed-off-by: Daniel Baumann --- contrib/amcheck/expected/check_btree.out | 23 +++++++++++++++ contrib/amcheck/sql/check_btree.sql | 17 +++++++++++ contrib/amcheck/verify_nbtree.c | 49 ++++++++++++++++++++++++++++---- 3 files changed, 84 insertions(+), 5 deletions(-) (limited to 'contrib/amcheck') diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out index 38791bb..2acbc98 100644 --- a/contrib/amcheck/expected/check_btree.out +++ b/contrib/amcheck/expected/check_btree.out @@ -199,6 +199,28 @@ SELECT bt_index_check('bttest_a_expr_idx', true); (1 row) +-- Check support of both 1B and 4B header sizes of short varlena datum +CREATE TABLE varlena_bug (v text); +ALTER TABLE varlena_bug ALTER column v SET storage plain; +INSERT INTO varlena_bug VALUES ('x'); +COPY varlena_bug from stdin; +CREATE INDEX varlena_bug_idx on varlena_bug(v); +SELECT bt_index_check('varlena_bug_idx', true); + bt_index_check +---------------- + +(1 row) + +-- Also check that we compress varlena values, which were previously stored +-- uncompressed in index. +INSERT INTO varlena_bug VALUES (repeat('Test', 250)); +ALTER TABLE varlena_bug ALTER COLUMN v SET STORAGE extended; +SELECT bt_index_check('varlena_bug_idx', true); + bt_index_check +---------------- + +(1 row) + -- cleanup DROP TABLE bttest_a; DROP TABLE bttest_b; @@ -208,3 +230,4 @@ DROP TABLE toast_bug; DROP FUNCTION ifun(int8); DROP OWNED BY regress_bttest_role; -- permissions DROP ROLE regress_bttest_role; +DROP TABLE varlena_bug; diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql index 033c04b..e2f47fc 100644 --- a/contrib/amcheck/sql/check_btree.sql +++ b/contrib/amcheck/sql/check_btree.sql @@ -135,6 +135,22 @@ CREATE INDEX bttest_a_expr_idx ON bttest_a ((ifun(id) + ifun(0))) SELECT bt_index_check('bttest_a_expr_idx', true); +-- Check support of both 1B and 4B header sizes of short varlena datum +CREATE TABLE varlena_bug (v text); +ALTER TABLE varlena_bug ALTER column v SET storage plain; +INSERT INTO varlena_bug VALUES ('x'); +COPY varlena_bug from stdin; +x +\. +CREATE INDEX varlena_bug_idx on varlena_bug(v); +SELECT bt_index_check('varlena_bug_idx', true); + +-- Also check that we compress varlena values, which were previously stored +-- uncompressed in index. +INSERT INTO varlena_bug VALUES (repeat('Test', 250)); +ALTER TABLE varlena_bug ALTER COLUMN v SET STORAGE extended; +SELECT bt_index_check('varlena_bug_idx', true); + -- cleanup DROP TABLE bttest_a; DROP TABLE bttest_b; @@ -144,3 +160,4 @@ DROP TABLE toast_bug; DROP FUNCTION ifun(int8); DROP OWNED BY regress_bttest_role; -- permissions DROP ROLE regress_bttest_role; +DROP TABLE varlena_bug; diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index 1886bd2..c08f19f 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -23,6 +23,7 @@ */ #include "postgres.h" +#include "access/heaptoast.h" #include "access/htup_details.h" #include "access/nbtree.h" #include "access/table.h" @@ -2641,7 +2642,7 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup) TupleDesc tupleDescriptor = RelationGetDescr(state->rel); Datum normalized[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - bool toast_free[INDEX_MAX_KEYS]; + bool need_free[INDEX_MAX_KEYS]; bool formnewtup = false; IndexTuple reformed; int i; @@ -2660,7 +2661,7 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup) att = TupleDescAttr(tupleDescriptor, i); /* Assume untoasted/already normalized datum initially */ - toast_free[i] = false; + need_free[i] = false; normalized[i] = index_getattr(itup, att->attnum, tupleDescriptor, &isnull[i]); @@ -2679,15 +2680,48 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup) ItemPointerGetBlockNumber(&(itup->t_tid)), ItemPointerGetOffsetNumber(&(itup->t_tid)), RelationGetRelationName(state->rel)))); + else if (!VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])) && + VARSIZE(DatumGetPointer(normalized[i])) > TOAST_INDEX_TARGET && + (att->attstorage == TYPSTORAGE_EXTENDED || + att->attstorage == TYPSTORAGE_MAIN)) + { + /* + * This value will be compressed by index_form_tuple() with the + * current storage settings. We may be here because this tuple + * was formed with different storage settings. So, force forming. + */ + formnewtup = true; + } else if (VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i]))) { formnewtup = true; normalized[i] = PointerGetDatum(PG_DETOAST_DATUM(normalized[i])); - toast_free[i] = true; + need_free[i] = true; + } + + /* + * Short tuples may have 1B or 4B header. Convert 4B header of short + * tuples to 1B + */ + else if (VARATT_CAN_MAKE_SHORT(DatumGetPointer(normalized[i]))) + { + /* convert to short varlena */ + Size len = VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(normalized[i])); + char *data = palloc(len); + + SET_VARSIZE_SHORT(data, len); + memcpy(data + 1, VARDATA(DatumGetPointer(normalized[i])), len - 1); + + formnewtup = true; + normalized[i] = PointerGetDatum(data); + need_free[i] = true; } } - /* Easier case: Tuple has varlena datums, none of which are compressed */ + /* + * Easier case: Tuple has varlena datums, none of which are compressed or + * short with 4B header + */ if (!formnewtup) return itup; @@ -2697,6 +2731,11 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup) * (normalized input datums). This is rather naive, but shouldn't be * necessary too often. * + * In the heap, tuples may contain short varlena datums with both 1B + * header and 4B headers. But the corresponding index tuple should always + * have such varlena's with 1B headers. So, if there is a short varlena + * with 4B header, we need to convert it for for fingerprinting. + * * Note that we rely on deterministic index_form_tuple() TOAST compression * of normalized input. */ @@ -2705,7 +2744,7 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup) /* Cannot leak memory here */ for (i = 0; i < tupleDescriptor->natts; i++) - if (toast_free[i]) + if (need_free[i]) pfree(DatumGetPointer(normalized[i])); return reformed; -- cgit v1.2.3