【标题】:修复I9HBFS所示的有脏数据导致unseable索引reindex失败的问题

【实现内容】: 修复I9HBFS所示的有脏数据导致unseable索引reindex失败的问题
【根因分析】: 因为openguass insert的时候会忽略掉unusable 索引,导致可以insert重复的数据,导致索引rebuild失败
【实现方案】: 参考pg的实现,insert数据的时候,把unseable索引也更新
【关联需求或issue】: https://gitee.com/opengauss/openGauss-server/issues/I9HBFS
This commit is contained in:
wangfeihuo
2024-04-25 11:06:15 +08:00
committed by yaoxin
parent 57e758a4b2
commit 64d364b082
8 changed files with 387 additions and 7 deletions

View File

@ -390,7 +390,8 @@ static const struct behavior_compat_entry behavior_compat_options[OPT_MAX] = {
{"select_into_return_null", OPT_SELECT_INTO_RETURN_NULL},
{"accept_empty_str", OPT_ACCEPT_EMPTY_STR},
{"plpgsql_dependency", OPT_PLPGSQL_DEPENDENCY},
{"proc_uncheck_default_param", OPT_PROC_UNCHECK_DEFAULT_PARAM}
{"proc_uncheck_default_param", OPT_PROC_UNCHECK_DEFAULT_PARAM},
{"update_unusable_unique_index_on_iud", OPT_UPDATE_UNUSABLE_UNIQUE_INDEX_ON_IUD}
};
// increase SQL_IGNORE_STRATEGY_NUM if we need more strategy

View File

