mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Indexes with INCLUDE columns and their support in B-tree
This patch introduces INCLUDE clause to index definition. This clause specifies a list of columns which will be included as a non-key part in the index. The INCLUDE columns exist solely to allow more queries to benefit from index-only scans. Also, such columns don't need to have appropriate operator classes. Expressions are not supported as INCLUDE columns since they cannot be used in index-only scans. Index access methods supporting INCLUDE are indicated by amcaninclude flag in IndexAmRoutine. For now, only B-tree indexes support INCLUDE clause. In B-tree indexes INCLUDE columns are truncated from pivot index tuples (tuples located in non-leaf pages and high keys). Therefore, B-tree indexes now might have variable number of attributes. This patch also provides generic facility to support that: pivot tuples contain number of their attributes in t_tid.ip_posid. Free 13th bit of t_info is used for indicating that. This facility will simplify further support of index suffix truncation. The changes of above are backward-compatible, pg_upgrade doesn't need special handling of B-tree indexes for that. Bump catalog version Author: Anastasia Lubennikova with contribition by Alexander Korotkov and me Reviewed by: Peter Geoghegan, Tomas Vondra, Antonin Houska, Jeff Janes, David Rowley, Alexander Korotkov Discussion: https://www.postgresql.org/message-id/flat/56168952.4010101@postgrespro.ru
This commit is contained in:
@ -109,8 +109,10 @@ static void ReindexPartitionedIndex(Relation parentIdx);
|
||||
* indexes. We acknowledge this when all operator classes, collations and
|
||||
* exclusion operators match. Though we could further permit intra-opfamily
|
||||
* changes for btree and hash indexes, that adds subtle complexity with no
|
||||
* concrete benefit for core types.
|
||||
|
||||
* concrete benefit for core types. Note, that INCLUDE columns aren't
|
||||
* checked by this function, for them it's enough that table rewrite is
|
||||
* skipped.
|
||||
*
|
||||
* When a comparison or exclusion operator has a polymorphic input type, the
|
||||
* actual input types must also match. This defends against the possibility
|
||||
* that operators could vary behavior in response to get_fn_expr_argtype().
|
||||
@ -224,7 +226,7 @@ CheckIndexCompatible(Oid oldId,
|
||||
}
|
||||
|
||||
/* Any change in operator class or collation breaks compatibility. */
|
||||
old_natts = indexForm->indnatts;
|
||||
old_natts = indexForm->indnkeyatts;
|
||||
Assert(old_natts == numberOfAttributes);
|
||||
|
||||
d = SysCacheGetAttr(INDEXRELID, tuple, Anum_pg_index_indcollation, &isnull);
|
||||
@ -351,6 +353,7 @@ DefineIndex(Oid relationId,
|
||||
bits16 flags;
|
||||
bits16 constr_flags;
|
||||
int numberOfAttributes;
|
||||
int numberOfKeyAttributes;
|
||||
TransactionId limitXmin;
|
||||
VirtualTransactionId *old_snapshots;
|
||||
ObjectAddress address;
|
||||
@ -361,10 +364,28 @@ DefineIndex(Oid relationId,
|
||||
Snapshot snapshot;
|
||||
int i;
|
||||
|
||||
if (list_intersection(stmt->indexParams, stmt->indexIncludingParams) != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("included columns must not intersect with key columns")));
|
||||
|
||||
/*
|
||||
* count attributes in index
|
||||
* count key attributes in index
|
||||
*/
|
||||
numberOfKeyAttributes = list_length(stmt->indexParams);
|
||||
|
||||
/*
|
||||
* We append any INCLUDE columns onto the indexParams list so that we have
|
||||
* one list with all columns. Later we can determine which of these are
|
||||
* key columns, and which are just part of the INCLUDE list by checking
|
||||
* the list position. A list item in a position less than
|
||||
* ii_NumIndexKeyAttrs is part of the key columns, and anything equal to
|
||||
* and over is part of the INCLUDE columns.
|
||||
*/
|
||||
stmt->indexParams = list_concat(stmt->indexParams,
|
||||
stmt->indexIncludingParams);
|
||||
numberOfAttributes = list_length(stmt->indexParams);
|
||||
|
||||
if (numberOfAttributes <= 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
@ -568,6 +589,11 @@ DefineIndex(Oid relationId,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("access method \"%s\" does not support unique indexes",
|
||||
accessMethodName)));
|
||||
if (list_length(stmt->indexIncludingParams) > 0 && !amRoutine->amcaninclude)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("access method \"%s\" does not support included columns",
|
||||
accessMethodName)));
|
||||
if (numberOfAttributes > 1 && !amRoutine->amcanmulticol)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
@ -605,6 +631,7 @@ DefineIndex(Oid relationId,
|
||||
*/
|
||||
indexInfo = makeNode(IndexInfo);
|
||||
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
||||
indexInfo->ii_NumIndexKeyAttrs = numberOfKeyAttributes;
|
||||
indexInfo->ii_Expressions = NIL; /* for now */
|
||||
indexInfo->ii_ExpressionsState = NIL;
|
||||
indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
|
||||
@ -624,7 +651,7 @@ DefineIndex(Oid relationId,
|
||||
|
||||
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
||||
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
||||
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
||||
classObjectId = (Oid *) palloc(numberOfKeyAttributes * sizeof(Oid));
|
||||
coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
|
||||
ComputeIndexAttrs(indexInfo,
|
||||
typeObjectId, collationObjectId, classObjectId,
|
||||
@ -1348,16 +1375,15 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
|
||||
ListCell *nextExclOp;
|
||||
ListCell *lc;
|
||||
int attn;
|
||||
int nkeycols = indexInfo->ii_NumIndexKeyAttrs;
|
||||
|
||||
/* Allocate space for exclusion operator info, if needed */
|
||||
if (exclusionOpNames)
|
||||
{
|
||||
int ncols = list_length(attList);
|
||||
|
||||
Assert(list_length(exclusionOpNames) == ncols);
|
||||
indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * ncols);
|
||||
indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * ncols);
|
||||
indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * ncols);
|
||||
Assert(list_length(exclusionOpNames) == nkeycols);
|
||||
indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * nkeycols);
|
||||
indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * nkeycols);
|
||||
indexInfo->ii_ExclusionStrats = (uint16 *) palloc(sizeof(uint16) * nkeycols);
|
||||
nextExclOp = list_head(exclusionOpNames);
|
||||
}
|
||||
else
|
||||
@ -1410,6 +1436,11 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
|
||||
Node *expr = attribute->expr;
|
||||
|
||||
Assert(expr != NULL);
|
||||
|
||||
if (attn >= nkeycols)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("expressions are not supported in included columns")));
|
||||
atttype = exprType(expr);
|
||||
attcollation = exprCollation(expr);
|
||||
|
||||
@ -1487,6 +1518,16 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
|
||||
|
||||
collationOidP[attn] = attcollation;
|
||||
|
||||
/*
|
||||
* Skip opclass and ordering options for included columns.
|
||||
*/
|
||||
if (attn >= nkeycols)
|
||||
{
|
||||
colOptionP[attn] = 0;
|
||||
attn++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Identify the opclass to use.
|
||||
*/
|
||||
|
@ -602,7 +602,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
|
||||
RelationGetRelationName(tempRel));
|
||||
diffname = make_temptable_name_n(tempname, 2);
|
||||
|
||||
relnatts = matviewRel->rd_rel->relnatts;
|
||||
relnatts = RelationGetNumberOfAttributes(matviewRel);
|
||||
|
||||
/* Open SPI context. */
|
||||
if (SPI_connect() != SPI_OK_CONNECT)
|
||||
@ -680,7 +680,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
|
||||
if (is_usable_unique_index(indexRel))
|
||||
{
|
||||
Form_pg_index indexStruct = indexRel->rd_index;
|
||||
int numatts = indexStruct->indnatts;
|
||||
int indnkeyatts = indexStruct->indnkeyatts;
|
||||
oidvector *indclass;
|
||||
Datum indclassDatum;
|
||||
bool isnull;
|
||||
@ -695,7 +695,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
|
||||
indclass = (oidvector *) DatumGetPointer(indclassDatum);
|
||||
|
||||
/* Add quals for all columns from this index. */
|
||||
for (i = 0; i < numatts; i++)
|
||||
for (i = 0; i < indnkeyatts; i++)
|
||||
{
|
||||
int attnum = indexStruct->indkey.values[i];
|
||||
Oid opclass = indclass->values[i];
|
||||
|
@ -5942,7 +5942,7 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
|
||||
* Loop over each attribute in the primary key and see if it
|
||||
* matches the to-be-altered attribute
|
||||
*/
|
||||
for (i = 0; i < indexStruct->indnatts; i++)
|
||||
for (i = 0; i < indexStruct->indnkeyatts; i++)
|
||||
{
|
||||
if (indexStruct->indkey.values[i] == attnum)
|
||||
ereport(ERROR,
|
||||
@ -7641,6 +7641,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||
RelationGetRelid(rel),
|
||||
fkattnum,
|
||||
numfks,
|
||||
numfks,
|
||||
InvalidOid, /* not a domain constraint */
|
||||
indexOid,
|
||||
RelationGetRelid(pkrel),
|
||||
@ -8199,7 +8200,7 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
|
||||
* assume a primary key cannot have expressional elements)
|
||||
*/
|
||||
*attnamelist = NIL;
|
||||
for (i = 0; i < indexStruct->indnatts; i++)
|
||||
for (i = 0; i < indexStruct->indnkeyatts; i++)
|
||||
{
|
||||
int pkattno = indexStruct->indkey.values[i];
|
||||
|
||||
@ -8277,7 +8278,7 @@ transformFkeyCheckAttrs(Relation pkrel,
|
||||
* partial index; forget it if there are any expressions, too. Invalid
|
||||
* indexes are out as well.
|
||||
*/
|
||||
if (indexStruct->indnatts == numattrs &&
|
||||
if (indexStruct->indnkeyatts == numattrs &&
|
||||
indexStruct->indisunique &&
|
||||
IndexIsValid(indexStruct) &&
|
||||
heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
|
||||
@ -12529,7 +12530,7 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
|
||||
RelationGetRelationName(indexRel))));
|
||||
|
||||
/* Check index for nullable columns. */
|
||||
for (key = 0; key < indexRel->rd_index->indnatts; key++)
|
||||
for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
|
||||
{
|
||||
int16 attno = indexRel->rd_index->indkey.values[key];
|
||||
Form_pg_attribute attr;
|
||||
|
@ -742,6 +742,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
|
||||
RelationGetRelid(rel),
|
||||
NULL, /* no conkey */
|
||||
0,
|
||||
0,
|
||||
InvalidOid, /* no domain */
|
||||
InvalidOid, /* no index */
|
||||
InvalidOid, /* no foreign key */
|
||||
|
@ -3157,6 +3157,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
InvalidOid, /* not a relation constraint */
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
domainOid, /* domain constraint */
|
||||
InvalidOid, /* no associated index */
|
||||
InvalidOid, /* Foreign key fields */
|
||||
|
Reference in New Issue
Block a user