Skip to content

Commit 63d3383

Browse files
committed
Dynamic Table.
Dynamic Table is a an auto-refreshing materialized view which could be constructed by base tables, external tables, materialized views and dynamic tables. And it could be used to answer query by AQUMV. As normal tables in CBDB, dynamic tables could also have distribution keys. The purpose of Dynamic Tables is to solve the problem often raised by customers who are big fans of a lakehouse architecture: how can we run queries on external tables as fast as internal tables? CREATE DYNAMIC TABLE: CREATE DYNAMIC TABLE dt0 SCHEDULE '5 * * * *' AS SELECT a, b, sum(c) FROM t1 GROUP BY a, b WITH NO DATA DISTRIBUTED BY(b); CREATE DYNAMIC TABLE \d List of relations Schema | Name | Type | Owner | Storage --------+------+---------------+---------+--------- public | dt0 | dynamic table | gpadmin | heap public | t1 | table | gpadmin | heap (2 rows) CREATE DYNAMIC TABLE xxx AS Query The Query allows any valid SELECT SQL of Materialized Views: from single or multiple relations, base tables, materialized views, and dynamic tables as well, joins, subquery, aggregation, group by and etc. SCHEDULE: A string used to schedule background job which auto-refreshes the dynamic table. We follow the valid string of pg_cron extension which supports linux crontab, refer https://crontab.guru ┌───────────── min (0 - 59) │ ┌────────────── hour (0 - 23) │ │ ┌─────────────── day of month (1 - 31) or last day of the month ($) │ │ │ ┌──────────────── month (1 - 12) │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to │ │ │ │ │ Saturday, or use names; 7 is also Sunday) │ │ │ │ │ │ │ │ │ │ * * * * * You can also use '[1-59] seconds' to schedule a job based on an interval. The example creates a cron job refreshing the dynamic table at minute 5 of each hour. For convenience, SCHEDULE is optional. If user didn't specific it, a default schedule is provided: at every 5th minute. WITH NO DATA: Same as Materialized View, will create an empty Dynamic Table if specified. DISTRIBUTED BY: Same as normal tables in CBDB, Dynamic Tables could support distribution keys as materialized views. Refresh Dynamic Table As seen in pg_task, we put a command to auto-refresh dynamic tables. However, if users want to do a REFRESH manually, exec command REFRESH DYNAMIC TABLE is also supported. REFRESH DYNAMIC TABLE dt0; REFRESH DYNAMIC TABLE Refresh WITH NO DATA Same as Materialized Views, Refresh with no data will truncate the Dynamic Table and make it unpopulated status. REFRESH DYNAMIC TABLE dt0 WITH NO DATA; REFRESH DYNAMIC TABLE Drop Dynamic Table Drop a Dynamic Table will drop its scheduler job automatically. DROP DYNAMIC TABLE dt0; DROP DYNAMIC TABLE Like Materialized Views, Dynamic Tables could be used to answer query too. This is limited by AQUMV. Authored-by: Zhang Mingli avamingli@gmail.com
1 parent f92faf0 commit 63d3383

26 files changed

Lines changed: 694 additions & 18 deletions

File tree

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
11531153
rows = (qc && (qc->commandTag == CMDTAG_COPY ||
11541154
qc->commandTag == CMDTAG_FETCH ||
11551155
qc->commandTag == CMDTAG_SELECT ||
1156+
qc->commandTag == CMDTAG_REFRESH_DYNAMIC_TABLE ||
11561157
qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW)) ?
11571158
qc->nprocessed : 0;
11581159

