@@ -284,6 +284,30 @@ CheckIndexCompatible(Oid oldId,
284
284
return ret ;
285
285
}
286
286
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
+
287
311
void
288
312
AlterIndex (Oid indexRelationId , IndexStmt * stmt )
289
313
{
@@ -297,14 +321,15 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
297
321
SPIPlanPtr plan ;
298
322
Portal portal ;
299
323
HeapTuple tuple ;
300
- HeapTuple updatedTuple ;
301
324
TupleTableSlot * slot ;
302
325
ItemPointer tupleid ;
303
326
IndexInfo * indexInfo ;
304
327
EState * estate ;
305
328
Oid namespaceId ;
306
- Relation pg_index ;
307
329
List * deparseCtx ;
330
+ char * oldIndexPredicate ;
331
+ char * newIndexPredicate ;
332
+ char * relationName ;
308
333
309
334
Assert (stmt -> whereClause );
310
335
CheckPredicate ((Expr * ) stmt -> whereClause );
@@ -319,8 +344,6 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
319
344
/* indexRelation = index_open(indexRelationId, AccessShareLock); */
320
345
namespaceId = RelationGetNamespace (indexRelation );
321
346
322
- pg_index = heap_open (IndexRelationId , RowExclusiveLock );
323
-
324
347
indexInfo = BuildIndexInfo (indexRelation );
325
348
Assert (indexInfo -> ii_Predicate );
326
349
Assert (!indexInfo -> ii_ExclusionOps );
@@ -332,75 +355,85 @@ AlterIndex(Oid indexRelationId, IndexStmt *stmt)
332
355
333
356
checkUnique = indexRelation -> rd_index -> indisunique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO ;
334
357
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
-
350
358
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);
351
365
352
366
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
+ {
367
372
ereport (ERROR ,
368
373
(errcode (ERRCODE_INVALID_CURSOR_STATE ),
369
- errmsg ("Failed to open cursor for %s" , select )));
374
+ errmsg ("Failed to execute statement %s" , select )));
370
375
}
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 */
380
420
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 );
395
427
}
396
- SPI_cursor_close (portal );
397
428
SPI_finish ();
398
429
399
430
ExecDropSingleTupleTableSlot (slot );
400
431
FreeExecutorState (estate );
401
432
402
433
heap_close (heapRelation , NoLock );
403
- index_close (indexRelation , NoLock );
434
+ if (indexRelation ) {
435
+ index_close (indexRelation , NoLock );
436
+ }
404
437
}
405
438
406
439
/*
0 commit comments