Fix bugs of IVM that occur when column names are specified in aggregate views (#41)

The names of additional columns for aggregate views are derived from column
names specified in the first parameter of create_immv, but when the number
was less than the length of the actual target list, segmentation fault occurred.
Furthermore, when the number of specified columns is more than the target list,
it overrode additional column names and it caused a failure of incremental
maintenance of an aggregate view.

To fix then, check the length of the specified column name list not to access
invalid area, and also prevent from overriding additional column names.
This commit is contained in:
Yugo Nagata 2022-12-16 19:13:08 +09:00 committed by GitHub
parent 326720874e
commit 26f0b03b58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 3 deletions

View file

@ -289,6 +289,15 @@ rewriteQueryForIMMV(Query *query, List *colNames)
ParseState *pstate = make_parsestate(NULL);
FuncCall *fn;
/*
* Check the length of colunm name list not to override names of
* additional columns
*/
if (list_length(colNames) > list_length(query->targetList))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("too many column names were specified")));
rewritten = copyObject(query);
pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
@ -321,10 +330,11 @@ rewriteQueryForIMMV(Query *query, List *colNames)
foreach(lc, rewritten->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
char *resname = (colNames == NIL ? tle->resname : strVal(list_nth(colNames, tle->resno - 1)));
char *resname = (colNames == NIL || foreach_current_index(lc) >= list_length(colNames) ?
tle->resname : strVal(list_nth(colNames, tle->resno - 1)));
if (IsA(tle->expr, Aggref))
makeIvmAggColumn(pstate, (Aggref *)tle->expr, resname, &next_resno, &aggs);
makeIvmAggColumn(pstate, (Aggref *) tle->expr, resname, &next_resno, &aggs);
}
rewritten->targetList = list_concat(rewritten->targetList, aggs);
}

View file

@ -464,6 +464,55 @@ SELECT * FROM mv_ivm_min_max;
| | 0 | 0 | 0
(1 row)
ROLLBACK;
-- aggregate views with column names specified
BEGIN;
SELECT create_immv('mv_ivm_agg(a)', 'SELECT i, SUM(j) FROM mv_base_a GROUP BY i');
NOTICE: created index "mv_ivm_agg_index" on immv "mv_ivm_agg"
create_immv
-------------
5
(1 row)
INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300);
UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20);
DELETE FROM mv_base_a WHERE (i,j) = (3,30);
SELECT * FROM mv_ivm_agg ORDER BY 1,2;
a | sum | __ivm_count_sum__ | __ivm_count__
---+------+-------------------+---------------
1 | 110 | 2 | 2
2 | 2200 | 2 | 2
3 | 300 | 1 | 1
4 | 40 | 1 | 1
5 | 50 | 1 | 1
(5 rows)
ROLLBACK;
BEGIN;
SELECT create_immv('mv_ivm_agg(a,b)', 'SELECT i, SUM(j) FROM mv_base_a GROUP BY i');
NOTICE: created index "mv_ivm_agg_index" on immv "mv_ivm_agg"
create_immv
-------------
5
(1 row)
INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300);
UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20);
DELETE FROM mv_base_a WHERE (i,j) = (3,30);
SELECT * FROM mv_ivm_agg ORDER BY 1,2;
a | b | __ivm_count_b__ | __ivm_count__
---+------+-----------------+---------------
1 | 110 | 2 | 2
2 | 2200 | 2 | 2
3 | 300 | 1 | 1
4 | 40 | 1 | 1
5 | 50 | 1 | 1
(5 rows)
ROLLBACK;
BEGIN;
SELECT create_immv('mv_ivm_agg(a,b,c)', 'SELECT i, SUM(j) FROM mv_base_a GROUP BY i');
ERROR: too many column names were specified
ROLLBACK;
-- support self join view and multiple change on the same table
BEGIN;

View file

@ -224,7 +224,7 @@ create_immv(PG_FUNCTION_ARGS)
query = transformStmt(pstate, (Node *)ctas);
Assert(query->commandType == CMD_UTILITY && IsA(query->utilityStmt, CreateTableAsStmt));
ExecCreateImmv(pstate, (CreateTableAsStmt *)query->utilityStmt, NULL, NULL, &qc);
ExecCreateImmv(pstate, (CreateTableAsStmt *) query->utilityStmt, NULL, NULL, &qc);
PG_RETURN_INT64(qc.nprocessed);
}

View file

@ -150,6 +150,25 @@ DELETE FROM mv_base_a;
SELECT * FROM mv_ivm_min_max;
ROLLBACK;
-- aggregate views with column names specified
BEGIN;
SELECT create_immv('mv_ivm_agg(a)', 'SELECT i, SUM(j) FROM mv_base_a GROUP BY i');
INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300);
UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20);
DELETE FROM mv_base_a WHERE (i,j) = (3,30);
SELECT * FROM mv_ivm_agg ORDER BY 1,2;
ROLLBACK;
BEGIN;
SELECT create_immv('mv_ivm_agg(a,b)', 'SELECT i, SUM(j) FROM mv_base_a GROUP BY i');
INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300);
UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20);
DELETE FROM mv_base_a WHERE (i,j) = (3,30);
SELECT * FROM mv_ivm_agg ORDER BY 1,2;
ROLLBACK;
BEGIN;
SELECT create_immv('mv_ivm_agg(a,b,c)', 'SELECT i, SUM(j) FROM mv_base_a GROUP BY i');
ROLLBACK;
-- support self join view and multiple change on the same table
BEGIN;
CREATE TABLE base_t (i int, v int);