From 8c3b4ba9d7061ac5338ce5ee55e11a66f982d904 Mon Sep 17 00:00:00 2001 From: Yugo Nagata Date: Tue, 27 Feb 2024 17:09:34 +0900 Subject: [PATCH] Consider tuple duplicity in maintenance of EXISTS views When a tuple is inserted into a table in an EXISTS subquery, the duplicity of row is computed by count(*), but it was not considered and only one tuple was inserted with ignoring the duplicity. This is fixed by duplicating rows as much as the duplicity by using generate_series at inserting. (Issue #82) --- expected/pg_ivm.out | 13 +++++++++++++ matview.c | 22 ++++++++++++++++++++-- sql/pg_ivm.sql | 3 +++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/expected/pg_ivm.out b/expected/pg_ivm.out index bf559d3..4252f62 100644 --- a/expected/pg_ivm.out +++ b/expected/pg_ivm.out @@ -828,6 +828,19 @@ SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j; 4 | 40 | 1 (4 rows) +DELETE FROM mv_base_b WHERE i = 1 or i = 3; +INSERT INTO mv_base_b VALUES (1,100), (3,300); +SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j; + i | j | __ivm_exists_count_0__ +---+-----+------------------------ + 1 | 10 | 1 + 1 | 10 | 1 + 3 | 30 | 1 + 3 | 30 | 1 + 3 | 300 | 1 + 4 | 40 | 1 +(6 rows) + ROLLBACK; -- support simple subquery in FROM clause BEGIN; diff --git a/matview.c b/matview.c index d12041e..11a7e13 100644 --- a/matview.c +++ b/matview.c @@ -2523,6 +2523,7 @@ apply_new_delta_with_count(const char *matviewname, const char* deltaname_new, StringInfoData returning_keys; ListCell *lc; char *match_cond = ""; + StringInfoData deltaname_new_for_insert; /* build WHERE condition for searching tuples to be updated */ match_cond = get_matching_condition_string(keys); @@ -2543,6 +2544,24 @@ apply_new_delta_with_count(const char *matviewname, const char* deltaname_new, else appendStringInfo(&returning_keys, "NULL"); + /* + * If count_colname is not "__ivm_count__", the view contains EXISTS + * subquery and the count column to be updated here is "__ivm_exists_count_*" + * that stores the number of columns generated by corresponding EXISTS + * subquery for each row in the view. In this case, __ivm_count__ in + * deltaname_new stores duplicity of rows, and each row need to be + * duplicated as much as __ivm_count__ by using generate_series at + * inserting. + */ + initStringInfo(&deltaname_new_for_insert); + if (!strcmp(count_colname, "__ivm_count__")) + appendStringInfo(&deltaname_new_for_insert, "%s", deltaname_new); + else + appendStringInfo(&deltaname_new_for_insert, + "(SELECT diff.* FROM %s diff," + "pg_catalog.generate_series(1, diff.\"__ivm_count__\"))", + deltaname_new); + /* Search for matching tuples from the view and update if found or insert if not. */ initStringInfo(&querybuf); appendStringInfo(&querybuf, @@ -2561,9 +2580,8 @@ apply_new_delta_with_count(const char *matviewname, const char* deltaname_new, match_cond, returning_keys.data, matviewname, target_list->data, - target_list->data, deltaname_new, + target_list->data, deltaname_new_for_insert.data, match_cond); - if (SPI_exec(querybuf.data, 0) != SPI_OK_INSERT) elog(ERROR, "SPI_exec failed: %s", querybuf.data); } diff --git a/sql/pg_ivm.sql b/sql/pg_ivm.sql index f99224c..8294e07 100644 --- a/sql/pg_ivm.sql +++ b/sql/pg_ivm.sql @@ -269,6 +269,9 @@ DELETE FROM mv_base_a WHERE (i,j) = (1,60); DELETE FROM mv_base_b WHERE i = 2; SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j; SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j; +DELETE FROM mv_base_b WHERE i = 1 or i = 3; +INSERT INTO mv_base_b VALUES (1,100), (3,300); +SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j; ROLLBACK; -- support simple subquery in FROM clause