Skip to content

Commit e1ca281

Browse files
knizhnikkelvich
authored andcommitted
Support more restricted predicate in ALTER INDEX clause
1 parent ced7bf4 commit e1ca281

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
@@ -284,6 +284,30 @@ CheckIndexCompatible(Oid oldId,
284284
return ret;
285285
}
286286

287+
static void
288+
UpdateIndex(Oid indexRelationId, Node* whereClause)
289+
{
290+
Datum values[Natts_pg_index];
291+
bool isnull[Natts_pg_index];
292+
HeapTuple oldTuple;
293+
HeapTuple newTuple;
294+
Relation pg_index;
295+
296+
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
297+
oldTuple = SearchSysCacheCopy1(INDEXRELID, ObjectIdGetDatum(indexRelationId));
298+
if (!HeapTupleIsValid(oldTuple))
299+
elog(ERROR, "cache lookup failed for index %u", indexRelationId);
300+
301+
heap_deform_tuple(oldTuple, RelationGetDescr(pg_index), values, isnull);
302+
values[Anum_pg_index_indpred - 1] = CStringGetTextDatum(nodeToString(whereClause));
303+
newTuple = heap_form_tuple(RelationGetDescr(pg_index), values, isnull);
304+
simple_heap_update(pg_index, &oldTuple->t_self, newTuple);
305+
CatalogUpdateIndexes(pg_index, newTuple);
306+
heap_freetuple(newTuple);
307+
heap_freetuple(oldTuple);
308+
heap_close(pg_index, NoLock);
309+
}
310+
287311
void
288312
AlterIndex(Oid indexRelationId, IndexStmt *stmt)
289313
{
@@ -297,14 +321,15 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
297321
SPIPlanPtr plan;
298322
Portal portal;
299323
HeapTuple tuple;
300-
HeapTuple updatedTuple;
301324
TupleTableSlot *slot;
302325
ItemPointer tupleid;
303326
IndexInfo *indexInfo;
304327
EState *estate;
305328
Oid namespaceId;
306-
Relation pg_index;
307329
List* deparseCtx;
330+
char* oldIndexPredicate;
331+
char* newIndexPredicate;
332+
char* relationName;
308333

309334
Assert(stmt->whereClause);
310335
CheckPredicate((Expr *) stmt->whereClause);
@@ -319,8 +344,6 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
319344
/* indexRelation = index_open(indexRelationId, AccessShareLock); */
320345
namespaceId = RelationGetNamespace(indexRelation);
321346

322-
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
323-
324347
indexInfo = BuildIndexInfo(indexRelation);
325348
Assert(indexInfo->ii_Predicate);
326349
Assert(!indexInfo->ii_ExclusionOps);
@@ -332,75 +355,85 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
332355

333356
checkUnique = indexRelation->rd_index->indisunique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO;
334357

335-
/* Update pg_index tuple */
336-
tuple = SearchSysCacheCopy1(INDEXRELID, ObjectIdGetDatum(indexRelationId));
337-
if (!HeapTupleIsValid(tuple))
338-
elog(ERROR, "cache lookup failed for index %u", indexRelationId);
339-
340-
Assert(Natts_pg_index <= INDEX_MAX_KEYS);
341-
heap_deform_tuple(tuple, RelationGetDescr(pg_index), values, isnull);
342-
values[Anum_pg_index_indpred - 1] = CStringGetTextDatum(nodeToString(stmt->whereClause));
343-
updatedTuple = heap_form_tuple(RelationGetDescr(pg_index), values, isnull);
344-
simple_heap_update(pg_index, &tuple->t_self, updatedTuple);
345-
CatalogUpdateIndexes(pg_index, updatedTuple);
346-
heap_freetuple(updatedTuple);
347-
heap_freetuple(tuple);
348-
heap_close(pg_index, NoLock);
349-
350358
slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
359+
360+
deparseCtx = deparse_context_for(RelationGetRelationName(heapRelation), heapRelationId);
361+
relationName = quote_qualified_identifier(get_namespace_name(namespaceId),
362+
get_rel_name(heapRelationId)),
363+
newIndexPredicate = deparse_expression(stmt->whereClause, deparseCtx, false, false);
364+
oldIndexPredicate = deparse_expression((Node*)make_ands_explicit(indexInfo->ii_Predicate), deparseCtx, false, false);
351365

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

381-
FormIndexDatum(indexInfo,
382-
slot,
383-
estate,
384-
values,
385-
isnull);
386-
index_insert(indexRelation, /* index relation */
387-
values, /* array of index Datums */
388-
isnull, /* null flags */
389-
tupleid, /* tid of heap tuple */
390-
heapRelation, /* heap relation */
391-
checkUnique); /* type of uniqueness check to do */
392-
393-
SPI_freetuple(tuple);
394-
SPI_freetuptable(SPI_tuptable);
421+
SPI_freetuple(tuple);
422+
SPI_freetuptable(SPI_tuptable);
423+
}
424+
SPI_cursor_close(portal);
425+
426+
UpdateIndex(indexRelationId, stmt->whereClause);
395427
}
396-
SPI_cursor_close(portal);
397428
SPI_finish();
398429

399430
ExecDropSingleTupleTableSlot(slot);
400431
FreeExecutorState(estate);
401432

402433
heap_close(heapRelation, NoLock);
403-
index_close(indexRelation, NoLock);
434+
if (indexRelation) {
435+
index_close(indexRelation, NoLock);
436+
}
404437
}
405438

406439
/*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy