50
50
#include "parser/parser.h"
51
51
#include "parser/parsetree.h"
52
52
#include "parser/parse_clause.h"
53
+ #include "parser/parse_func.h"
53
54
#include "rewrite/rewriteHandler.h"
54
55
#include "tcop/tcopprot.h"
55
56
#include "utils/builtins.h"
@@ -305,6 +306,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
305
306
errhint ("functions must be marked IMMUTABLE" )));
306
307
307
308
check_ivm_restriction ((Node * ) query );
309
+
310
+ /* For IMMV, we need to rewrite matview query */
311
+ query = rewriteQueryForIMMV (query , into -> colNames );
308
312
}
309
313
310
314
if (into -> skipData )
@@ -409,6 +413,49 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
409
413
return address ;
410
414
}
411
415
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
+
412
459
/*
413
460
* GetIntoRelEFlags --- compute executor flags needed for CREATE TABLE AS
414
461
*
@@ -532,7 +579,8 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
532
579
ColumnDef * col ;
533
580
char * colname ;
534
581
535
- if (lc )
582
+ /* Don't override hidden columns added for IVM */
583
+ if (lc && !isIvmName (NameStr (attribute -> attname )))
536
584
{
537
585
colname = strVal (lfirst (lc ));
538
586
lc = lnext (into -> colNames , lc );
@@ -936,10 +984,6 @@ check_ivm_restriction_walker(Node *node, void *context)
936
984
ereport (ERROR ,
937
985
(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
938
986
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" )));
943
987
if (qry -> hasDistinctOn )
944
988
ereport (ERROR ,
945
989
(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
@@ -1086,12 +1130,18 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
1086
1130
char idxname [NAMEDATALEN ];
1087
1131
List * indexoidlist = RelationGetIndexList (matviewRel );
1088
1132
ListCell * indexoidscan ;
1089
- Bitmapset * key_attnos ;
1090
1133
1091
1134
snprintf (idxname , sizeof (idxname ), "%s_index" , RelationGetRelationName (matviewRel ));
1092
1135
1093
1136
index = makeNode (IndexStmt );
1094
1137
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
+
1095
1145
index -> unique = true;
1096
1146
index -> primary = false;
1097
1147
index -> isconstraint = false;
@@ -1118,41 +1168,68 @@ CreateIndexOnIMMV(Query *query, Relation matviewRel)
1118
1168
index -> concurrent = false;
1119
1169
index -> if_not_exists = false;
1120
1170
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 )
1124
1172
{
1173
+ /* create unique constraint on all columns */
1125
1174
foreach (lc , query -> targetList )
1126
1175
{
1127
1176
TargetEntry * tle = (TargetEntry * ) lfirst (lc );
1128
1177
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 );
1145
1190
}
1146
1191
}
1147
1192
else
1148
1193
{
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
+ }
1156
1233
}
1157
1234
1158
1235
/* If we have a compatible index, we don't need to create another. */
0 commit comments