From 26f0b03b586c5ae059190fcbc6ed258023668a5b Mon Sep 17 00:00:00 2001 From: Yugo Nagata Date: Fri, 16 Dec 2022 19:13:08 +0900 Subject: [PATCH] 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. --- createas.c | 14 +++++++++++-- expected/pg_ivm.out | 49 +++++++++++++++++++++++++++++++++++++++++++++ pg_ivm.c | 2 +- sql/pg_ivm.sql | 19 ++++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/createas.c b/createas.c index b6649e5..d5aa664 100644 --- a/createas.c +++ b/createas.c @@ -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); } diff --git a/expected/pg_ivm.out b/expected/pg_ivm.out index c329758..b203ad3 100644 --- a/expected/pg_ivm.out +++ b/expected/pg_ivm.out @@ -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; diff --git a/pg_ivm.c b/pg_ivm.c index 02c96a6..885dab5 100644 --- a/pg_ivm.c +++ b/pg_ivm.c @@ -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); } diff --git a/sql/pg_ivm.sql b/sql/pg_ivm.sql index 5d2dded..c775c85 100644 --- a/sql/pg_ivm.sql +++ b/sql/pg_ivm.sql @@ -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);