Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 85edb1f

Browse files
committed
Preserve pg_attribute.attstattarget across REINDEX CONCURRENTLY
For an index, attstattarget can be updated using ALTER INDEX SET STATISTICS. This data was lost on the new index after REINDEX CONCURRENTLY. The update of this field is done when the old and new indexes are swapped to make the fix back-patchable. Another approach we could look after in the long-term is to change index_create() to pass the wanted values of attstattarget when creating the new relation, but, as this would cause an ABI breakage this can be done only on HEAD. Reported-by: Ronan Dunklau Author: Michael Paquier Reviewed-by: Ronan Dunklau, Tomas Vondra Discussion: https://postgr.es/m/16628084.uLZWGnKmhe@laptop-ronand Backpatch-through: 12
1 parent 1b9eb7c commit 85edb1f

File tree

5 files changed

+107
-0
lines changed

5 files changed

+107
-0
lines changed

src/backend/catalog/index.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,6 +1717,62 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
17171717
/* Copy data of pg_statistic from the old index to the new one */
17181718
CopyStatistics(oldIndexId, newIndexId);
17191719

1720+
/* Copy pg_attribute.attstattarget for each index attribute */
1721+
{
1722+
HeapTuple attrTuple;
1723+
Relation pg_attribute;
1724+
SysScanDesc scan;
1725+
ScanKeyData key[1];
1726+
1727+
pg_attribute = table_open(AttributeRelationId, RowExclusiveLock);
1728+
ScanKeyInit(&key[0],
1729+
Anum_pg_attribute_attrelid,
1730+
BTEqualStrategyNumber, F_OIDEQ,
1731+
ObjectIdGetDatum(newIndexId));
1732+
scan = systable_beginscan(pg_attribute, AttributeRelidNumIndexId,
1733+
true, NULL, 1, key);
1734+
1735+
while (HeapTupleIsValid((attrTuple = systable_getnext(scan))))
1736+
{
1737+
Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attrTuple);
1738+
Datum repl_val[Natts_pg_attribute];
1739+
bool repl_null[Natts_pg_attribute];
1740+
bool repl_repl[Natts_pg_attribute];
1741+
int attstattarget;
1742+
HeapTuple newTuple;
1743+
1744+
/* Ignore dropped columns */
1745+
if (att->attisdropped)
1746+
continue;
1747+
1748+
/*
1749+
* Get attstattarget from the old index and refresh the new value.
1750+
*/
1751+
attstattarget = get_attstattarget(oldIndexId, att->attnum);
1752+
1753+
/* no need for a refresh if both match */
1754+
if (attstattarget == att->attstattarget)
1755+
continue;
1756+
1757+
memset(repl_val, 0, sizeof(repl_val));
1758+
memset(repl_null, false, sizeof(repl_null));
1759+
memset(repl_repl, false, sizeof(repl_repl));
1760+
1761+
repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
1762+
repl_val[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(attstattarget);
1763+
1764+
newTuple = heap_modify_tuple(attrTuple,
1765+
RelationGetDescr(pg_attribute),
1766+
repl_val, repl_null, repl_repl);
1767+
CatalogTupleUpdate(pg_attribute, &newTuple->t_self, newTuple);
1768+
1769+
heap_freetuple(newTuple);
1770+
}
1771+
1772+
systable_endscan(scan);
1773+
table_close(pg_attribute, RowExclusiveLock);
1774+
}
1775+
17201776
/* Close relations */
17211777
table_close(pg_class, RowExclusiveLock);
17221778
table_close(pg_index, RowExclusiveLock);

src/backend/utils/cache/lsyscache.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,33 @@ get_attnum(Oid relid, const char *attname)
821821
return InvalidAttrNumber;
822822
}
823823

