Fix to get view definition string with correct column names (#26)

Previously, a query string returned from pg_ivm_get_querydef
did not include column names specified when IMMV was defined
by create_immv. This caused failures in maintenance of MIN/MAX
aggregate views whose columns had alias names.

It is fixed by rewriting the result column name in the parse tree
using the view's tuple descriptor prior to calling pg_get_querydef
for PG15 or higher, or specifying the tuple descriptor to
get_query_def for PG14 or earlier.
This commit is contained in:
Yugo Nagata 2022-09-29 22:24:28 +09:00 committed by GitHub
parent 79d4b13ba1
commit 4c6016999d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 45 deletions

View file

@ -383,7 +383,7 @@ SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3;
ROLLBACK;
-- support MIN(), MAX() aggregate functions
BEGIN;
SELECT create_immv('mv_ivm_min_max', 'SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i');
SELECT create_immv('mv_ivm_min_max(i, min_j, max_j)', 'SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i');
NOTICE: created index "mv_ivm_min_max_index" on immv "mv_ivm_min_max"
create_immv
-------------
@ -391,8 +391,8 @@ NOTICE: created index "mv_ivm_min_max_index" on immv "mv_ivm_min_max"
(1 row)
SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3;
i | min | max | __ivm_count_min__ | __ivm_count_max__ | __ivm_count__
---+-----+-----+-------------------+-------------------+---------------
i | min_j | max_j | __ivm_count_min_j__ | __ivm_count_max_j__ | __ivm_count__
---+-------+-------+---------------------+---------------------+---------------
1 | 10 | 10 | 1 | 1 | 1
2 | 20 | 20 | 1 | 1 | 1
3 | 30 | 30 | 1 | 1 | 1
@ -407,8 +407,8 @@ INSERT INTO mv_base_a VALUES
(4,41), (4,42),
(5,51), (5,52);
SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3;
i | min | max | __ivm_count_min__ | __ivm_count_max__ | __ivm_count__
---+-----+-----+-------------------+-------------------+---------------
i | min_j | max_j | __ivm_count_min_j__ | __ivm_count_max_j__ | __ivm_count__
---+-------+-------+---------------------+---------------------+---------------
1 | 10 | 12 | 3 | 3 | 3
2 | 20 | 22 | 3 | 3 | 3
3 | 30 | 32 | 3 | 3 | 3
@ -418,8 +418,8 @@ SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3;
DELETE FROM mv_base_a WHERE (i,j) IN ((1,10), (2,21), (3,32));
SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3;
i | min | max | __ivm_count_min__ | __ivm_count_max__ | __ivm_count__
---+-----+-----+-------------------+-------------------+---------------
i | min_j | max_j | __ivm_count_min_j__ | __ivm_count_max_j__ | __ivm_count__
---+-------+-------+---------------------+---------------------+---------------
1 | 11 | 12 | 2 | 2 | 2
2 | 20 | 22 | 2 | 2 | 2
3 | 30 | 31 | 2 | 2 | 2
@ -430,37 +430,37 @@ SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3;
ROLLBACK;
-- support MIN(), MAX() aggregate functions without GROUP clause
BEGIN;
SELECT create_immv('mv_ivm_min_max', 'SELECT MIN(j), MAX(j) FROM mv_base_a');
SELECT create_immv('mv_ivm_min_max(min_j, max_j)', 'SELECT MIN(j), MAX(j) FROM mv_base_a');
create_immv
-------------
1
(1 row)
SELECT * FROM mv_ivm_min_max;
min | max | __ivm_count_min__ | __ivm_count_max__ | __ivm_count__
-----+-----+-------------------+-------------------+---------------
min_j | max_j | __ivm_count_min_j__ | __ivm_count_max_j__ | __ivm_count__
-------+-------+---------------------+---------------------+---------------
10 | 50 | 5 | 5 | 5
(1 row)
INSERT INTO mv_base_a VALUES
(0,0), (6,60), (7,70);
SELECT * FROM mv_ivm_min_max;
min | max | __ivm_count_min__ | __ivm_count_max__ | __ivm_count__
-----+-----+-------------------+-------------------+---------------
min_j | max_j | __ivm_count_min_j__ | __ivm_count_max_j__ | __ivm_count__
-------+-------+---------------------+---------------------+---------------
0 | 70 | 8 | 8 | 8
(1 row)
DELETE FROM mv_base_a WHERE (i,j) IN ((0,0), (7,70));
SELECT * FROM mv_ivm_min_max;
min | max | __ivm_count_min__ | __ivm_count_max__ | __ivm_count__
-----+-----+-------------------+-------------------+---------------
min_j | max_j | __ivm_count_min_j__ | __ivm_count_max_j__ | __ivm_count__
-------+-------+---------------------+---------------------+---------------
10 | 60 | 6 | 6 | 6
(1 row)
DELETE FROM mv_base_a;
SELECT * FROM mv_ivm_min_max;
min | max | __ivm_count_min__ | __ivm_count_max__ | __ivm_count__
-----+-----+-------------------+-------------------+---------------
min_j | max_j | __ivm_count_min_j__ | __ivm_count_max_j__ | __ivm_count__
-------+-------+---------------------+---------------------+---------------
| | 0 | 0 | 0
(1 row)

View file

@ -139,7 +139,6 @@ static uint64 refresh_immv_datafill(DestReceiver *dest, Query *query,
static void refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence);
static void OpenImmvIncrementalMaintenance(void);
static void CloseImmvIncrementalMaintenance(void);
static Query *get_immv_query(Relation matviewRel);
static Query *rewrite_query_for_preupdate_state(Query *query, List *tables,
TransactionId xid, CommandId cid,
@ -586,7 +585,7 @@ CloseImmvIncrementalMaintenance(void)
/*
* get_immv_query - get the Query of IMMV.
*/
static Query *
Query *
get_immv_query(Relation matviewRel)
{
Relation pgIvmImmv = table_open(PgIvmImmvRelationId(), AccessShareLock);
@ -2559,7 +2558,7 @@ get_plan_for_recalc(Relation matviewRel, List *namelist, List *keys, Oid *keyTyp
char *viewdef;
/* get view definition of matview */
viewdef = pg_ivm_get_querydef(get_immv_query(matviewRel), false);
viewdef = pg_ivm_get_viewdef(matviewRel, false);
/*
* Build a query string for recalculating values. This is like

View file

@ -46,6 +46,7 @@ extern void makeIvmAggColumn(ParseState *pstate, Aggref *aggref, char *resname,
extern ObjectAddress ExecRefreshImmv(const RangeVar *relation, bool skipData,
const char *queryString, QueryCompletion *qc);
extern bool ImmvIncrementalMaintenanceIsEnabled(void);
extern Query *get_immv_query(Relation matviewRel);
extern Datum IVM_immediate_before(PG_FUNCTION_ARGS);
extern Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
extern void AtAbort_IVM(void);
@ -53,6 +54,6 @@ extern bool isIvmName(const char *s);
/* ruleutils.c */
extern char *pg_ivm_get_querydef(Query *query, bool pretty);
extern char *pg_ivm_get_viewdef(Relation immvrel, bool pretty);
#endif

View file

@ -15,6 +15,7 @@
#include "postgres.h"
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 150000)
#include "utils/rel.h"
#include "utils/ruleutils.h"
#elif defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 140000)
#include "ruleutils_14.c"
@ -30,19 +31,49 @@
: PRETTYFLAG_INDENT)
/* ----------
* pg_get_querydef
* pg_ivm_get_viewdef
*
* Public entry point to deparse one query parsetree.
* Public entry point to deparse a view definition query parsetree.
* The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
*
* The result is a palloc'd C string.
* ----------
*/
char *
pg_ivm_get_querydef(Query *query, bool pretty)
pg_ivm_get_viewdef(Relation immvrel, bool pretty)
{
Query *query = get_immv_query(immvrel);
TupleDesc resultDesc = RelationGetDescr(immvrel);
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 150000)
ListCell *lc;
int colno = 0;
/*
* Rewrite the result column name using the view's tuple
* descriptor.
*
* The column name is usually figured out in get_query_def
* using a tupleDesc specified as an argument, but this
* function is static, so we cannot directly call it.
* Therefore, we rewrite them prior to calling the public
* function pg_get_querydef (for PG15 or higher).
*/
query = copyObject(query);
foreach (lc, query->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
if (tle->resjunk)
continue; /* ignore junk entries */
colno++;
if (resultDesc && colno <= resultDesc->natts)
tle->resname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
}
return pg_get_querydef(query, pretty);
#else
StringInfoData buf;
int prettyFlags;
@ -51,7 +82,12 @@ pg_ivm_get_querydef(Query *query, bool pretty)
initStringInfo(&buf);
get_query_def(query, &buf, NIL, NULL, true,
/*
* For PG14 or earlier, we use get_query_def which is copied
* from the core because any public function for this purpose
* is not available.
*/
get_query_def(query, &buf, NIL, resultDesc, true,
prettyFlags, WRAP_COLUMN_DEFAULT, 0);
return buf.data;

View file

@ -124,7 +124,7 @@ ROLLBACK;
-- support MIN(), MAX() aggregate functions
BEGIN;
SELECT create_immv('mv_ivm_min_max', 'SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i');
SELECT create_immv('mv_ivm_min_max(i, min_j, max_j)', 'SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i');
SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3;
INSERT INTO mv_base_a VALUES
(1,11), (1,12),
@ -139,7 +139,7 @@ ROLLBACK;
-- support MIN(), MAX() aggregate functions without GROUP clause
BEGIN;
SELECT create_immv('mv_ivm_min_max', 'SELECT MIN(j), MAX(j) FROM mv_base_a');
SELECT create_immv('mv_ivm_min_max(min_j, max_j)', 'SELECT MIN(j), MAX(j) FROM mv_base_a');
SELECT * FROM mv_ivm_min_max;
INSERT INTO mv_base_a VALUES
(0,0), (6,60), (7,70);