Skip to content

Commit 9e380ab

Browse files
committed
Support more restricted predicate in ALTER INDEX clause
1 parent 0f246eb commit 9e380ab

File tree

1 file changed

+92
-59
lines changed

1 file changed

+92
-59
lines changed

src/backend/commands/indexcmds.c

Lines changed: 92 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,30 @@ CheckIndexCompatible(Oid oldId,
279279
return ret;
280280
}
281281

282+
static void
283+
UpdateIndex(Oid indexRelationId, Node* whereClause)
284+
{
285+
Datum values[Natts_pg_index];
286+
bool isnull[Natts_pg_index];
287+
HeapTuple oldTuple;
288+
HeapTuple newTuple;
289+
Relation pg_index;
290+
291+
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
292+
oldTuple = SearchSysCacheCopy1(INDEXRELID, ObjectIdGetDatum(indexRelationId));
293+
if (!HeapTupleIsValid(oldTuple))
294+
elog(ERROR, "cache lookup failed for index %u", indexRelationId);
295+
296+
heap_deform_tuple(oldTuple, RelationGetDescr(pg_index), values, isnull);
297+
values[Anum_pg_index_indpred - 1] = CStringGetTextDatum(nodeToString(whereClause));
298+
newTuple = heap_form_tuple(RelationGetDescr(pg_index), values, isnull);
299+
simple_heap_update(pg_index, &oldTuple->t_self, newTuple);
300+
CatalogUpdateIndexes(pg_index, newTuple);
301+
heap_freetuple(newTuple);
302+
heap_freetuple(oldTuple);
303+
heap_close(pg_index, NoLock);
304+
}
305+
282306
void
283307
AlterIndex(Oid indexRelationId, IndexStmt *stmt)
284308
{
@@ -292,14 +316,15 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
292316
SPIPlanPtr plan;
293317
Portal portal;
294318
HeapTuple tuple;
295-
HeapTuple updatedTuple;
296319
TupleTableSlot *slot;
297320
ItemPointer tupleid;
298321
IndexInfo *indexInfo;
299322
EState *estate;
300323
Oid namespaceId;
301-
Relation pg_index;
302324
List* deparseCtx;
325+
char* oldIndexPredicate;
326+
char* newIndexPredicate;
327+
char* relationName;
303328

304329
Assert(stmt->whereClause);
305330
CheckPredicate((Expr *) stmt->whereClause);
@@ -314,8 +339,6 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
314339
/* indexRelation = index_open(indexRelationId, AccessShareLock); */
315340
namespaceId = RelationGetNamespace(indexRelation);
316341

317-
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
318-
319342
indexInfo = BuildIndexInfo(indexRelation);
320343
Assert(indexInfo->ii_Predicate);
321344
Assert(!indexInfo->ii_ExclusionOps);
@@ -327,75 +350,85 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
327350

328351
checkUnique = indexRelation->rd_index->indisunique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO;
329352

330-
/* Update pg_index tuple */
331-
tuple = SearchSysCacheCopy1(INDEXRELID, ObjectIdGetDatum(indexRelationId));
332-
if (!HeapTupleIsValid(tuple))
333-
elog(ERROR, "cache lookup failed for index %u", indexRelationId);
334-
335-
Assert(Natts_pg_index <= INDEX_MAX_KEYS);
336-
heap_deform_tuple(tuple, RelationGetDescr(pg_index), values, isnull);
337-
values[Anum_pg_index_indpred - 1] = CStringGetTextDatum(nodeToString(stmt->whereClause));
338-
updatedTuple = heap_form_tuple(RelationGetDescr(pg_index), values, isnull);
339-
simple_heap_update(pg_index, &tuple->t_self, updatedTuple);
340-
CatalogUpdateIndexes(pg_index, updatedTuple);
341-
heap_freetuple(updatedTuple);
342-
heap_freetuple(tuple);
343-
heap_close(pg_index, NoLock);
344-
345353
slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
354+
355+
deparseCtx = deparse_context_for(RelationGetRelationName(heapRelation), heapRelationId);
356+
relationName = quote_qualified_identifier(get_namespace_name(namespaceId),
357+
get_rel_name(heapRelationId)),
358+
newIndexPredicate = deparse_expression(stmt->whereClause, deparseCtx, false, false);
359+
oldIndexPredicate = deparse_expression((Node*)make_ands_explicit(indexInfo->ii_Predicate), deparseCtx, false, false);
346360

347361
SPI_connect();
348-
deparseCtx = deparse_context_for(RelationGetRelationName(heapRelation), heapRelationId);
349-
select = psprintf("select * from %s where %s and not (%s)",
350-
quote_qualified_identifier(get_namespace_name(namespaceId),
351-
get_rel_name(heapRelationId)),
352-
deparse_expression(stmt->whereClause, deparseCtx, false, false),
353-
deparse_expression((Node*)make_ands_explicit(indexInfo->ii_Predicate), deparseCtx, false, false));
354-
plan = SPI_prepare(select, 0, NULL);
355-
if (plan == NULL) {
356-
ereport(ERROR,
357-
(errcode(ERRCODE_INVALID_CURSOR_STATE),
358-
errmsg("Failed to preapre statement %s", select)));
359-
}
360-
portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
361-
if (portal == NULL) {
362+
363+
select = psprintf("select * from %s where %s and not (%s) limit 1",
364+
relationName, oldIndexPredicate, newIndexPredicate);
365+
if (SPI_execute(select, true, 1) != SPI_OK_SELECT)
366+
{
362367
ereport(ERROR,
363368
(errcode(ERRCODE_INVALID_CURSOR_STATE),
364-
errmsg("Failed to open cursor for %s", select)));
369+
errmsg("Failed to execute statement %s", select)));
365370
}
366-
while (true)
367-
{
368-
SPI_cursor_fetch(portal, true, 1);
369-
if (!SPI_processed) {
370-
break;
371-
}
372-
tuple = SPI_tuptable->vals[0];
373-
tupleid = &tuple->t_data->t_ctid;
374-
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
371+
if (SPI_processed) {
372+
/* There is no way in Postgres to exclude records from index, so we have to completelty rebuild index in this case */
373+
bool relpersistence = indexRelation->rd_rel->relpersistence;
374+
index_close(indexRelation, NoLock);
375+
indexRelation->rd_indpred = make_ands_implicit((Expr *) stmt->whereClause);
376+
indexRelation = NULL;
377+
UpdateIndex(indexRelationId, stmt->whereClause);
378+
reindex_index(indexRelationId, false, relpersistence, 0);
379+
} else {
380+
select = psprintf("select * from %s where %s and not (%s)",
381+
relationName, newIndexPredicate, oldIndexPredicate);
382+
plan = SPI_prepare(select, 0, NULL);
383+
if (plan == NULL) {
384+
ereport(ERROR,
385+
(errcode(ERRCODE_INVALID_CURSOR_STATE),
386+
errmsg("Failed to preapre statement %s", select)));
387+
}
388+
portal = SPI_cursor_open(NULL, plan, NULL, NULL, true);
389+
if (portal == NULL) {
390+
ereport(ERROR,
391+
(errcode(ERRCODE_INVALID_CURSOR_STATE),
392+
errmsg("Failed to open cursor for %s", select)));
393+
}
394+
while (true)
395+
{
396+
SPI_cursor_fetch(portal, true, 1);
397+
if (!SPI_processed) {
398+
break;
399+
}
400+
tuple = SPI_tuptable->vals[0];
401+
tupleid = &tuple->t_data->t_ctid;
402+
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
403+
404+
FormIndexDatum(indexInfo,
405+
slot,
406+
estate,
407+
values,
408+
isnull);
409+
index_insert(indexRelation, /* index relation */
410+
values, /* array of index Datums */
411+
isnull, /* null flags */
412+
tupleid, /* tid of heap tuple */
413+
heapRelation, /* heap relation */
414+
checkUnique); /* type of uniqueness check to do */
375415

376-
FormIndexDatum(indexInfo,
377-
slot,
378-
estate,
379-
values,
380-
isnull);
381-
index_insert(indexRelation, /* index relation */
382-
values, /* array of index Datums */
383-
isnull, /* null flags */
384-
tupleid, /* tid of heap tuple */
385-
heapRelation, /* heap relation */
386-
checkUnique); /* type of uniqueness check to do */
387-
388-
SPI_freetuple(tuple);
389-
SPI_freetuptable(SPI_tuptable);
416+
SPI_freetuple(tuple);
417+
SPI_freetuptable(SPI_tuptable);
418+
}
419+
SPI_cursor_close(portal);
420+
421+
UpdateIndex(indexRelationId, stmt->whereClause);
390422
}
391-
SPI_cursor_close(portal);
392423
SPI_finish();
393424

394425
ExecDropSingleTupleTableSlot(slot);
395426
FreeExecutorState(estate);
396427

397428
heap_close(heapRelation, NoLock);
398-
index_close(indexRelation, NoLock);
429+
if (indexRelation) {
430+
index_close(indexRelation, NoLock);
431+
}
399432
}
400433

401434
/*

0 commit comments

Comments
 (0)