src/backend/catalog/heap.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,7 @@ InsertPgClassTuple(Relation pg_class_desc,
13271327
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
13281328
values[Anum_pg_class_relminmxid - 1] = MultiXactIdGetDatum(rd_rel->relminmxid);
13291329
values[Anum_pg_class_relisivm - 1] = BoolGetDatum(rd_rel->relisivm);
1330+
values[Anum_pg_class_relisdynamic - 1] = BoolGetDatum(rd_rel->relisdynamic);
13301331
if (relacl != (Datum) 0)
13311332
values[Anum_pg_class_relacl - 1] = relacl;
13321333
else

src/backend/catalog/index.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ index_create_internal(Relation heapRelation,
10741074
indexRelation->rd_rel->relam = accessMethodObjectId;
10751075
indexRelation->rd_rel->relispartition = OidIsValid(parentIndexRelid);
10761076
indexRelation->rd_rel->relisivm = false;
1077+
indexRelation->rd_rel->relisdynamic = false;
10771078

10781079
/*
10791080
* store index's pg_class entry

src/backend/catalog/objectaddress.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,9 @@ static const struct object_type_map
758758
{
759759
"materialized view", OBJECT_MATVIEW
760760
},
761+
{
762+
"dynamic table", OBJECT_MATVIEW
763+
},
761764
{
762765
"composite type", -1
763766
}, /* unmapped */
@@ -4384,8 +4387,16 @@ getRelationDescription(StringInfo buffer, Oid relid, bool missing_ok)
43844387
relname);
43854388
break;
43864389
case RELKIND_MATVIEW:
4387-
appendStringInfo(buffer, _("materialized view %s"),
4388-
relname);
4390+
if (relForm->relisdynamic)
4391+
{
4392+
appendStringInfo(buffer, _("dynamic table %s"),
4393+
relname);
4394+
}
4395+
else
4396+
{
4397+
appendStringInfo(buffer, _("materialized view %s"),
4398+
relname);
4399+
}
43894400
break;
43904401
case RELKIND_COMPOSITE_TYPE:
43914402
appendStringInfo(buffer, _("composite type %s"),
@@ -4954,7 +4965,10 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId,
49544965
appendStringInfoString(buffer, "view");
49554966
break;
49564967
case RELKIND_MATVIEW:
4957-
appendStringInfoString(buffer, "materialized view");
4968+
if (relForm->relisdynamic)
4969+
appendStringInfoString(buffer, "dynamic table");
4970+
else
4971+
appendStringInfoString(buffer, "materialized view");
49584972
break;
49594973
case RELKIND_COMPOSITE_TYPE:
49604974
appendStringInfoString(buffer, "composite type");

src/backend/commands/createas.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "commands/prepare.h"
4545
#include "commands/tablecmds.h"
4646
#include "commands/tablespace.h"
47+
#include "commands/taskcmds.h"
4748
#include "commands/trigger.h"
4849
#include "commands/view.h"
4950
#include "miscadmin.h"
@@ -119,6 +120,9 @@ static bool check_ivm_restriction_walker(Node *node, check_ivm_restriction_conte
119120
static Bitmapset *get_primary_key_attnos_from_query(Query *query, List **constraintList);
120121
static bool check_aggregate_supports_ivm(Oid aggfnoid);
121122

123+
#define DYNAMIC_TABLE_DEFAULT_REFRESH_INTERVAL "5 * * * *"
124+
static void create_dynamic_table_auto_refresh_task(ParseState *pstate, Relation DynamicTableRel, char *schedule);
125+
122126
/*
123127
* create_ctas_internal
124128
*
@@ -537,6 +541,14 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
537541
CreateIvmTriggersOnBaseTables(query_immv, matviewOid);
538542
}
539543
}
544+
545+
/* Set Dynamic Tables. */
546+
if (into->dynamicTbl)
547+
{
548+
SetDynamicTableState(matviewRel);
549+
create_dynamic_table_auto_refresh_task(pstate, matviewRel, into->schedule);
550+
}
551+
540552
table_close(matviewRel, NoLock);
541553
}
542554

@@ -1808,3 +1820,38 @@ get_primary_key_attnos_from_query(Query *query, List **constraintList)
18081820

18091821
return keys;
18101822
}
1823+
1824+
/*
1825+
* Create auto-refresh task for Dynamic Tables.
1826+
*/
1827+
static void
1828+
create_dynamic_table_auto_refresh_task(ParseState *pstate, Relation DynamicTableRel, char *schedule)
1829+
{
1830+
ObjectAddress refaddr;
1831+
ObjectAddress address;
1832+
StringInfoData buf;
1833+
char *dtname = NULL;
1834+
1835+
if (schedule == NULL)
1836+
schedule = DYNAMIC_TABLE_DEFAULT_REFRESH_INTERVAL;
1837+
1838+
/* Create auto refresh task. */
1839+
CreateTaskStmt *task_stmt = makeNode(CreateTaskStmt);
1840+
1841+
initStringInfo(&buf);
1842+
appendStringInfo(&buf, "gp_dynamic_table_refresh_%u", RelationGetRelid(DynamicTableRel));
1843+
task_stmt->taskname = pstrdup(buf.data);
1844+
task_stmt->schedule = pstrdup(schedule);
1845+
task_stmt->if_not_exists = false; /* report error if failed. */
1846+
dtname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(DynamicTableRel)),
1847+
RelationGetRelationName(DynamicTableRel));
1848+
resetStringInfo(&buf);
1849+
appendStringInfo(&buf, "REFRESH DYNAMIC TABLE %s", dtname);
1850+
task_stmt->sql = pstrdup(buf.data);
1851+
address = DefineTask(pstate, task_stmt);
1852+
1853+
refaddr.classId = RelationRelationId;
1854+
refaddr.objectId = RelationGetRelid(DynamicTableRel);
1855+
refaddr.objectSubId = 0;
1856+
recordDependencyOn(&address, &refaddr, DEPENDENCY_AUTO);
1857+
}