824+
/*
825+
* get_attstattarget
826+
*
827+
* Given the relation id and the attribute number,
828+
* return the "attstattarget" field from the attribute relation.
829+
*
830+
* Errors if not found.
831+
*/
832+
int
833+
get_attstattarget(Oid relid, AttrNumber attnum)
834+
{
835+
HeapTuple tp;
836+
Form_pg_attribute att_tup;
837+
int result;
838+
839+
tp = SearchSysCache2(ATTNUM,
840+
ObjectIdGetDatum(relid),
841+
Int16GetDatum(attnum));
842+
if (!HeapTupleIsValid(tp))
843+
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
844+
attnum, relid);
845+
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
846+
result = att_tup->attstattarget;
847+
ReleaseSysCache(tp);
848+
return result;
849+
}
850+
824851
/*
825852
* get_attgenerated
826853
*

src/include/utils/lsyscache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ extern Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype,
8686
int16 procnum);
8787
extern char *get_attname(Oid relid, AttrNumber attnum, bool missing_ok);
8888
extern AttrNumber get_attnum(Oid relid, const char *attname);
89+
extern int get_attstattarget(Oid relid, AttrNumber attnum);
8990
extern char get_attgenerated(Oid relid, AttrNumber attnum);
9091
extern Oid get_atttype(Oid relid, AttrNumber attnum);
9192
extern void get_atttypetypmodcoll(Oid relid, AttrNumber attnum,

src/test/regress/expected/create_index.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,6 +2406,7 @@ CREATE UNIQUE INDEX concur_exprs_index_pred ON concur_exprs_tab (c1)
24062406
CREATE UNIQUE INDEX concur_exprs_index_pred_2
24072407
ON concur_exprs_tab ((1 / c1))
24082408
WHERE ('-H') >= (c2::TEXT) COLLATE "C";
2409+
ALTER INDEX concur_exprs_index_expr ALTER COLUMN 1 SET STATISTICS 100;
24092410
ANALYZE concur_exprs_tab;
24102411
SELECT starelid::regclass, count(*) FROM pg_statistic WHERE starelid IN (
24112412
'concur_exprs_index_expr'::regclass,
@@ -2485,6 +2486,20 @@ SELECT starelid::regclass, count(*) FROM pg_statistic WHERE starelid IN (
24852486
concur_exprs_index_expr | 1
24862487
(1 row)
24872488

2489+
-- attstattarget should remain intact
2490+
SELECT attrelid::regclass, attnum, attstattarget
2491+
FROM pg_attribute WHERE attrelid IN (
2492+
'concur_exprs_index_expr'::regclass,
2493+
'concur_exprs_index_pred'::regclass,
2494+
'concur_exprs_index_pred_2'::regclass)
2495+
ORDER BY 'concur_exprs_index_expr'::regclass::text, attnum;
2496+
attrelid | attnum | attstattarget
2497+
---------------------------+--------+---------------
2498+
concur_exprs_index_expr | 1 | 100
2499+
concur_exprs_index_pred | 1 | -1
2500+
concur_exprs_index_pred_2 | 1 | -1
2501+
(3 rows)
2502+
24882503
DROP TABLE concur_exprs_tab;
24892504
-- Temporary tables and on-commit actions, where CONCURRENTLY is ignored.
24902505
-- ON COMMIT PRESERVE ROWS, the default.

src/test/regress/sql/create_index.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,7 @@ CREATE UNIQUE INDEX concur_exprs_index_pred ON concur_exprs_tab (c1)
997997
CREATE UNIQUE INDEX concur_exprs_index_pred_2
998998
ON concur_exprs_tab ((1 / c1))
999999
WHERE ('-H') >= (c2::TEXT) COLLATE "C";
1000+
ALTER INDEX concur_exprs_index_expr ALTER COLUMN 1 SET STATISTICS 100;
10001001
ANALYZE concur_exprs_tab;
10011002
SELECT starelid::regclass, count(*) FROM pg_statistic WHERE starelid IN (
10021003
'concur_exprs_index_expr'::regclass,
@@ -1021,6 +1022,13 @@ SELECT starelid::regclass, count(*) FROM pg_statistic WHERE starelid IN (
10211022
'concur_exprs_index_pred'::regclass,
10221023
'concur_exprs_index_pred_2'::regclass)
10231024
GROUP BY starelid ORDER BY starelid::regclass::text;
1025+
-- attstattarget should remain intact
1026+
SELECT attrelid::regclass, attnum, attstattarget
1027+
FROM pg_attribute WHERE attrelid IN (
1028+
'concur_exprs_index_expr'::regclass,
1029+
'concur_exprs_index_pred'::regclass,
1030+
'concur_exprs_index_pred_2'::regclass)
1031+
ORDER BY 'concur_exprs_index_expr'::regclass::text, attnum;
10241032
DROP TABLE concur_exprs_tab;
10251033

10261034
-- Temporary tables and on-commit actions, where CONCURRENTLY is ignored.

0 commit comments

Comments
 (0)