@ -1184,6 +1184,82 @@ Partition ExecOpenScanParitition(EState* estate, Relation parent, PartitionIdent
return partitionOpen(parent, partoid, lockmode);
}
void ExecOpenUnusedIndices(ResultRelInfo* resultRelInfo, bool speculative)
{
Relation resultRelation = resultRelInfo->ri_RelationDesc;
List* indexoidlist = NIL;
ListCell* l = NULL;
int len, i;
RelationPtr relationDescs;
IndexInfo** indexInfoArray;
/* fast path if no indexes */
if (!RelationGetForm(resultRelation)->relhasindex)
return;
/*
* Get cached list of index OIDs
*/
indexoidlist = RelationGetIndexList(resultRelation, true);
len = list_length(indexoidlist);
if (len == 0) {
return;
}
/*
* allocate space for result arrays
*/
relationDescs = (RelationPtr)palloc(len * sizeof(Relation));
indexInfoArray = (IndexInfo**)palloc(len * sizeof(IndexInfo*));
resultRelInfo->ri_UnusableIndexRelationDescs = relationDescs;
resultRelInfo->ri_UnusableIndexRelationInfo = indexInfoArray;
/*
* For each index, open the index relation and save pg_index info. We
* acquire RowExclusiveLock, signifying we will update the index.
*
* Note: we do this even if the index is not IndexIsReady; it's not worth
* the trouble to optimize for the case where it isn't.
*/
i = 0;
foreach (l, indexoidlist) {
Oid indexOid = lfirst_oid(l);
Relation indexDesc;
IndexInfo* ii = NULL;
indexDesc = index_open(indexOid, RowExclusiveLock);
if (IndexIsUsable(indexDesc->rd_index) || !IndexIsUnique(indexDesc->rd_index)) {
index_close(indexDesc, RowExclusiveLock);
continue;
}
/* extract index key information from the index's pg_index info */
ii = BuildIndexInfo(indexDesc);
/*
* If the indexes are to be used for speculative insertion, add extra
* information required by unique index entries.
*/
if (speculative) {
BuildSpeculativeIndexInfo(indexDesc, ii);
}
relationDescs[i] = indexDesc;
indexInfoArray[i] = ii;
i++;
}
// remember to set the number of unusable indexes
resultRelInfo->ri_NumUnusableIndices = i;
if (resultRelInfo->ri_NumUnusableIndices == 0) {
ExecCloseUnsedIndices(resultRelInfo);
}
list_free_ext(indexoidlist);
}
/* ----------------------------------------------------------------
* ExecInsertIndexTuples support
* ----------------------------------------------------------------
@ -1209,6 +1285,10 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
resultRelInfo->ri_NumIndices = 0;
resultRelInfo->ri_ContainGPI = false;
resultRelInfo->ri_NumUnusableIndices = 0;
resultRelInfo->ri_UnusableIndexRelationDescs = NULL;
resultRelInfo->ri_UnusableIndexRelationInfo = NULL;
/* fast path if no indexes */
if (!RelationGetForm(resultRelation)->relhasindex)
@ -1275,9 +1355,38 @@ void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative)
// remember to set the number of usable indexes
resultRelInfo->ri_NumIndices = i;
if (UPDATE_UNUSABLE_UNIQUE_INDEX_ON_IUD) {
ExecOpenUnusedIndices(resultRelInfo, speculative);
}
list_free_ext(indexoidlist);
}
void ExecCloseUnsedIndices(ResultRelInfo* resultRelInfo)
{
int i;
int numIndices = resultRelInfo->ri_NumUnusableIndices;
RelationPtr indexDescs = resultRelInfo->ri_UnusableIndexRelationDescs;
for (i = 0; i < numIndices; i++) {
if (indexDescs[i] == NULL)
continue; /* shouldn't happen? */
/* Drop lock acquired by ExecOpenIndices */
index_close(indexDescs[i], RowExclusiveLock);
}
pfree_ext(resultRelInfo->ri_UnusableIndexRelationDescs);
if (resultRelInfo->ri_UnusableIndexRelationInfo) {
for (i = 0; i < numIndices; i++) {
pfree_ext(resultRelInfo->ri_UnusableIndexRelationInfo[i]);
}
pfree_ext(resultRelInfo->ri_UnusableIndexRelationInfo);
}
resultRelInfo->ri_UnusableIndexRelationDescs = NULL;
resultRelInfo->ri_UnusableIndexRelationInfo = NULL;
}
/* ----------------------------------------------------------------
* ExecCloseIndices
*
@ -1308,6 +1417,11 @@ void ExecCloseIndices(ResultRelInfo* resultRelInfo)
}
pfree_ext(resultRelInfo->ri_IndexRelationInfo);
}
if (UPDATE_UNUSABLE_UNIQUE_INDEX_ON_IUD && resultRelInfo->ri_NumUnusableIndices > 0) {
ExecCloseUnsedIndices(resultRelInfo);
}
}
/*
@ -1873,9 +1987,12 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
ResultRelInfo* resultRelInfo = NULL;
int i;
int numIndices;
int numUnusedIndices;
RelationPtr relationDescs;
RelationPtr unusedRelationDescs;
Relation heapRelation;
IndexInfo** indexInfoArray;
IndexInfo** unusedindexInfoArray;
ExprContext* econtext = NULL;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@ -1883,6 +2000,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
bool ispartitionedtable = false;
bool containGPI;
List* partitionIndexOidList = NIL;
int totalIndices;
/*
* Get information from the result relation info structure.
@ -1891,6 +2009,13 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
numIndices = resultRelInfo->ri_NumIndices;
relationDescs = resultRelInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
numUnusedIndices = resultRelInfo->ri_NumUnusableIndices;
unusedRelationDescs = resultRelInfo->ri_UnusableIndexRelationDescs;
unusedindexInfoArray = resultRelInfo->ri_UnusableIndexRelationInfo;
totalIndices = numIndices + numUnusedIndices;
heapRelation = resultRelInfo->ri_RelationDesc;
containGPI = resultRelInfo->ri_ContainGPI;
@ -1936,8 +2061,8 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
/*
* for each index, form and insert the index tuple
*/
for (i = 0; i < numIndices; i++) {
Relation indexRelation = relationDescs[i];
for (i = 0; i < totalIndices; i++) {
Relation indexRelation = i < numIndices ? relationDescs[i] : unusedRelationDescs[i - numIndices];
IndexInfo* indexInfo = NULL;
IndexUniqueCheck checkUnique;
bool satisfiesConstraint = false;
@ -1950,7 +2075,7 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e
continue;
}
indexInfo = indexInfoArray[i];
indexInfo = i < numIndices ? indexInfoArray[i] : unusedindexInfoArray[i - numIndices];
/* If the index is marked as read-only, ignore it */
if (!indexInfo->ii_ReadyForInserts) {

View File

@ -111,6 +111,7 @@ typedef FormData_pg_index *Form_pg_index;
* the less ugly representation used after 9.2.
*/
#define IndexIsUsable(indexForm) ((indexForm)->indisusable)
#define IndexIsUnique(indexForm) ((indexForm)->indisunique)
#define IndexIsReady(indexForm) ((indexForm)->indisready)
#define IndexIsValid(indexForm) (((indexForm)->indisvalid && (indexForm)->indisready) && ((indexForm)->indisusable))
#define IndexIsLive(indexForm) (((indexForm)->indisready || !(indexForm)->indisvalid) && ((indexForm)->indisusable))

View File

@ -643,6 +643,8 @@ static inline Datum autoinc2datum(ConstrAutoInc *cons_autoinc, int128 autoinc)
extern Partition ExecOpenScanParitition(
EState* estate, Relation parent, PartitionIdentifier* partID, LOCKMODE lockmode);
extern void ExecOpenUnusedIndices(ResultRelInfo* resultRelInfo, bool speculative);
extern void ExecCloseUnsedIndices(ResultRelInfo* resultRelInfo);
extern void ExecOpenIndices(ResultRelInfo* resultRelInfo, bool speculative);
extern void ExecCloseIndices(ResultRelInfo* resultRelInfo);
extern List* ExecInsertIndexTuples(

View File

@ -206,7 +206,8 @@ extern bool contain_backend_version(uint32 version_number);
#define OPT_ACCEPT_EMPTY_STR 134217728
#define OPT_PLPGSQL_DEPENDENCY 268435456
#define OPT_PROC_UNCHECK_DEFAULT_PARAM 536870912
#define OPT_MAX 30
#define OPT_UPDATE_UNUSABLE_UNIQUE_INDEX_ON_IUD 1073741824
#define OPT_MAX 31
#define PLPSQL_OPT_FOR_LOOP 1
#define PLPSQL_OPT_OUTPARAM 2
@ -250,6 +251,7 @@ extern bool contain_backend_version(uint32 version_number);
#define SELECT_INTO_RETURN_NULL (u_sess->utils_cxt.behavior_compat_flags & OPT_SELECT_INTO_RETURN_NULL)
#define PLPGSQL_DEPENDENCY (u_sess->utils_cxt.behavior_compat_flags & OPT_PLPGSQL_DEPENDENCY)
#define PROC_UNCHECK_DEFAULT_PARAM (u_sess->utils_cxt.behavior_compat_flags & OPT_PROC_UNCHECK_DEFAULT_PARAM)
#define UPDATE_UNUSABLE_UNIQUE_INDEX_ON_IUD (u_sess->utils_cxt.behavior_compat_flags & OPT_UPDATE_UNUSABLE_UNIQUE_INDEX_ON_IUD)
/* define database compatibility Attribute */
typedef struct {

View File

@ -593,6 +593,11 @@ typedef struct ResultRelInfo {
#ifdef USE_SPQ
AttrNumber ri_actionAttno; /* is this an INSERT or DELETE ? */
#endif
/* number of the unusable index*/
int ri_NumUnusableIndices;
RelationPtr ri_UnusableIndexRelationDescs;
IndexInfo** ri_UnusableIndexRelationInfo;
} ResultRelInfo;
/* bloom filter controller */

View File

@ -509,4 +509,131 @@ select * from test_drop_llt where a=40;
set enable_bitmapscan=on;
set enable_seqscan=on;
drop table if exists test_drop_llt;
-- End. Clean up
--astore
CREATE TABLE web_returns_p_a
(
sk_date INTEGER,
cm_num INTEGER,
nv_num INTEGER,
cn_name INTEGER
)
with (STORAGE_TYPE=ASTORE)
PARTITION BY RANGE(sk_date)
(
PARTITION P1 VALUES LESS THAN(1),
PARTITION P2 VALUES LESS THAN(2),
PARTITION P3 VALUES LESS THAN(3),
PARTITION P4 VALUES LESS THAN(4),
PARTITION P5 VALUES LESS THAN(5),
PARTITION P6 VALUES LESS THAN(6),
PARTITION P7 VALUES LESS THAN(7),
PARTITION P8 VALUES LESS THAN(8),
PARTITION P9 VALUES LESS THAN(9),
PARTITION Pmax VALUES LESS THAN(MAXVALUE)
);
insert into web_returns_p_a values (1,1,1,1);
insert into web_returns_p_a values (2,2,2,2);
insert into web_returns_p_a values (3,3,3,3);
insert into web_returns_p_a values (4,4,4,4);
insert into web_returns_p_a values (5,5,5,5);
insert into web_returns_p_a values (6,6,6,6);
insert into web_returns_p_a values (7,7,7,7);
insert into web_returns_p_a values (8,8,8,8);
insert into web_returns_p_a values (9,9,9,9);
create index idx_cm_num_a on web_returns_p_a(cm_num) global;
create index idx_nv_num_a on web_returns_p_a(nv_num) local;
create unique index idx_uq_a on web_returns_p_a(sk_date) global;
set behavior_compat_options = 'update_unusable_unique_index_on_iud';
alter table web_returns_p_a drop partition p2;
insert into web_returns_p_a values (1,1,1,1);
insert into web_returns_p_a values (1,1,1,1);
ERROR: duplicate key value violates unique constraint "idx_uq_a"
DETAIL: Key (sk_date)=(1) already exists.
update web_returns_p_a set sk_date = 1 where true;
ERROR: duplicate key value violates unique constraint "idx_uq_a"
DETAIL: Key (sk_date)=(1) already exists.
set enable_opfusion = off;
insert into web_returns_p_a values (1,1,1,1);
ERROR: duplicate key value violates unique constraint "idx_uq_a"
DETAIL: Key (sk_date)=(1) already exists.
insert into web_returns_p_a values (1,1,1,1);
ERROR: duplicate key value violates unique constraint "idx_uq_a"
DETAIL: Key (sk_date)=(1) already exists.
update web_returns_p_a set sk_date = 1 where true;
ERROR: duplicate key value violates unique constraint "idx_uq_a"
DETAIL: Key (sk_date)=(1) already exists.
set enable_opfusion = on;
alter index idx_uq_a rebuild;
alter table web_returns_p_a drop partition p3;
reset behavior_compat_options;
insert into web_returns_p_a values (2,2,2,2);
insert into web_returns_p_a values (2,2,2,2);
reset behavior_compat_options;
-- ustore
CREATE TABLE web_returns_p_u
(
sk_date INTEGER,
cm_num INTEGER,
nv_num INTEGER,
cn_name INTEGER
)
with (STORAGE_TYPE=ASTORE)
PARTITION BY RANGE(sk_date)
(
PARTITION P1 VALUES LESS THAN(1),
PARTITION P2 VALUES LESS THAN(2),
PARTITION P3 VALUES LESS THAN(3),
PARTITION P4 VALUES LESS THAN(4),
PARTITION P5 VALUES LESS THAN(5),
PARTITION P6 VALUES LESS THAN(6),
PARTITION P7 VALUES LESS THAN(7),
PARTITION P8 VALUES LESS THAN(8),
PARTITION P9 VALUES LESS THAN(9),
PARTITION Pmax VALUES LESS THAN(MAXVALUE)
);
insert into web_returns_p_u values (1,1,1,1);
insert into web_returns_p_u values (2,2,2,2);
insert into web_returns_p_u values (3,3,3,3);
insert into web_returns_p_u values (4,4,4,4);
insert into web_returns_p_u values (5,5,5,5);
insert into web_returns_p_u values (6,6,6,6);
insert into web_returns_p_u values (7,7,7,7);
insert into web_returns_p_u values (8,8,8,8);
insert into web_returns_p_u values (9,9,9,9);
create index idx_cm_num_u on web_returns_p_u(cm_num) global;
create index idx_nv_num_u on web_returns_p_u(nv_num) local;
create unique index idx_uq_u on web_returns_p_u(sk_date) global;
set behavior_compat_options = 'update_unusable_unique_index_on_iud';
alter table web_returns_p_u drop partition p2;
insert into web_returns_p_u values (1,1,1,1);
insert into web_returns_p_u values (1,1,1,1);
ERROR: duplicate key value violates unique constraint "idx_uq_u"
DETAIL: Key (sk_date)=(1) already exists.
update web_returns_p_u set sk_date = 1 where true;
ERROR: duplicate key value violates unique constraint "idx_uq_u"
DETAIL: Key (sk_date)=(1) already exists.
set enable_opfusion = off;
insert into web_returns_p_u values (1,1,1,1);
ERROR: duplicate key value violates unique constraint "idx_uq_u"
DETAIL: Key (sk_date)=(1) already exists.
insert into web_returns_p_u values (1,1,1,1);
ERROR: duplicate key value violates unique constraint "idx_uq_u"
DETAIL: Key (sk_date)=(1) already exists.
update web_returns_p_u set sk_date = 1 where true;
ERROR: duplicate key value violates unique constraint "idx_uq_u"
DETAIL: Key (sk_date)=(1) already exists.
set enable_opfusion = on;
alter index idx_uq_u rebuild;
alter table web_returns_p_u drop partition p3;
reset behavior_compat_options;
insert into web_returns_p_u values (2,2,2,2);
insert into web_returns_p_u values (2,2,2,2);
drop index idx_cm_num_a;
drop index idx_nv_num_a;
drop index idx_uq_a;
drop table web_returns_p_a;
drop index idx_cm_num_u;
drop index idx_nv_num_u;
drop index idx_uq_u;
drop table web_returns_p_u;
-- End. Clean u

View File

@ -223,4 +223,121 @@ set enable_bitmapscan=on;
set enable_seqscan=on;
drop table if exists test_drop_llt;
-- End. Clean up
--astore
CREATE TABLE web_returns_p_a
(
sk_date INTEGER,
cm_num INTEGER,
nv_num INTEGER,
cn_name INTEGER
)
with (STORAGE_TYPE=ASTORE)
PARTITION BY RANGE(sk_date)
(
PARTITION P1 VALUES LESS THAN(1),
PARTITION P2 VALUES LESS THAN(2),
PARTITION P3 VALUES LESS THAN(3),
PARTITION P4 VALUES LESS THAN(4),
PARTITION P5 VALUES LESS THAN(5),
PARTITION P6 VALUES LESS THAN(6),
PARTITION P7 VALUES LESS THAN(7),
PARTITION P8 VALUES LESS THAN(8),
PARTITION P9 VALUES LESS THAN(9),
PARTITION Pmax VALUES LESS THAN(MAXVALUE)
);
insert into web_returns_p_a values (1,1,1,1);
insert into web_returns_p_a values (2,2,2,2);
insert into web_returns_p_a values (3,3,3,3);
insert into web_returns_p_a values (4,4,4,4);
insert into web_returns_p_a values (5,5,5,5);
insert into web_returns_p_a values (6,6,6,6);
insert into web_returns_p_a values (7,7,7,7);
insert into web_returns_p_a values (8,8,8,8);
insert into web_returns_p_a values (9,9,9,9);
create index idx_cm_num_a on web_returns_p_a(cm_num) global;
create index idx_nv_num_a on web_returns_p_a(nv_num) local;
create unique index idx_uq_a on web_returns_p_a(sk_date) global;
set behavior_compat_options = 'update_unusable_unique_index_on_iud';
alter table web_returns_p_a drop partition p2;
insert into web_returns_p_a values (1,1,1,1);
insert into web_returns_p_a values (1,1,1,1);
update web_returns_p_a set sk_date = 1 where true;
set enable_opfusion = off;
insert into web_returns_p_a values (1,1,1,1);
insert into web_returns_p_a values (1,1,1,1);
update web_returns_p_a set sk_date = 1 where true;
set enable_opfusion = on;
alter index idx_uq_a rebuild;
alter table web_returns_p_a drop partition p3;
reset behavior_compat_options;
insert into web_returns_p_a values (2,2,2,2);
insert into web_returns_p_a values (2,2,2,2);
reset behavior_compat_options;
-- ustore
CREATE TABLE web_returns_p_u
(
sk_date INTEGER,
cm_num INTEGER,
nv_num INTEGER,
cn_name INTEGER
)
with (STORAGE_TYPE=ASTORE)
PARTITION BY RANGE(sk_date)
(
PARTITION P1 VALUES LESS THAN(1),
PARTITION P2 VALUES LESS THAN(2),
PARTITION P3 VALUES LESS THAN(3),
PARTITION P4 VALUES LESS THAN(4),
PARTITION P5 VALUES LESS THAN(5),
PARTITION P6 VALUES LESS THAN(6),
PARTITION P7 VALUES LESS THAN(7),
PARTITION P8 VALUES LESS THAN(8),
PARTITION P9 VALUES LESS THAN(9),
PARTITION Pmax VALUES LESS THAN(MAXVALUE)
);
insert into web_returns_p_u values (1,1,1,1);
insert into web_returns_p_u values (2,2,2,2);
insert into web_returns_p_u values (3,3,3,3);
insert into web_returns_p_u values (4,4,4,4);
insert into web_returns_p_u values (5,5,5,5);
insert into web_returns_p_u values (6,6,6,6);
insert into web_returns_p_u values (7,7,7,7);
insert into web_returns_p_u values (8,8,8,8);
insert into web_returns_p_u values (9,9,9,9);
create index idx_cm_num_u on web_returns_p_u(cm_num) global;
create index idx_nv_num_u on web_returns_p_u(nv_num) local;
create unique index idx_uq_u on web_returns_p_u(sk_date) global;
set behavior_compat_options = 'update_unusable_unique_index_on_iud';
alter table web_returns_p_u drop partition p2;
insert into web_returns_p_u values (1,1,1,1);
insert into web_returns_p_u values (1,1,1,1);
update web_returns_p_u set sk_date = 1 where true;
set enable_opfusion = off;
insert into web_returns_p_u values (1,1,1,1);
insert into web_returns_p_u values (1,1,1,1);
update web_returns_p_u set sk_date = 1 where true;
set enable_opfusion = on;
alter index idx_uq_u rebuild;
alter table web_returns_p_u drop partition p3;
reset behavior_compat_options;
insert into web_returns_p_u values (2,2,2,2);
insert into web_returns_p_u values (2,2,2,2);
drop index idx_cm_num_a;
drop index idx_nv_num_a;
drop index idx_uq_a;
drop table web_returns_p_a;
drop index idx_cm_num_u;
drop index idx_nv_num_u;
drop index idx_uq_u;
drop table web_returns_p_u;
-- End. Clean u