src/backend/commands/explain.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,12 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
550550
if (ctas->objtype == OBJECT_TABLE)
551551
ExplainDummyGroup("CREATE TABLE AS", NULL, es);
552552
else if (ctas->objtype == OBJECT_MATVIEW)
553-
ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
553+
{
554+
if(ctas->into && ctas->into->dynamicTbl)
555+
ExplainDummyGroup("CREATE DYNAMIC TABLE", NULL, es);
556+
else
557+
ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
558+
}
554559
else
555560
elog(ERROR, "unexpected object type: %d",
556561
(int) ctas->objtype);

src/backend/commands/matview.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,46 @@ MakeRefreshClause(bool concurrent, bool skipData, RangeVar *relation)
289289
return refreshClause;
290290
}
291291

292+
/*
293+
* SetMatViewIVMState
294+
* Mark a materialized view as IVM, or not.
295+
*
296+
* NOTE: caller must be holding an appropriate lock on the relation.
297+
*/
298+
void
299+
SetDynamicTableState(Relation relation)
300+
{
301+
Relation pgrel;
302+
HeapTuple tuple;
303+
304+
Assert(relation->rd_rel->relkind == RELKIND_MATVIEW);
305+
306+
/*
307+
* Update relation's pg_class entry. Crucial side-effect: other backends
308+
* (and this one too!) are sent SI message to make them rebuild relcache
309+
* entries.
310+
*/
311+
pgrel = table_open(RelationRelationId, RowExclusiveLock);
312+
tuple = SearchSysCacheCopy1(RELOID,
313+
ObjectIdGetDatum(RelationGetRelid(relation)));
314+
if (!HeapTupleIsValid(tuple))
315+
elog(ERROR, "cache lookup failed for relation %u",
316+
RelationGetRelid(relation));
317+
318+
((Form_pg_class) GETSTRUCT(tuple))->relisdynamic = true;
319+
320+
CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
321+
322+
heap_freetuple(tuple);
323+
table_close(pgrel, RowExclusiveLock);
324+
325+
/*
326+
* Advance command counter to make the updated pg_class row locally
327+
* visible.
328+
*/
329+
CommandCounterIncrement();
330+
}
331+
292332
/*
293333
* SetMatViewIVMState
294334
* Mark a materialized view as IVM, or not.
@@ -701,7 +741,12 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
701741
* completion tag output might break applications using it.
702742
*/
703743
if (qc)
704-
SetQueryCompletion(qc, CMDTAG_REFRESH_MATERIALIZED_VIEW, processed);
744+
{
745+
if (stmt->isdynamic)
746+
SetQueryCompletion(qc, CMDTAG_REFRESH_DYNAMIC_TABLE, processed);
747+
else
748+
SetQueryCompletion(qc, CMDTAG_REFRESH_MATERIALIZED_VIEW, processed);
749+
}
705750

