Skip to content

Commit 0934908

Browse files
yugo-nCommitfest Bot
authored andcommitted
Add DISTINCT support for IVM
When IMMV is created with DISTINCT, multiplicity of tuples is counted and stored in "__ivm_count__" column, which is a hidden column of IMMV. The value in __ivm_count__ is updated when IMMV is maintained incrementally. A tuple in IMMV can be removed if and only if the count becomes zero.
1 parent 79cd064 commit 0934908

File tree

10 files changed

+320
-45
lines changed

10 files changed

+320
-45
lines changed

src/backend/commands/createas.c

Lines changed: 109 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "parser/parser.h"
5151
#include "parser/parsetree.h"
5252
#include "parser/parse_clause.h"
53+
#include "parser/parse_func.h"
5354
#include "rewrite/rewriteHandler.h"
5455
#include "tcop/tcopprot.h"
5556
#include "utils/builtins.h"
@@ -305,6 +306,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
305306
errhint("functions must be marked IMMUTABLE")));
306307

307308
check_ivm_restriction((Node *) query);
309+
310+
/* For IMMV, we need to rewrite matview query */
311+
query = rewriteQueryForIMMV(query, into->colNames);
308312
}
309313

310314
if (into->skipData)
@@ -409,6 +413,49 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
409413
return address;
410414
}
411415

416+
/*
417+
* rewriteQueryForIMMV -- rewrite view definition query for IMMV
418+
*
419+
* count(*) is added for counting distinct tuples in views.
420+
*/
421+
Query *
422+
rewriteQueryForIMMV(Query *query, List *colNames)
423+
{
424+
Query *rewritten;
425+
426+
Node *node;
427+
ParseState *pstate = make_parsestate(NULL);
428+
FuncCall *fn;
429+
430+
rewritten = copyObject(query);
431+
pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
432+
433+
/*
434+
* Convert DISTINCT to GROUP BY and add count(*) for counting distinct
435+
* tuples in views.
436+
*/
437+
if (rewritten->distinctClause)
438+
{
439+
TargetEntry *tle;
440+
441+
rewritten->groupClause = transformDistinctClause(NULL, &rewritten->targetList, rewritten->sortClause, false);
442+
443+
fn = makeFuncCall(SystemFuncName("count"), NIL, COERCE_EXPLICIT_CALL, -1);
444+
fn->agg_star = true;
445+
446+
node = ParseFuncOrColumn(pstate, fn->funcname, NIL, NULL, fn, false, -1);
447+
448+
tle = makeTargetEntry((Expr *) node,
449+
list_length(rewritten->targetList) + 1,
450+
pstrdup("__ivm_count__"),
451+
false);
452+
rewritten->targetList = lappend(rewritten->targetList, tle);
453+
rewritten->hasAggs = true;
454+
}
455+
456+
return rewritten;
457+
}
458+
412459
/*
413460
* GetIntoRelEFlags --- compute executor flags needed for CREATE TABLE AS
414461
*
@@ -532,7 +579,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
532579
ColumnDef *col;
533580
char *colname;
534581

535-
if (lc)
582+
/* Don't override hidden columns added for IVM */
583+
if (lc && !isIvmName(NameStr(attribute->attname)))
536584
{
537585
colname = strVal(lfirst(lc));
538586
lc = lnext(into->colNames, lc);
@@ -936,10 +984,6 @@ check_ivm_restriction_walker(Node *node, void *context)
936984
ereport(ERROR,
937985
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
938986
errmsg("LIMIT/OFFSET clause is not supported on incrementally maintainable materialized view")));
939-
if (qry->distinctClause)
940-
ereport(ERROR,
941-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
942-
errmsg("DISTINCT is not supported on incrementally maintainable materialized view")));
943987
if (qry->hasDistinctOn)
944988
ereport(ERROR,
945989
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1086,12 +1130,18 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
10861130
char idxname[NAMEDATALEN];
10871131
List *indexoidlist = RelationGetIndexList(matviewRel);
10881132
ListCell *indexoidscan;
1089-
Bitmapset *key_attnos;
10901133

10911134
snprintf(idxname, sizeof(idxname), "%s_index", RelationGetRelationName(matviewRel));
10921135

10931136
index = makeNode(IndexStmt);
10941137

1138+
/*
1139+
* We consider null values not distinct to make sure that views with DISTINCT
1140+
* or GROUP BY don't contain multiple NULL rows when NULL is inserted to
1141+
* a base table concurrently.
1142+
*/
1143+
index->nulls_not_distinct = true;
1144+
10951145
index->unique = true;
10961146
index->primary = false;
10971147
index->isconstraint = false;
@@ -1118,41 +1168,68 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
11181168
index->concurrent = false;
11191169
index->if_not_exists = false;
11201170

1121-
/* create index on the base tables' primary key columns */
1122-
key_attnos = get_primary_key_attnos_from_query(query, &constraintList);
1123-
if (key_attnos)
1171+
if (query->distinctClause)
11241172
{
1173+
/* create unique constraint on all columns */
11251174
foreach(lc, query->targetList)
11261175
{
11271176
TargetEntry *tle = (TargetEntry *) lfirst(lc);
11281177
Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, tle->resno - 1);
1129-
1130-
if (bms_is_member(tle->resno - FirstLowInvalidHeapAttributeNumber, key_attnos))
1131-
{
1132-
IndexElem *iparam;
1133-
1134-
iparam = makeNode(IndexElem);
1135-
iparam->name = pstrdup(NameStr(attr->attname));
1136-
iparam->expr = NULL;
1137-
iparam->indexcolname = NULL;
1138-
iparam->collation = NIL;
1139-
iparam->opclass = NIL;
1140-
iparam->opclassopts = NIL;
1141-
iparam->ordering = SORTBY_DEFAULT;
1142-
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
1143-
index->indexParams = lappend(index->indexParams, iparam);
1144-
}
1178+
IndexElem *iparam;
1179+
1180+
iparam = makeNode(IndexElem);
1181+
iparam->name = pstrdup(NameStr(attr->attname));
1182+
iparam->expr = NULL;
1183+
iparam->indexcolname = NULL;
1184+
iparam->collation = NIL;
1185+
iparam->opclass = NIL;
1186+
iparam->opclassopts = NIL;
1187+
iparam->ordering = SORTBY_DEFAULT;
1188+
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
1189+
index->indexParams = lappend(index->indexParams, iparam);
11451190
}
11461191
}
11471192
else
11481193
{
1149-
/* create no index, just notice that an appropriate index is necessary for efficient IVM */
1150-
ereport(NOTICE,
1151-
(errmsg("could not create an index on materialized view \"%s\" automatically",
1152-
RelationGetRelationName(matviewRel)),
1153-
errdetail("This target list does not have all the primary key columns. "),
1154-
errhint("Create an index on the materialized view for efficient incremental maintenance.")));
1155-
return;
1194+
Bitmapset *key_attnos;
1195+
1196+
/* create index on the base tables' primary key columns */
1197+
key_attnos = get_primary_key_attnos_from_query(query, &constraintList);
1198+
if (key_attnos)
1199+
{
1200+
foreach(lc, query->targetList)
1201+
{
1202+
TargetEntry *tle = (TargetEntry *) lfirst(lc);
1203+
Form_pg_attribute attr = TupleDescAttr(matviewRel->rd_att, tle->resno - 1);
1204+
1205+
if (bms_is_member(tle->resno - FirstLowInvalidHeapAttributeNumber, key_attnos))
1206+
{
1207+
IndexElem *iparam;
1208+
1209+
iparam = makeNode(IndexElem);
1210+
iparam->name = pstrdup(NameStr(attr->attname));
1211+
iparam->expr = NULL;
1212+
iparam->indexcolname = NULL;
1213+
iparam->collation = NIL;
1214+
iparam->opclass = NIL;
1215+
iparam->opclassopts = NIL;
1216+
iparam->ordering = SORTBY_DEFAULT;
1217+
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
1218+
index->indexParams = lappend(index->indexParams, iparam);
1219+
}
1220+
}
1221+
}
1222+
else
1223+
{
1224+
/* create no index, just notice that an appropriate index is necessary for efficient IVM */
1225+
ereport(NOTICE,
1226+
(errmsg("could not create an index on materialized view \"%s\" automatically",
1227+
RelationGetRelationName(matviewRel)),
1228+
errdetail("This target list does not have all the primary key columns, "
1229+
"or this view does not contain DISTINCT clause."),
1230+
errhint("Create an index on the materialized view for efficient incremental maintenance.")));
1231+
return;
1232+
}
11561233
}
11571234

11581235
/* If we have a compatible index, we don't need to create another. */

src/backend/commands/indexcmds.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "commands/dbcommands.h"
4141
#include "commands/defrem.h"
4242
#include "commands/event_trigger.h"
43+
#include "commands/matview.h"
4344
#include "commands/progress.h"
4445
#include "commands/tablecmds.h"
4546
#include "commands/tablespace.h"
@@ -1120,6 +1121,45 @@ DefineIndex(Oid tableId,
11201121
safe_index = indexInfo->ii_Expressions == NIL &&
11211122
indexInfo->ii_Predicate == NIL;
11221123

1124+
/*
1125+
* We disallow unique indexes on IVM columns of IMMVs.
1126+
*/
1127+
if (RelationIsIVM(rel) && stmt->unique)
1128+
{
1129+
for (int i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
1130+
{
1131+
AttrNumber attno = indexInfo->ii_IndexAttrNumbers[i];
1132+
if (attno > 0)
1133+
{
1134+
char *name = NameStr(TupleDescAttr(rel->rd_att, attno - 1)->attname);
1135+
if (name && isIvmName(name))
1136+
ereport(ERROR,
1137+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1138+
errmsg("unique index creation on IVM columns is not supported")));
1139+
}
1140+
}
1141+
1142+
if (indexInfo->ii_Expressions)
1143+
{
1144+
Bitmapset *indexattrs = NULL;
1145+
int varno = -1;
1146+
1147+
pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
1148+
1149+
while ((varno = bms_next_member(indexattrs, varno)) >= 0)
1150+
{
1151+
int attno = varno + FirstLowInvalidHeapAttributeNumber;
1152+
char *name = NameStr(TupleDescAttr(rel->rd_att, attno - 1)->attname);
1153+
if (name && isIvmName(name))
1154+
ereport(ERROR,
1155+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1156+
errmsg("unique index creation on IVM columns is not supported")));
1157+
}
1158+
1159+
}
1160+
}
1161+
1162+
11231163
/*
11241164
* Report index creation if appropriate (delay this till after most of the
11251165
* error checks)

0 commit comments

Comments
 (0)