706751
return address;
707752
}

src/backend/nodes/copyfuncs.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,8 @@ _copyIntoClause(const IntoClause *from)
18081808
COPY_SCALAR_FIELD(ivm);
18091809
COPY_SCALAR_FIELD(matviewOid);
18101810
COPY_STRING_FIELD(enrname);
1811+
COPY_SCALAR_FIELD(dynamicTbl);
1812+
COPY_STRING_FIELD(schedule);
18111813

18121814
return newnode;
18131815
}
@@ -4218,6 +4220,7 @@ _copyDropStmt(const DropStmt *from)
42184220
COPY_SCALAR_FIELD(behavior);
42194221
COPY_SCALAR_FIELD(missing_ok);
42204222
COPY_SCALAR_FIELD(concurrent);
4223+
COPY_SCALAR_FIELD(isdynamic);
42214224

42224225
return newnode;
42234226
}
@@ -4786,6 +4789,7 @@ _copyRefreshMatViewStmt(const RefreshMatViewStmt *from)
47864789
COPY_SCALAR_FIELD(concurrent);
47874790
COPY_SCALAR_FIELD(skipData);
47884791
COPY_NODE_FIELD(relation);
4792+
COPY_SCALAR_FIELD(isdynamic);
47894793

47904794
return newnode;
47914795
}

src/backend/nodes/equalfuncs.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ _equalIntoClause(const IntoClause *a, const IntoClause *b)
176176
COMPARE_SCALAR_FIELD(ivm);
177177
COMPARE_SCALAR_FIELD(matviewOid);
178178
COMPARE_STRING_FIELD(enrname);
179+
COMPARE_SCALAR_FIELD(dynamicTbl);
180+
COMPARE_STRING_FIELD(schedule);
179181
return true;
180182
}
181183

@@ -1480,6 +1482,7 @@ _equalDropStmt(const DropStmt *a, const DropStmt *b)
14801482
COMPARE_SCALAR_FIELD(behavior);
14811483
COMPARE_SCALAR_FIELD(missing_ok);
14821484
COMPARE_SCALAR_FIELD(concurrent);
1485+
COMPARE_SCALAR_FIELD(isdynamic);
14831486

14841487
return true;
14851488
}
@@ -1962,6 +1965,7 @@ _equalRefreshMatViewStmt(const RefreshMatViewStmt *a, const RefreshMatViewStmt *
19621965
COMPARE_SCALAR_FIELD(concurrent);
19631966
COMPARE_SCALAR_FIELD(skipData);
19641967
COMPARE_NODE_FIELD(relation);
1968+
COMPARE_SCALAR_FIELD(isdynamic);
19651969

19661970
return true;
19671971
}

src/backend/nodes/outfuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,8 @@ _outIntoClause(StringInfo str, const IntoClause *node)
12101210
WRITE_BOOL_FIELD(ivm);
12111211
WRITE_OID_FIELD(matviewOid);
12121212
WRITE_STRING_FIELD(enrname);
1213+
WRITE_BOOL_FIELD(dynamicTbl);
1214+
WRITE_STRING_FIELD(schedule);
12131215
}
12141216

12151217
static void

0 commit comments

Comments
 (0)