diff --git a/doc/src/sgml/ref/create_materialized_view.sgmlin b/doc/src/sgml/ref/create_materialized_view.sgmlin
index 1b514aabd..73b4bbfed 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgmlin
+++ b/doc/src/sgml/ref/create_materialized_view.sgmlin
@@ -24,7 +24,9 @@ PostgreSQL documentation
CREATE [ INCREMENTAL ] MATERIALIZED VIEW table_name
[ (column_name [, ...] ) ]
[ TABLESPACE tablespace_name ]
+ [ BUILD DEFERRED | BUILD IMMEDIATE ]
AS query
+ [ WITH [ NO ] DATA ]
diff --git a/doc/src/sgml/ref/create_materialized_view_log.sgmlin b/doc/src/sgml/ref/create_materialized_view_log.sgmlin
new file mode 100644
index 000000000..b7954e286
--- /dev/null
+++ b/doc/src/sgml/ref/create_materialized_view_log.sgmlin
@@ -0,0 +1,16 @@
+
+
+CREATE MATERIALIZED VIEW LOG
+7
+SQL - Language Statements
+
+
+CREATE MATERIALIZED VIEW LOG
+define a new materialized view log
+
+
+
+CREATE MATERIALIZED VIEW LOG ON table_name;
+
+
+
diff --git a/doc/src/sgml/ref/drop_materialized_view_log.sgmlin b/doc/src/sgml/ref/drop_materialized_view_log.sgmlin
new file mode 100644
index 000000000..32a69728e
--- /dev/null
+++ b/doc/src/sgml/ref/drop_materialized_view_log.sgmlin
@@ -0,0 +1,16 @@
+
+
+DROP MATERIALIZED VIEW LOG
+7
+SQL - Language Statements
+
+
+DROP MATERIALIZED VIEW LOG
+drop materialized view log
+
+
+
+DROP MATERIALIZED VIEW LOG ON table_name;
+
+
+
diff --git a/src/bin/pg_dump/pg_backup_archiver.cpp b/src/bin/pg_dump/pg_backup_archiver.cpp
index 103f5bb43..d92c5a862 100644
--- a/src/bin/pg_dump/pg_backup_archiver.cpp
+++ b/src/bin/pg_dump/pg_backup_archiver.cpp
@@ -3441,7 +3441,7 @@ static void _printTocEntry(ArchiveHandle* AH, TocEntry* te, RestoreOptions* ropt
strcmp(te->desc, "FK CONSTRAINT") == 0 || strcmp(te->desc, "INDEX") == 0 ||
strcmp(te->desc, "RULE") == 0 || strcmp(te->desc, "TRIGGER") == 0 ||
strcmp(te->desc, "USER MAPPING") == 0 || strcmp(te->desc, "PACKAGE BODY") == 0 ||
- strcmp(te->desc, "PACKAGE") == 0) {
+ strcmp(te->desc, "PACKAGE") == 0 || strcmp(te->desc, "MATERIALIZED VIEW LOG") == 0) {
/* these object types don't have separate owners */
} else {
write_msg(modulename, "WARNING: don't know how to set owner for object type %s\n", te->desc);
diff --git a/src/bin/pg_dump/pg_dump.cpp b/src/bin/pg_dump/pg_dump.cpp
index cb14cf07e..2eb2dd641 100644
--- a/src/bin/pg_dump/pg_dump.cpp
+++ b/src/bin/pg_dump/pg_dump.cpp
@@ -17170,6 +17170,50 @@ static int collectSecLabels(Archive* fout, SecLabelItem** items)
return ntups;
}
+/*
+* tryDumpMlogTable
+* Write out to fout the declarations of a materialized view log.
+*/
+static void tryDumpMlogTable(Archive* fout, TableInfo* tbinfo)
+{
+ PQExpBuffer query = createPQExpBuffer();
+ PQExpBuffer q = createPQExpBuffer();
+ PGresult* res = NULL;
+
+ appendPQExpBuffer(query,
+ "SELECT 1 FROM pg_catalog.gs_matview_log "
+ "WHERE relid = '%u'",
+ tbinfo->dobj.catId.oid);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ if (PQntuples(res) != 0) {
+ appendPQExpBuffer(q, "CREATE MATERIALIZED VIEW LOG ON %s;\n", fmtId(tbinfo->dobj.name));
+ ArchiveEntry(fout,
+ tbinfo->dobj.catId,
+ tbinfo->dobj.dumpId,
+ tbinfo->dobj.name,
+ tbinfo->dobj.nmspace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false,
+ "MATERIALIZED VIEW LOG",
+ SECTION_PRE_DATA,
+ q->data,
+ "",
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(q);
+ return;
+}
+
/*
* dumpTable
* write out to fout the declarations (not data) of a user-defined table
@@ -17252,6 +17296,8 @@ static void dumpTable(Archive* fout, TableInfo* tbinfo)
free(namecopy);
namecopy = NULL;
+
+ tryDumpMlogTable(fout, tbinfo);
}
}
diff --git a/src/common/backend/catalog/CMakeLists.txt b/src/common/backend/catalog/CMakeLists.txt
index 767b547cd..9cebf9915 100755
--- a/src/common/backend/catalog/CMakeLists.txt
+++ b/src/common/backend/catalog/CMakeLists.txt
@@ -13,7 +13,7 @@ set(POSTGRES_BKI_SRCS_S
@gs_masking_policy_filters.h @gs_encrypted_columns.h @gs_column_keys.h @gs_column_keys_args.h @gs_client_global_keys.h @gs_encrypted_proc.h
@gs_client_global_keys_args.h @pg_job.h @gs_asp.h @pg_job_proc.h @pg_extension_data_source.h @pg_statistic_ext.h
@pg_object.h @pg_synonym.h @toasting.h @indexing.h @gs_obsscaninfo.h @pg_directory.h @pg_hashbucket.h @gs_global_chain.h @gs_global_config.h
-@pg_streaming_stream.h @pg_streaming_cont_query.h @pg_streaming_reaper_status.h @gs_matview.h @gs_matview_dependency.h @pgxc_slice.h
+@pg_streaming_stream.h @pg_streaming_cont_query.h @pg_streaming_reaper_status.h @gs_matview.h @gs_matview_dependency.h @gs_matview_log.h @pgxc_slice.h
@gs_opt_model.h @pg_recyclebin.h @pg_snapshot.h @gs_model.h @gs_dependencies.h @gs_dependencies_obj.h @gs_package.h @gs_job_argument.h @gs_job_attribute.h @pg_uid.h @gs_db_privilege.h
@pg_replication_origin.h @pg_publication.h @pg_publication_rel.h @pg_subscription.h @gs_sql_patch.h @pg_subscription_rel.h @pg_proc_ext.h"
diff --git a/src/common/backend/catalog/Makefile b/src/common/backend/catalog/Makefile
index 752231006..e688100b3 100644
--- a/src/common/backend/catalog/Makefile
+++ b/src/common/backend/catalog/Makefile
@@ -59,7 +59,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_job.h gs_asp.h pg_job_proc.h pg_extension_data_source.h pg_statistic_ext.h pg_object.h pg_synonym.h \
toasting.h indexing.h gs_obsscaninfo.h pg_directory.h pg_hashbucket.h gs_global_chain.h gs_global_config.h\
pg_streaming_stream.h pg_streaming_cont_query.h pg_streaming_reaper_status.h gs_matview.h\
- gs_matview_dependency.h pgxc_slice.h gs_opt_model.h gs_dependencies.h gs_dependencies_obj.h gs_package.h gs_model.h\
+ gs_matview_dependency.h gs_matview_log.h pgxc_slice.h gs_opt_model.h gs_dependencies.h gs_dependencies_obj.h gs_package.h gs_model.h\
pg_recyclebin.h pg_snapshot.h gs_job_argument.h gs_job_attribute.h pg_uid.h gs_db_privilege.h\
pg_replication_origin.h pg_publication.h pg_publication_rel.h pg_subscription.h gs_sql_patch.h\
pg_subscription_rel.h pg_proc_ext.h\
diff --git a/src/common/backend/catalog/gs_matview.cpp b/src/common/backend/catalog/gs_matview.cpp
index dadfbf1d6..a02238627 100644
--- a/src/common/backend/catalog/gs_matview.cpp
+++ b/src/common/backend/catalog/gs_matview.cpp
@@ -23,6 +23,7 @@
#include "catalog/namespace.h"
#include "catalog/gs_matview.h"
#include "catalog/gs_matview_dependency.h"
+#include "catalog/gs_matview_log.h"
#include "catalog/objectaddress.h"
#include "catalog/dependency.h"
#include "commands/matview.h"
@@ -92,9 +93,9 @@ void update_matview_tuple(Oid matviewOid, bool needrefresh, Datum curtime)
rc = memset_s(values, sizeof(values), 0, sizeof(values));
securec_check(rc, "\0", "\0");
rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
- securec_check_c(rc, "\0", "\0");
+ securec_check(rc, "\0", "\0");
rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces));
- securec_check_c(rc, "\0", "\0");
+ securec_check(rc, "\0", "\0");
/*
* handle found.
@@ -264,6 +265,8 @@ try_delete_mlog_table(Relation matviewdep, Oid mlogid)
mlogobject.objectId = mlogid;
mlogobject.objectSubId = 0;
performDeletion(&mlogobject, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+
+ delete_matview_log_tuple(relid);
}
tableam_scan_end(scan);
@@ -340,6 +343,120 @@ void delete_matdep_table(Oid mlogid)
return;
}
+/*
+ * invalidate mlogids since mlog dropped
+ */
+void invalidate_matdep_mlog(Oid relid)
+{
+ HeapTuple tup;
+ HeapTuple newtuple;
+ TableScanDesc scan;
+ Form_gs_matview_dependency matviewDepForm;
+ Relation relation;
+ errno_t rc = 0;
+
+ /* preapre tuple for update. */
+ Datum values[Natts_gs_matview];
+ bool nulls[Natts_gs_matview];
+ bool replaces[Natts_gs_matview];
+
+ rc = memset_s(values, sizeof(values), 0, sizeof(values));
+ securec_check(rc, "\0", "\0");
+ rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
+ securec_check(rc, "\0", "\0");
+ rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces));
+ securec_check(rc, "\0", "\0");
+
+ /*
+ * handle found.
+ */
+ relation = heap_open(MatviewDependencyId, RowExclusiveLock);
+ scan = tableam_scan_begin(relation, SnapshotNow, 0, NULL);
+ tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
+
+ while (tup != NULL) {
+ matviewDepForm = (Form_gs_matview_dependency)GETSTRUCT(tup);
+ if (matviewDepForm->relid == relid) {
+ /* ok found tuple */
+ values[Anum_gs_matview_dep_mlogid - 1] = InvalidOid;
+ nulls[Anum_gs_matview_dep_mlogid - 1] = false;
+ replaces[Anum_gs_matview_dep_mlogid - 1] = true;
+ newtuple = heap_modify_tuple(tup, RelationGetDescr(relation),
+ values, nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ CatalogUpdateIndexes(relation, newtuple);
+ }
+ tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
+ }
+
+ /* Make the changes visible */
+ CommandCounterIncrement();
+
+ tableam_scan_end(scan);
+ heap_close(relation, NoLock);
+
+ return;
+}
+
+/*
+ * validate mlogids since mlog created
+ */
+void validate_matdep_mlog(Oid mlogid, Oid relid)
+{
+ HeapTuple tup;
+ HeapTuple newtuple;
+ TableScanDesc scan;
+ Form_gs_matview_dependency matviewDepForm;
+ Relation relation;
+ errno_t rc = 0;
+
+ /* preapre tuple for update. */
+ Datum values[Natts_gs_matview];
+ bool nulls[Natts_gs_matview];
+ bool replaces[Natts_gs_matview];
+
+ rc = memset_s(values, sizeof(values), 0, sizeof(values));
+ securec_check(rc, "\0", "\0");
+ rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls));
+ securec_check(rc, "\0", "\0");
+ rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces));
+ securec_check(rc, "\0", "\0");
+
+ /*
+ * handle found.
+ */
+ relation = heap_open(MatviewDependencyId, RowExclusiveLock);
+ scan = tableam_scan_begin(relation, SnapshotNow, 0, NULL);
+ tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
+
+ while (tup != NULL) {
+ matviewDepForm = (Form_gs_matview_dependency)GETSTRUCT(tup);
+ if (matviewDepForm->relid == relid) {
+ /* ok found tuple */
+ values[Anum_gs_matview_dep_mlogid - 1] = mlogid;
+ nulls[Anum_gs_matview_dep_mlogid - 1] = false;
+ replaces[Anum_gs_matview_dep_mlogid - 1] = true;
+ newtuple = heap_modify_tuple(tup, RelationGetDescr(relation),
+ values, nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ CatalogUpdateIndexes(relation, newtuple);
+ }
+ tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
+ }
+
+ /* Make the changes visible */
+ CommandCounterIncrement();
+
+ tableam_scan_end(scan);
+ heap_close(relation, NoLock);
+
+ return;
+}
+
Oid get_matview_mlog_baserelid(Oid mlogOid)
{
Relation relation = NULL;
@@ -659,3 +776,77 @@ void acquire_mativew_tables_lock(Query *query, bool incremental)
return;
}
+
+void insert_matview_log_tuple(Oid mlogid, Oid relid)
+{
+ errno_t rc;
+ Relation matview_log_relation;
+ HeapTuple gs_matview_log_htup;
+ bool gs_matview_log_nulls[Natts_gs_matview_log];
+ Datum gs_matview_log_values[Natts_gs_matview_log];
+
+ matview_log_relation = heap_open(MatviewLogRelationId, RowExclusiveLock);
+
+ rc = memset_s(gs_matview_log_values, sizeof(gs_matview_log_values), 0, sizeof(gs_matview_log_values));
+ securec_check(rc, "\0", "\0");
+ rc = memset_s(gs_matview_log_nulls, sizeof(gs_matview_log_nulls), false, sizeof(gs_matview_log_nulls));
+ securec_check(rc, "\0", "\0");
+
+ gs_matview_log_values[Anum_gs_matview_log_mlogid - 1] = ObjectIdGetDatum(mlogid);
+ gs_matview_log_values[Anum_gs_matview_relid - 1] = ObjectIdGetDatum(relid);
+
+ gs_matview_log_nulls[Anum_gs_matview_log_mlogid - 1] = false;
+ gs_matview_log_nulls[Anum_gs_matview_relid - 1] = false;
+
+ gs_matview_log_htup = heap_form_tuple(matview_log_relation->rd_att, gs_matview_log_values, gs_matview_log_nulls);
+
+ /* Do the insertion */
+ (void)simple_heap_insert(matview_log_relation, gs_matview_log_htup);
+
+ CatalogUpdateIndexes(matview_log_relation, gs_matview_log_htup);
+
+ /* Make the changes visible */
+ CommandCounterIncrement();
+
+ heap_close(matview_log_relation, NoLock);
+ return;
+}
+
+Oid delete_matview_log_tuple(Oid relid)
+{
+ Oid mlogid = InvalidOid;
+ Relation relation = NULL;
+ TableScanDesc scan;
+ Form_gs_matview_log matviewLogForm;
+ HeapTuple tup = NULL;
+
+ relation = heap_open(MatviewLogRelationId, RowExclusiveLock);
+ scan = tableam_scan_begin(relation, SnapshotNow, 0, NULL);
+ tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
+
+ while (tup != NULL) {
+ matviewLogForm = (Form_gs_matview_log)GETSTRUCT(tup);
+ if (matviewLogForm->relid == relid) {
+ /* ok found tuple */
+ mlogid = matviewLogForm->mlogid;
+ break;
+ }
+ tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
+ }
+
+ if (!HeapTupleIsValid(tup)) {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("there is no materialized view log on this table")));
+ }
+
+ simple_heap_delete(relation, &tup->t_self);
+
+ tableam_scan_end(scan);
+ heap_close(relation, NoLock);
+
+ /* Make the changes visible */
+ CommandCounterIncrement();
+
+ return mlogid;
+}
diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp
index fbbbde7e2..b8f45b4f4 100644
--- a/src/common/backend/nodes/copyfuncs.cpp
+++ b/src/common/backend/nodes/copyfuncs.cpp
@@ -6096,6 +6096,24 @@ _copyRefreshMatViewStmt(const RefreshMatViewStmt *from)
return newnode;
}
+static CreateMatViewLogStmt* _copyCreateMatViewLogStmt(const CreateMatViewLogStmt* from)
+{
+ CreateMatViewLogStmt* newnode = makeNode(CreateMatViewLogStmt);
+
+ COPY_NODE_FIELD(relation);
+
+ return newnode;
+}
+
+static DropMatViewLogStmt* _copyDropMatViewLogStmt(const DropMatViewLogStmt* from)
+{
+ DropMatViewLogStmt* newnode = makeNode(DropMatViewLogStmt);
+
+ COPY_NODE_FIELD(relation);
+
+ return newnode;
+}
+
static CreateSeqStmt* _copyCreateSeqStmt(const CreateSeqStmt* from)
{
CreateSeqStmt* newnode = makeNode(CreateSeqStmt);
@@ -8662,6 +8680,12 @@ void* copyObject(const void* from)
case T_RefreshMatViewStmt:
retval = _copyRefreshMatViewStmt((RefreshMatViewStmt*)from);
break;
+ case T_CreateMatViewLogStmt:
+ retval = _copyCreateMatViewLogStmt((CreateMatViewLogStmt*)from);
+ break;
+ case T_DropMatViewLogStmt:
+ retval = _copyDropMatViewLogStmt((DropMatViewLogStmt*)from);
+ break;
case T_ReplicaIdentityStmt:
retval = _copyReplicaIdentityStmt((ReplicaIdentityStmt*)from);
break;
diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp
index 81c433b45..cb1a9df83 100644
--- a/src/common/backend/nodes/equalfuncs.cpp
+++ b/src/common/backend/nodes/equalfuncs.cpp
@@ -1950,6 +1950,20 @@ static bool _equalRefreshMatViewStmt(const RefreshMatViewStmt *a, const RefreshM
return true;
}
+static bool _equalCreateMatViewLogStmt(const CreateMatViewLogStmt* a, const CreateMatViewLogStmt* b)
+{
+ COMPARE_NODE_FIELD(relation);
+
+ return true;
+}
+
+static bool _equalDropMatViewLogStmt(const DropMatViewLogStmt* a, const DropMatViewLogStmt* b)
+{
+ COMPARE_NODE_FIELD(relation);
+
+ return true;
+}
+
static bool _equalReplicaIdentityStmt(const ReplicaIdentityStmt* a, const ReplicaIdentityStmt* b)
{
COMPARE_SCALAR_FIELD(identity_type);
@@ -4179,6 +4193,12 @@ bool equal(const void* a, const void* b)
case T_RefreshMatViewStmt:
retval = _equalRefreshMatViewStmt((RefreshMatViewStmt *)a, (RefreshMatViewStmt *)b);
break;
+ case T_CreateMatViewLogStmt:
+ retval = _equalCreateMatViewLogStmt((CreateMatViewLogStmt*)a, (CreateMatViewLogStmt*)b);
+ break;
+ case T_DropMatViewLogStmt:
+ retval = _equalDropMatViewLogStmt((DropMatViewLogStmt*)a, (DropMatViewLogStmt*)b);
+ break;
case T_CreateSeqStmt:
retval = _equalCreateSeqStmt((CreateSeqStmt*)a, (CreateSeqStmt*)b);
break;
diff --git a/src/common/backend/nodes/nodes.cpp b/src/common/backend/nodes/nodes.cpp
index 538b1887f..ce21ef1b0 100755
--- a/src/common/backend/nodes/nodes.cpp
+++ b/src/common/backend/nodes/nodes.cpp
@@ -642,7 +642,9 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"},
{T_CondInfo, "CondInfo"},
{T_GetDiagStmt, "GetDiagStmt"},
{T_DolphinCallStmt, "DolphinCallStmt"},
- {T_CallContext, "CallContext"}
+ {T_CallContext, "CallContext"},
+ {T_CreateMatViewLogStmt, "CreateMatViewLogStmt"},
+ {T_DropMatViewLogStmt, "DropMatViewLogStmt"}
};
char* nodeTagToString(NodeTag tag)
diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp
index e70984e91..52e64ea4b 100755
--- a/src/common/backend/nodes/outfuncs.cpp
+++ b/src/common/backend/nodes/outfuncs.cpp
@@ -4141,6 +4141,18 @@ static void _outAlterTriggerStmt(StringInfo str, AlterTriggerStmt *node)
WRITE_CHAR_FIELD(tgenabled);
}
+static void _outCreateMatViewLogStmt(StringInfo str, CreateMatViewLogStmt* node)
+{
+ WRITE_NODE_TYPE("CREATEMATVIEWLOG");
+ WRITE_NODE_FIELD(relation);
+}
+
+static void _outDropMatViewLogStmt(StringInfo str, DropMatViewLogStmt* node)
+{
+ WRITE_NODE_TYPE("DROPMATVIEWLOG");
+ WRITE_NODE_FIELD(relation);
+}
+
static void _outCopyStmt(StringInfo str, CopyStmt* node)
{
WRITE_NODE_TYPE("COPY");
@@ -6961,6 +6973,12 @@ static void _outNode(StringInfo str, const void* obj)
case T_AlterTriggerStmt:
_outAlterTriggerStmt(str, (AlterTriggerStmt*)obj);
break;
+ case T_CreateMatViewLogStmt:
+ _outCreateMatViewLogStmt(str, (CreateMatViewLogStmt*)obj);
+ break;
+ case T_DropMatViewLogStmt:
+ _outDropMatViewLogStmt(str, (DropMatViewLogStmt*)obj);
+ break;
case T_SelectStmt:
_outSelectStmt(str, (SelectStmt*)obj);
break;
diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp
index f6d48f8a1..82598b9ae 100755
--- a/src/common/backend/nodes/readfuncs.cpp
+++ b/src/common/backend/nodes/readfuncs.cpp
@@ -1733,6 +1733,20 @@ static AlterTriggerStmt* _readAlterTriggerStmt(void)
READ_LOCALS(AlterTriggerStmt);
READ_STRING_FIELD(trigname);
READ_CHAR_FIELD(tgenabled);
+}
+
+static CreateMatViewLogStmt* _readCreateMatViewLogStmt(void)
+{
+ READ_LOCALS(CreateMatViewLogStmt);
+ READ_NODE_FIELD(relation);
+
+ READ_DONE();
+}
+
+static DropMatViewLogStmt* _readDropMatViewLogStmt(void)
+{
+ READ_LOCALS(DropMatViewLogStmt);
+ READ_NODE_FIELD(relation);
READ_DONE();
}
@@ -7071,6 +7085,10 @@ Node* parseNodeString(void)
return_value = _readCopyStmt();
} else if (MATCH("ALTERTABLE", 10)) {
return_value = _readAlterTableStmt();
+ } else if (MATCH("CREATEMATVIEWLOG", 16)) {
+ return_value = _readCreateMatViewLogStmt();
+ } else if (MATCH("DROPMATVIEWLOG", 14)) {
+ return_value = _readDropMatViewLogStmt();
} else if (MATCH("PLDEBUG_VARIABLE", 16)) {
return_value = _readPLDebug_variable();
} else if (MATCH("PLDEBUG_BREAKPOINT", 18)) {
diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y
index ea91ef32e..b6d77aa03 100644
--- a/src/common/backend/parser/gram.y
+++ b/src/common/backend/parser/gram.y
@@ -407,7 +407,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner);
CreateResourcePoolStmt AlterResourcePoolStmt DropResourcePoolStmt
CreateWorkloadGroupStmt AlterWorkloadGroupStmt DropWorkloadGroupStmt
CreateAppWorkloadGroupMappingStmt AlterAppWorkloadGroupMappingStmt DropAppWorkloadGroupMappingStmt
- MergeStmt PurgeStmt CreateMatViewStmt RefreshMatViewStmt CreateAmStmt
+ MergeStmt PurgeStmt CreateMatViewStmt RefreshMatViewStmt CreateMatViewLogStmt DropMatViewLogStmt CreateAmStmt
CreateWeakPasswordDictionaryStmt DropWeakPasswordDictionaryStmt
AlterGlobalConfigStmt DropGlobalConfigStmt
CreatePublicationStmt AlterPublicationStmt
@@ -864,6 +864,10 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner);
%type statement_information_item condition_information_item
%type condition_number
%type condition_information statement_information
+
+/* MATVIEW */
+%type build_deferred
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -891,7 +895,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner);
ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUDIT AUTHID AUTHORIZATION AUTOEXTEND AUTOMAPPED AUTO_INCREMENT
BACKWARD BARRIER BEFORE BEGIN_NON_ANOYBLOCK BEGIN_P BETWEEN BIGINT BINARY BINARY_DOUBLE BINARY_DOUBLE_INF BINARY_DOUBLE_NAN BINARY_INTEGER BIT BLANKS
- BLOB_P BLOCKCHAIN BODY_P BOGUS BOOLEAN_P BOTH BUCKETCNT BUCKETS BY BYTE_P BYTEAWITHOUTORDER BYTEAWITHOUTORDERWITHEQUAL
+ BLOB_P BLOCKCHAIN BODY_P BOGUS BOOLEAN_P BOTH BUCKETCNT BUCKETS BUILD BY BYTE_P BYTEAWITHOUTORDER BYTEAWITHOUTORDERWITHEQUAL
CACHE CALL CALLED CANCELABLE CASCADE CASCADED CASE CAST CATALOG_P CATALOG_NAME CHAIN CHANGE CHAR_P
CHARACTER CHARACTERISTICS CHARACTERSET CHARSET CHECK CHECKPOINT CLASS CLASS_ORIGIN CLEAN CLIENT CLIENT_MASTER_KEY CLIENT_MASTER_KEYS CLOB CLOSE
@@ -934,8 +938,8 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner);
LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF LINES
LEAST LESS LEFT LEVEL LIKE LIMIT LIST LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
- LOCATION LOCK_P LOCKED LOG_P LOGGING LOGIN_ANY LOGIN_FAILURE LOGIN_SUCCESS LOGOUT LOOP
- MAPPING MASKING MASTER MATCH MATERIALIZED MATCHED MAXEXTENTS MAXSIZE MAXTRANS MAXVALUE MERGE MESSAGE_TEXT METHOD MINUS_P MINUTE_P MINUTE_SECOND_P MINVALUE MINEXTENTS MODE
+ LOCATION LOCK_P LOCKED LOG_ON LOG_P LOGGING LOGIN_ANY LOGIN_FAILURE LOGIN_SUCCESS LOGOUT LOOP
+ MAPPING MASKING MASTER MATCH MATERIALIZED MATCHED MAXEXTENTS MAXSIZE MAXTRANS MAXVALUE MERGE MESSAGE_TEXT METHOD MINUS_P MINUTE_P MINUTE_SECOND_P MINVALUE MINEXTENTS MODE
MODEL MODIFY_P MONTH_P MOVE MOVEMENT MYSQL_ERRNO
// DB4AI
NAME_P NAMES NAN_P NATIONAL NATURAL NCHAR NEXT NO NOCOMPRESS NOCYCLE NODE NOLOGGING NOMAXVALUE NOMINVALUE NONE
@@ -1264,6 +1268,8 @@ stmt :
| CreatePackageBodyStmt
| CreateGroupStmt
| CreateMatViewStmt
+ | CreateMatViewLogStmt
+ | DropMatViewLogStmt
| CreateModelStmt // DB4AI
| CreateNodeGroupStmt
| CreateNodeStmt
@@ -9977,7 +9983,7 @@ OptSnapshotStratify:
*****************************************************************************/
CreateMatViewStmt:
- CREATE OptNoLog opt_incremental MATERIALIZED VIEW create_mv_target AS SelectStmt opt_with_data
+ CREATE OptNoLog INCREMENTAL MATERIALIZED VIEW create_mv_target AS SelectStmt opt_with_data
{
CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
ctas->query = $8;
@@ -9987,14 +9993,8 @@ CreateMatViewStmt:
/* cram additional flags into the IntoClause */
$6->rel->relpersistence = $2;
$6->skipData = !($9);
- if ($6->skipData) {
- const char* message = "WITH NO DATA for materialized views not yet supported";
- InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc);
- ereport(errstate,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("WITH NO DATA for materialized views not yet supported")));
- }
- if ($3 && $6->options) {
+ $6->ivm = true;
+ if ($6->options) {
const char* message = "options for incremental materialized views not yet supported";
InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc);
ereport(errstate,
@@ -10002,7 +10002,7 @@ CreateMatViewStmt:
errmsg("options for incremental materialized views not yet supported")));
}
#ifndef ENABLE_MULTIPLE_NODES
- if ($3 && $6->distributeby) {
+ if ($6->distributeby) {
const char* message = "It's not supported to specify distribute key on incremental materialized views";
InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc);
ereport(errstate,
@@ -10014,10 +10014,47 @@ CreateMatViewStmt:
ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("matview is not supported while DMS and DSS enabled.")));
}
-
- $6->ivm = $3;
+
$$ = (Node *) ctas;
}
+ | CREATE MATERIALIZED VIEW create_mv_target build_deferred AS SelectStmt
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+ ctas->query = $7;
+ ctas->into = $4;
+ ctas->relkind = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ /* cram additional flags into the IntoClause */
+ $4->rel->relpersistence = RELPERSISTENCE_PERMANENT;
+ $4->skipData = $5;
+ $4->ivm = false;
+
+ if (ENABLE_DMS) {
+ ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("matview is not supported while DMS and DSS enabled.")));
+ }
+
+ $$ = (Node *) ctas;
+ }
+ | CREATE MATERIALIZED VIEW create_mv_target AS SelectStmt opt_with_data
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+ ctas->query = $6;
+ ctas->into = $4;
+ ctas->relkind = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ /* cram additional flags into the IntoClause */
+ $4->rel->relpersistence = RELPERSISTENCE_PERMANENT;
+ $4->skipData = !($7);
+ $4->ivm = false;
+
+ if (ENABLE_DMS) {
+ ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("matview is not supported while DMS and DSS enabled.")));
+ }
+
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
@@ -10051,6 +10088,10 @@ opt_incremental:
| /*EMPTY*/ { $$ = FALSE; }
;
+build_deferred:
+ BUILD DEFERRED { $$ = TRUE; }
+ | BUILD IMMEDIATE { $$ = FALSE; }
+ ;
/*****************************************************************************
*
@@ -10077,6 +10118,23 @@ RefreshMatViewStmt:
}
;
+CreateMatViewLogStmt:
+ CREATE MATERIALIZED VIEW LOG_ON qualified_name
+ {
+ CreateMatViewLogStmt* stmt = makeNode(CreateMatViewLogStmt);
+ stmt->relation = $5;
+ $$ = (Node*)stmt;
+ }
+ ;
+
+DropMatViewLogStmt:
+ DROP MATERIALIZED VIEW LOG_ON qualified_name
+ {
+ DropMatViewLogStmt* stmt = makeNode(DropMatViewLogStmt);
+ stmt->relation = $5;
+ $$ = (Node*)stmt;
+ }
+ ;
/*****************************************************************************
*
@@ -30007,6 +30065,7 @@ unreserved_keyword:
| BLOB_P
| BLOCKCHAIN
| BODY_P
+ | BUILD
| BY
| BYTE_P
| CACHE
diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp
index f35542d0b..c39261eb5 100644
--- a/src/common/backend/parser/parser.cpp
+++ b/src/common/backend/parser/parser.cpp
@@ -336,6 +336,26 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner)
break;
}
break;
+ case LOG_P:
+ /*
+ * For materialized view log, view log must be reduced to one token
+ */
+ GET_NEXT_TOKEN();
+
+ switch (next_token) {
+ case ON:
+ cur_token = LOG_ON;
+ break;
+ default:
+ /* save the lookahead token for next time */
+ SET_LOOKAHEAD_TOKEN();
+
+ /* and back up the output info to cur_token */
+ lvalp->core_yystype = cur_yylval;
+ *llocp = cur_yylloc;
+ break;
+ }
+ break;
case WITH:
/*
* WITH TIME must be reduced to one token
diff --git a/src/common/backend/utils/cache/relcache.cpp b/src/common/backend/utils/cache/relcache.cpp
index 0909332a2..6a01f5eaa 100755
--- a/src/common/backend/utils/cache/relcache.cpp
+++ b/src/common/backend/utils/cache/relcache.cpp
@@ -144,6 +144,7 @@
#include "catalog/gs_db_privilege.h"
#include "catalog/gs_matview.h"
#include "catalog/gs_matview_dependency.h"
+#include "catalog/gs_matview_log.h"
#include "catalog/pg_snapshot.h"
#include "catalog/gs_opt_model.h"
#include "catalog/gs_global_chain.h"
@@ -328,6 +329,7 @@ static const FormData_pg_attribute Desc_gs_masking_policy_filters[Natts_gs_maski
static const FormData_pg_attribute Desc_gs_asp[Natts_gs_asp] = {Schema_gs_asp};
static const FormData_pg_attribute Desc_gs_matview[Natts_gs_matview] = {Schema_gs_matview};
static const FormData_pg_attribute Desc_gs_matview_dependency[Natts_gs_matview_dependency] = {Schema_gs_matview_dependency};
+static const FormData_pg_attribute Desc_gs_matview_log[Natts_gs_matview_log] = {Schema_gs_matview_log};
static const FormData_pg_attribute Desc_pgxc_slice[Natts_pgxc_slice] = {Schema_pgxc_slice};
static const FormData_pg_attribute Desc_gs_column_keys[Natts_gs_column_keys] = {Schema_gs_column_keys};
@@ -1252,6 +1254,15 @@ static struct CatalogRelationBuildParam catalogBuildParam[CATALOG_NUM] = {{Defau
Desc_gs_encrypted_proc,
false,
true},
+ {MatviewLogRelationId,
+ "gs_matview_log",
+ MatviewLogRelationId_Rowtype_Id,
+ false,
+ false,
+ Natts_gs_matview_log,
+ Desc_gs_matview_log,
+ false,
+ true},
{MatviewRelationId,
"gs_matview",
MatviewRelationId_Rowtype_Id,
@@ -2456,7 +2467,12 @@ static Relation RelationBuildDescExtended(Oid targetRelId, bool insertIt, bool b
* mlog oid
*/
if (!IsCatalogRelation(relation)) {
- relation->rd_mlogoid = find_matview_mlog_table(relid);
+ /* if do not have incremental matview, no need to insert into mlog */
+ if (is_table_in_incre_matview(relid)) {
+ relation->rd_mlogoid = find_matview_mlog_table(relid);
+ } else {
+ relation->rd_mlogoid = InvalidOid;
+ }
}
/*
diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp
index c0c607246..5630f8117 100644
--- a/src/common/backend/utils/init/globals.cpp
+++ b/src/common/backend/utils/init/globals.cpp
@@ -77,7 +77,7 @@ bool will_shutdown = false;
*
********************************************/
-const uint32 GRAND_VERSION_NUM = 93001;
+const uint32 GRAND_VERSION_NUM = 93002;
/********************************************
* 2.VERSION NUM FOR EACH FEATURE
diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y
index 2193197d7..21b352f3a 100755
--- a/src/common/interfaces/libpq/frontend_parser/gram.y
+++ b/src/common/interfaces/libpq/frontend_parser/gram.y
@@ -516,7 +516,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus;
ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUDIT AUDIT_POLICY AUTHID AUTHORIZATION AUTOEXTEND AUTOMAPPED AUTO_INCREMENT
BACKWARD BARRIER BEFORE BEGIN_NON_ANOYBLOCK BEGIN_P BETWEEN BIGINT BINARY BINARY_DOUBLE BINARY_DOUBLE_INF BINARY_DOUBLE_NAN BINARY_INTEGER BIT BLANKS BLOB_P BLOCKCHAIN BODY_P BOGUS
- BOOLEAN_P BOTH BUCKETCNT BUCKETS BY BYTE_P BYTEAWITHOUTORDER BYTEAWITHOUTORDERWITHEQUAL
+ BOOLEAN_P BOTH BUCKETCNT BUCKETS BUILD BY BYTE_P BYTEAWITHOUTORDER BYTEAWITHOUTORDERWITHEQUAL
CACHE CALL CALLED CANCELABLE CASCADE CASCADED CASE CAST CATALOG_P CATALOG_NAME CHAIN CHANGE CHAR_P
CHARACTER CHARACTERISTICS CHARACTERSET CHECK CHECKPOINT CHARSET CLASS CLASS_ORIGIN CLEAN CLIENT CLIENT_MASTER_KEY CLIENT_MASTER_KEYS CLOB CLOSE
@@ -11692,6 +11692,7 @@ unreserved_keyword:
| BLOB_P
| BLOCKCHAIN
| BODY_P
+ | BUILD
| BY
| BYTE_P
| CACHE
diff --git a/src/gausskernel/optimizer/commands/event_trigger.cpp b/src/gausskernel/optimizer/commands/event_trigger.cpp
index a522c97b9..1631f0f91 100644
--- a/src/gausskernel/optimizer/commands/event_trigger.cpp
+++ b/src/gausskernel/optimizer/commands/event_trigger.cpp
@@ -80,6 +80,7 @@ static const event_trigger_support_data event_trigger_support[] = {
{"LANGUAGE", true},
{"LARGE SEQUENCE", true},
{"MATERIALIZED VIEW", true},
+ {"MATERIALIZED VIEW LOG", true},
{"OPERATOR", true},
{"OPERATOR CLASS", true},
{"OPERATOR FAMILY", true},
diff --git a/src/gausskernel/optimizer/commands/matview.cpp b/src/gausskernel/optimizer/commands/matview.cpp
index 5859a3e30..138126905 100755
--- a/src/gausskernel/optimizer/commands/matview.cpp
+++ b/src/gausskernel/optimizer/commands/matview.cpp
@@ -27,6 +27,7 @@
#include "catalog/namespace.h"
#include "catalog/gs_matview.h"
#include "catalog/gs_matview_dependency.h"
+#include "catalog/gs_matview_log.h"
#include "catalog/toasting.h"
#include "catalog/cstore_ctlg.h"
#include "commands/cluster.h"
@@ -807,6 +808,170 @@ static void ExecutorRefreshMatInc(QueryDesc* queryDesc, Query *query,
return;
}
+/*
+* Guarantee mlog created time bigger than mview last refresh time
+*/
+static void checkTimeChangeForMlog(Oid mlogid, Oid relid)
+{
+ Relation rel = NULL;
+ TableScanDesc scan;
+ ScanKeyData scanKey;
+ HeapTuple tup = NULL;
+ Form_gs_matview_dependency dep;
+ Datum createTime;
+ Datum createTimeTZ;
+ bool isTimeNULL = false;
+ bool ctimeIsNULL = false;
+
+ Relation pgObjectRelation = heap_open(PgObjectRelationId, RowExclusiveLock);
+ TupleDesc pgObjectTupdesc = RelationGetDescr(pgObjectRelation);
+ tup = SearchSysCache2(PGOBJECTID, ObjectIdGetDatum(mlogid), CharGetDatum(RELKIND_RELATION));
+ if (!HeapTupleIsValid(tup)) {
+ return;
+ }
+
+ createTimeTZ = heap_getattr(tup, Anum_pg_object_ctime, pgObjectTupdesc, &ctimeIsNULL);
+ createTime = DirectFunctionCall1(timestamptz_timestamp, DatumGetTimestampTz(createTimeTZ));
+
+ ReleaseSysCache(tup);
+ heap_close(pgObjectRelation, RowExclusiveLock);
+
+ ScanKeyInit(&scanKey,
+ Anum_gs_matview_dep_relid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(relid));
+ rel = heap_open(MatviewDependencyId, AccessShareLock);
+ scan = tableam_scan_begin(rel, SnapshotNow, 1, &scanKey);
+
+ while ((tup = (HeapTuple)tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
+ dep = (Form_gs_matview_dependency)GETSTRUCT(tup);
+ Datum oldTime = get_matview_refreshtime(dep->matviewid, &isTimeNULL);
+ if (timestamp_cmp_internal(DatumGetTimestamp(createTime), DatumGetTimestamp(oldTime)) <= 0) {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("system time cannot be smaller than last refresh time")));
+ }
+ }
+
+ tableam_scan_end(scan);
+ heap_close(rel, NoLock);
+}
+
+static void checkTimeChangeForRelativeMlog(List *mlogidList, Datum curtime, Datum curMViewOldTime, bool incRefresh)
+{
+ Relation rel = NULL;
+ TableScanDesc scan;
+ ScanKeyData scanKey;
+ HeapTuple tup = NULL;
+ Form_gs_matview_dependency dep;
+ ListCell *mlogidCell = NULL;
+ Oid mlogid = InvalidOid;
+ TupleDesc pgObjectTupdesc;
+ bool isTimeNULL = false;
+ bool isNewMlog = false;
+
+ /* Compare with relevant mlog */
+ foreach (mlogidCell, mlogidList) {
+ mlogid = lfirst_oid(mlogidCell);
+ rel = heap_open(PgObjectRelationId, RowExclusiveLock);
+ pgObjectTupdesc = RelationGetDescr(rel);
+ tup = SearchSysCache2(PGOBJECTID, ObjectIdGetDatum(mlogid), CharGetDatum(RELKIND_RELATION));
+ if (!HeapTupleIsValid(tup)) {
+ return;
+ }
+
+ Datum createTimeTZ = heap_getattr(tup, Anum_pg_object_ctime, pgObjectTupdesc, &isTimeNULL);
+ Datum createTime = DirectFunctionCall1(timestamptz_timestamp, DatumGetTimestampTz(createTimeTZ));
+
+ ReleaseSysCache(tup);
+ heap_close(rel, RowExclusiveLock);
+
+ if (timestamp_cmp_internal(DatumGetTimestamp(curtime), DatumGetTimestamp(createTime)) <= 0) {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("system time cannot be smaller than mlog created time")));
+ }
+
+ isNewMlog = timestamp_cmp_internal(DatumGetTimestamp(curMViewOldTime), DatumGetTimestamp(createTime)) <= 0;
+ if (incRefresh && isNewMlog) {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialized view log younger than last refresh")));
+ }
+
+ ScanKeyInit(&scanKey,
+ Anum_gs_matview_dep_mlogid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(mlogid));
+ rel = heap_open(MatviewDependencyId, AccessShareLock);
+ scan = tableam_scan_begin(rel, SnapshotNow, 1, &scanKey);
+
+ /* Compare current time with the last refresh time of relevant mviews */
+ while ((tup = (HeapTuple)tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
+ dep = (Form_gs_matview_dependency)GETSTRUCT(tup);
+ Datum otherMViewOldTime = get_matview_refreshtime(dep->matviewid, &isTimeNULL);
+ if (timestamp_cmp_internal(DatumGetTimestamp(curtime), DatumGetTimestamp(otherMViewOldTime)) <= 0) {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("system time cannot be smaller than last refresh time")));
+ }
+ }
+
+ tableam_scan_end(scan);
+ heap_close(rel, NoLock);
+ }
+
+ list_free(mlogidList);
+}
+
+/*
+* Incremental mview refresh time must bigger than mlog created time and relative mview last refresh time.
+*/
+static void checkTimeChangeForMView(Oid matviewid, Datum curtime, bool incRefresh)
+{
+ Relation rel = NULL;
+ TableScanDesc scan;
+ ScanKeyData scanKey;
+ HeapTuple tup = NULL;
+ Form_gs_matview_dependency dep;
+ List *mlogidList = NULL;
+ bool isTimeNULL = false;
+
+ /* Compare current time with the last refresh time of current matview */
+ Datum curMViewOldTime = get_matview_refreshtime(matviewid, &isTimeNULL);
+
+ if (isTimeNULL) {
+ if (incRefresh) {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialized view must use complete refresh first")));
+ }
+ } else if (timestamp_cmp_internal(DatumGetTimestamp(curtime), DatumGetTimestamp(curMViewOldTime)) <= 0) {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("system time cannot be smaller than last refresh time")));
+ }
+
+ ScanKeyInit(&scanKey,
+ Anum_gs_matview_dep_matviewid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(matviewid));
+ rel = heap_open(MatviewDependencyId, AccessShareLock);
+ scan = tableam_scan_begin(rel, SnapshotNow, 1, &scanKey);
+
+ while ((tup = (HeapTuple)tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
+ dep = (Form_gs_matview_dependency)GETSTRUCT(tup);
+ if (dep->mlogid != InvalidOid) {
+ mlogidList = lappend_oid(mlogidList, dep->mlogid);
+ } else if (incRefresh) {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("missing mlog for incrementally refresh")));
+ }
+ }
+
+ tableam_scan_end(scan);
+ heap_close(rel, NoLock);
+
+ checkTimeChangeForRelativeMlog(mlogidList, curtime, curMViewOldTime, incRefresh);
+}
+
ObjectAddress ExecRefreshMatViewInc(RefreshMatViewStmt *stmt, const char *queryString,
ParamListInfo params, char *completionTag)
{
@@ -816,7 +981,6 @@ ObjectAddress ExecRefreshMatViewInc(RefreshMatViewStmt *stmt, const char *queryS
Query *query = NULL;
PlannedStmt* plan = NULL;
QueryDesc* queryDesc = NULL;
- bool isTimeNULL = false;
Datum curtime;
Oid save_userid;
int save_sec_context;
@@ -835,11 +999,8 @@ ObjectAddress ExecRefreshMatViewInc(RefreshMatViewStmt *stmt, const char *queryS
RangeVarCallbackOwnsMatView, NULL);
Oid mapid = DatumGetObjectId(get_matview_mapid(matviewOid));
- Datum oldTime = get_matview_refreshtime(matviewOid, &isTimeNULL);
- if (timestamp_cmp_internal(DatumGetTimestamp(curtime),
- DatumGetTimestamp(oldTime)) <= 0) {
- return InvalidObjectAddress;
- }
+
+ checkTimeChangeForMView(matviewOid, curtime, true);
matviewRel = heap_open(matviewOid, ExclusiveLock);
@@ -975,6 +1136,7 @@ ObjectAddress ExecRefreshIncMatViewAll(RefreshMatViewStmt *stmt, const char *que
RelationGetRelationName(matviewRel))));
}
+ checkTimeChangeForMView(matviewOid, curtime, false);
Assert(!IsSystemRelation(matviewRel));
Assert(!matviewRel->rd_rel->relhasoids);
@@ -1248,7 +1410,9 @@ static void ExecCreateMatInc(QueryDesc*queryDesc, Query *query, Relation matview
ListCell *idx = NULL;
/* update tuples in mlog which refreshtime if NULL based on current */
- update_mlog_time_all(mlogid, curtime);
+ if (mlogid != InvalidOid) {
+ update_mlog_time_all(mlogid, curtime);
+ }
index = get_index_ref(queryDesc, relid);
@@ -1363,10 +1527,15 @@ ObjectAddress ExecCreateMatViewInc(CreateTableAsStmt* stmt, const char* querySt
matviewid = RelationGetRelid(matview);
mapid = DatumGetObjectId(get_matview_mapid(matviewid));
- /* update matview metadata of gs_matview. */
- update_matview_tuple(matviewid, true, curtime);
+ if (!stmt->into->skipData) {
+ checkTimeChangeForMView(matviewid, curtime, false);
+
+ /* update matview metadata of gs_matview. */
+ update_matview_tuple(matviewid, true, curtime);
+
+ ExecCreateMatInc(queryDesc, query, matview, mapid, curtime);
+ }
- ExecCreateMatInc(queryDesc, query, matview, mapid, curtime);
address = ((DR_intorel *) dest)->reladdr;
(*dest->rShutdown)(dest);
@@ -1783,6 +1952,8 @@ static Oid create_mlog_table(Oid relid)
AlterTableCreateToastTable(mlogid, reloptions);
+ insert_matview_log_tuple(mlogid, relid);
+
/*
* Make changes visible
*/
@@ -1796,18 +1967,18 @@ static Oid find_mlog_table(Oid relid)
Oid mlogid;
HeapTuple tup;
TableScanDesc scan;
- Relation matview_dep;
- Form_gs_matview_dependency matviewdepForm;
+ Relation matview_log;
+ Form_gs_matview_log matviewlogForm;
- matview_dep = heap_open(MatviewDependencyId, AccessShareLock);
- scan = tableam_scan_begin(matview_dep, SnapshotNow, 0, NULL);
+ matview_log = heap_open(MatviewLogRelationId, AccessShareLock);
+ scan = tableam_scan_begin(matview_log, SnapshotNow, 0, NULL);
tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
- while(tup != NULL) {
- matviewdepForm = (Form_gs_matview_dependency)GETSTRUCT(tup);
+ while (tup != NULL) {
+ matviewlogForm = (Form_gs_matview_log)GETSTRUCT(tup);
- if (matviewdepForm->relid == relid) {
- mlogid = matviewdepForm->mlogid;
+ if (matviewlogForm->relid == relid) {
+ mlogid = matviewlogForm->mlogid;
break;
}
tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection);
@@ -1815,16 +1986,18 @@ static Oid find_mlog_table(Oid relid)
if (tup != NULL && HeapTupleIsValid(tup)) {
tableam_scan_end(scan);
- heap_close(matview_dep, NoLock);
+ heap_close(matview_log, NoLock);
return mlogid;
}
tableam_scan_end(scan);
- heap_close(matview_dep, NoLock);
+ heap_close(matview_log, NoLock);
/* if not found, create mlog-table. */
mlogid = create_mlog_table(relid);
+ checkTimeChangeForMlog(mlogid, relid);
+
return mlogid;
}
@@ -2100,6 +2273,28 @@ Oid find_matview_mlog_table(Oid relid)
return mlogid;
}
+bool is_table_in_incre_matview(Oid relid)
+{
+ Relation rel = NULL;
+ TableScanDesc scan;
+ ScanKeyData scanKey;
+ bool result = false;
+
+ ScanKeyInit(&scanKey,
+ Anum_gs_matview_dep_relid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(relid));
+ rel = heap_open(MatviewDependencyId, AccessShareLock);
+ scan = tableam_scan_begin(rel, SnapshotNow, 1, &scanKey);
+ if (tableam_scan_getnexttuple(scan, ForwardScanDirection) != NULL) {
+ result = true;
+ }
+ tableam_scan_end(scan);
+ heap_close(rel, NoLock);
+ return result;
+}
+
void insert_into_mlog_table(Relation rel, Oid mlogid, HeapTuple tuple, ItemPointer tid, TransactionId xid, char action)
{
int i;
@@ -2659,6 +2854,75 @@ void create_matview_meta(Query *query, RangeVar *rel, bool incremental)
return;
}
+ObjectAddress CreateMatViewLog(CreateMatViewLogStmt* parse_tree)
+{
+ RangeVar* rv = parse_tree->relation;
+ ObjectAddress address = InvalidObjectAddress;
+ Oid mlogid = InvalidOid;
+ Oid relid = RangeVarGetRelidExtended(rv, NoLock, false, false, false, true, NULL, NULL, NULL, NULL);
+ HeapTuple tup;
+ TableScanDesc scan;
+ Relation matview_log;
+ Form_gs_matview_log matview_log_form;
+
+ matview_log = heap_open(MatviewLogRelationId, AccessShareLock);
+ scan = tableam_scan_begin(matview_log, SnapshotNow, 0, NULL);
+ tup = (HeapTuple)tableam_scan_getnexttuple(scan, ForwardScanDirection);
+
+ while (tup != NULL) {
+ matview_log_form = (Form_gs_matview_log)GETSTRUCT(tup);
+ /* If mlog already exists */
+ if (matview_log_form->relid == relid) {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialized view log for table \"%s\" already exists.", rv->relname)));
+ }
+ tup = (HeapTuple)tableam_scan_getnexttuple(scan, ForwardScanDirection);
+ }
+
+ tableam_scan_end(scan);
+ heap_close(matview_log, NoLock);
+
+ mlogid = create_mlog_table(relid);
+
+ checkTimeChangeForMlog(mlogid, relid);
+
+ /* validate all mlogids in relevant matview dependency */
+ validate_matdep_mlog(mlogid, relid);
+
+ ObjectAddressSet(address, RelationRelationId, mlogid);
+ return address;
+}
+
+void DropMatViewLog(DropMatViewLogStmt* parse_tree)
+{
+ RangeVar* rv = parse_tree->relation;
+ Oid mlogid = InvalidOid;
+ HeapTuple tuple;
+ Form_pg_class classform;
+ Oid relid = RangeVarGetRelidExtended(rv, NoLock, false, false, false, true, NULL, NULL, NULL, NULL);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple)) {
+ ReleaseSysCache(tuple);
+ return;
+ }
+
+ classform = (Form_pg_class)GETSTRUCT(tuple);
+ DropRelationPermissionCheck(classform->relkind, relid, classform->relnamespace, rv->relname);
+ ReleaseSysCache(tuple);
+
+ mlogid = delete_matview_log_tuple(relid);
+
+ /* invalidate all mlogids in relevant matview dependency */
+ invalidate_matdep_mlog(relid);
+
+ ObjectAddress mlogobject;
+ mlogobject.classId = RelationRelationId;
+ mlogobject.objectId = mlogid;
+ mlogobject.objectSubId = 0;
+ performDeletion(&mlogobject, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+}
+
DistributeBy *infer_incmatview_distkey(CreateTableAsStmt *stmt) {
Query *query = NULL;
RangeTblEntry *rte = NULL;
@@ -2749,7 +3013,9 @@ static void vacuum_mlog_for_matview(Oid matviewid) {
while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL ) {
dep = (Form_gs_matview_dependency)GETSTRUCT(tup);
- mlogidList = lappend_oid(mlogidList, dep->mlogid);
+ if (dep->mlogid != InvalidOid) {
+ mlogidList = lappend_oid(mlogidList, dep->mlogid);
+ }
}
tableam_scan_end(scan);
heap_close(rel, NoLock);
diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp
index 508ec03c5..760fde384 100755
--- a/src/gausskernel/optimizer/commands/tablecmds.cpp
+++ b/src/gausskernel/optimizer/commands/tablecmds.cpp
@@ -4112,7 +4112,7 @@ static bool CheckClassFormPermission(Form_pg_class classform)
}
/* Allow DROP to table owner, schema owner or users who have DROP privilege of the target object */
-static void DropRelationPermissionCheck(char relkind, Oid relOid, Oid nspOid, const char* relname)
+void DropRelationPermissionCheck(char relkind, Oid relOid, Oid nspOid, const char* relname)
{
AclResult aclresult;
if (relkind == RELKIND_INDEX) {
diff --git a/src/gausskernel/process/tcop/utility.cpp b/src/gausskernel/process/tcop/utility.cpp
index e7366e4d7..74b9f8b7d 100755
--- a/src/gausskernel/process/tcop/utility.cpp
+++ b/src/gausskernel/process/tcop/utility.cpp
@@ -6352,6 +6352,14 @@ ProcessUtilitySlow(Node *parse_tree,
PG_END_TRY();
EventTriggerUndoInhibitCommandCollection();
} break;
+
+ case T_CreateMatViewLogStmt:
+ address = CreateMatViewLog((CreateMatViewLogStmt*)parse_tree);
+ break;
+
+ case T_DropMatViewLogStmt:
+ DropMatViewLog((DropMatViewLogStmt*)parse_tree);
+ break;
case T_CreateTrigStmt:
address = CreateTrigger(
@@ -9259,6 +9267,14 @@ const char* CreateCommandTag(Node* parse_tree)
tag = "REFRESH MATERIALIZED VIEW";
break;
+ case T_CreateMatViewLogStmt:
+ tag = "CREATE MATERIALIZED VIEW LOG";
+ break;
+
+ case T_DropMatViewLogStmt:
+ tag = "DROP MATERIALIZED VIEW LOG";
+ break;
+
#ifndef ENABLE_MULTIPLE_NODES
case T_AlterSystemStmt:
tag = "ALTER SYSTEM SET";
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 839777652..68360b203 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -62,6 +62,6 @@
#define NAILED_IN_CATALOG_NUM 8
-#define CATALOG_NUM 110
+#define CATALOG_NUM 111
#endif
diff --git a/src/include/catalog/gs_matview.h b/src/include/catalog/gs_matview.h
index 0aa554eb9..d0c7297a9 100644
--- a/src/include/catalog/gs_matview.h
+++ b/src/include/catalog/gs_matview.h
@@ -73,6 +73,11 @@ extern void insert_matviewdep_tuple(Oid matviewOid, Oid relid, Oid mlogid);
extern void delete_matviewdep_tuple(Oid matviewOid);
extern void delete_matdep_table(Oid mlogid);
+extern void insert_matview_log_tuple(Oid mlog, Oid relid);
+extern Oid delete_matview_log_tuple(Oid relid);
+extern void invalidate_matdep_mlog(Oid relid);
+extern void validate_matdep_mlog(Oid mlogid, Oid relid);
+
extern Datum get_matview_refreshtime(Oid matviewOid, bool *isNUll);
extern Datum get_matview_mapid(Oid matviewOid);
extern bool is_incremental_matview(Oid oid);
diff --git a/src/include/catalog/gs_matview_log.h b/src/include/catalog/gs_matview_log.h
new file mode 100644
index 000000000..7d527f6ee
--- /dev/null
+++ b/src/include/catalog/gs_matview_log.h
@@ -0,0 +1,23 @@
+#ifndef GS_MATVIEW_LOG_H
+#define GS_MATVIEW_LOG_H
+
+#include "catalog/genbki.h"
+#include "nodes/parsenodes.h"
+
+#define MatviewLogRelationId 9753
+#define MatviewLogRelationId_Rowtype_Id 9756
+
+CATALOG(gs_matview_log,9753) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
+{
+ Oid mlogid;
+ Oid relid;
+} FormData_gs_matview_log;
+
+typedef FormData_gs_matview_log *Form_gs_matview_log;
+
+#define Natts_gs_matview_log 2
+
+#define Anum_gs_matview_log_mlogid 1
+#define Anum_gs_matview_relid 2
+
+#endif /* GS_MATVIEW_LOG_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 519552d8c..7bff03ba4 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -621,6 +621,10 @@ DECLARE_UNIQUE_INDEX(gs_matview_oid_index, 9991, on gs_matview using btree(oid o
#define GsMatviewOidIndexId 9991
DECLARE_UNIQUE_INDEX(gs_matviewdep_oid_index, 9992, on gs_matview_dependency using btree(oid oid_ops));
#define GsMatviewDepOidIndexId 9992
+DECLARE_UNIQUE_INDEX(gs_matviewlog_mlogid_index, 9754, on gs_matview_log using btree(mlogid oid_ops));
+#define GsMatviewLogMlogIdIndexId 9754
+DECLARE_UNIQUE_INDEX(gs_matviewlog_relid_index, 9755, on gs_matview_log using btree(relid oid_ops));
+#define GsMatviewLogRelidIndexId 9755
/* Add indexes for pg_recyclebin */
DECLARE_UNIQUE_INDEX(gs_recyclebin_id_index, 8647, on gs_recyclebin using btree(oid oid_ops));
diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_93_002.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_93_002.sql
new file mode 100644
index 000000000..460b5404e
--- /dev/null
+++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_93_002.sql
@@ -0,0 +1,4 @@
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_mlogid_index;
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_relid_index;
+DROP TABLE IF EXISTS pg_catalog.gs_matview_log;
+DROP TYPE IF EXISTS pg_catalog.gs_matview_log;
diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_93_002.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_93_002.sql
new file mode 100644
index 000000000..460b5404e
--- /dev/null
+++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_93_002.sql
@@ -0,0 +1,4 @@
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_mlogid_index;
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_relid_index;
+DROP TABLE IF EXISTS pg_catalog.gs_matview_log;
+DROP TYPE IF EXISTS pg_catalog.gs_matview_log;
diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_93_002.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_93_002.sql
new file mode 100644
index 000000000..d0adc32b0
--- /dev/null
+++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_93_002.sql
@@ -0,0 +1,18 @@
+SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 9753, 9756, 0, 0;
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_mlogid_index;
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_relid_index;
+DROP TABLE IF EXISTS pg_catalog.gs_matview_log;
+DROP TYPE IF EXISTS pg_catalog.gs_matview_log;
+CREATE TABLE IF NOT EXISTS pg_catalog.gs_matview_log
+(
+ mlogid Oid NOCOMPRESS NOT NULL,
+ relid Oid NOCOMPRESS NOT NULL
+);
+
+SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 9754;
+CREATE UNIQUE INDEX gs_matviewlog_mlogid_index ON pg_catalog.gs_matview_log USING BTREE(mlogid oid_ops);
+SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 9755;
+CREATE UNIQUE INDEX gs_matviewlog_relid_index ON pg_catalog.gs_matview_log USING BTREE(relid oid_ops);
+GRANT SELECT ON TABLE pg_catalog.gs_matview_log TO PUBLIC;
+
+INSERT INTO pg_catalog.gs_matview_log SELECT DISTINCT mlogid, relid FROM pg_catalog.gs_matview_dependency;
diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_93_002.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_93_002.sql
new file mode 100644
index 000000000..d0adc32b0
--- /dev/null
+++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_93_002.sql
@@ -0,0 +1,18 @@
+SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 9753, 9756, 0, 0;
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_mlogid_index;
+DROP INDEX IF EXISTS pg_catalog.gs_matviewlog_relid_index;
+DROP TABLE IF EXISTS pg_catalog.gs_matview_log;
+DROP TYPE IF EXISTS pg_catalog.gs_matview_log;
+CREATE TABLE IF NOT EXISTS pg_catalog.gs_matview_log
+(
+ mlogid Oid NOCOMPRESS NOT NULL,
+ relid Oid NOCOMPRESS NOT NULL
+);
+
+SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 9754;
+CREATE UNIQUE INDEX gs_matviewlog_mlogid_index ON pg_catalog.gs_matview_log USING BTREE(mlogid oid_ops);
+SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 9755;
+CREATE UNIQUE INDEX gs_matviewlog_relid_index ON pg_catalog.gs_matview_log USING BTREE(relid oid_ops);
+GRANT SELECT ON TABLE pg_catalog.gs_matview_log TO PUBLIC;
+
+INSERT INTO pg_catalog.gs_matview_log SELECT DISTINCT mlogid, relid FROM pg_catalog.gs_matview_dependency;
diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h
index 41fa12cd8..223afdddb 100644
--- a/src/include/commands/matview.h
+++ b/src/include/commands/matview.h
@@ -67,10 +67,14 @@ extern Oid create_matview_map(Oid intoRelationId);
extern void insert_into_matview_map(Oid mapid, Oid matid, ItemPointer matcitd,
Oid relid, ItemPointer relctid, TransactionId xid);
extern Oid find_matview_mlog_table(Oid relid);
+extern bool is_table_in_incre_matview(Oid relid);
extern void insert_into_mlog_table(Relation rel, Oid mlogid, HeapTuple tuple,
ItemPointer tid, TransactionId xid, char action);
extern void create_matview_meta(Query *query, RangeVar *rel, bool incremental);
+extern ObjectAddress CreateMatViewLog(CreateMatViewLogStmt* parse_tree);
+extern void DropMatViewLog(DropMatViewLogStmt* parse_tree);
+
extern void check_matview_op_supported(CreateTableAsStmt *ctas);
extern DistributeBy *infer_incmatview_distkey(CreateTableAsStmt *stmt);
extern void check_basetable_permission(Query *query);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index df2c4ed18..1cc455def 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -246,6 +246,7 @@ extern void ExecuteTimeCapsule(TimeCapsuleStmt* stmt);
extern void truncate_check_rel(Relation rel);
extern void CheckDropViewValidity(ObjectType stmtType, char relKind, const char* relname);
extern int getPartitionElementsIndexByOid(Relation partTableRel, Oid partOid);
+extern void DropRelationPermissionCheck(char relkind, Oid relOid, Oid nspOid, const char* relname);
extern void SetPartionIndexType(IndexStmt* stmt, Relation rel, bool is_alter_table);
extern bool ConstraintSatisfyAutoIncrement(HeapTuple tuple, TupleDesc desc, AttrNumber attrnum, char contype);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 447e758ba..c5d5b0822 100755
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -589,6 +589,8 @@ typedef enum NodeTag {
T_ShrinkStmt,
T_VariableMultiSetStmt,
T_CursorExpression,
+ T_CreateMatViewLogStmt,
+ T_DropMatViewLogStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
* note: TAGS FOR PARSE TREE NODES (parsenodes.h) can no longer place new tags,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b6fff9b1f..8646e7f10 100755
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2187,6 +2187,26 @@ typedef struct RefreshMatViewStmt
RangeVar *relation; /* relation to insert into */
} RefreshMatViewStmt;
+/* ----------------------
+ * CREATE MATERIALIZED VIEW LOG Statement
+ * ----------------------
+ */
+typedef struct CreateMatViewLogStmt
+{
+ NodeTag type;
+ RangeVar* relation; /* relation to create matview log for */
+} CreateMatViewLogStmt;
+
+/* ----------------------
+ * DROP MATERIALIZED VIEW LOG Statement
+ * ----------------------
+ */
+typedef struct DropMatViewLogStmt
+{
+ NodeTag type;
+ RangeVar* relation; /* relation to drop matview log from */
+} DropMatViewLogStmt;
+
/* ----------------------
* Checkpoint Statement
* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index daa0e84b0..2ce83d597 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -85,6 +85,7 @@ PG_KEYWORD("boolean", BOOLEAN_P, COL_NAME_KEYWORD)
PG_KEYWORD("both", BOTH, RESERVED_KEYWORD)
PG_KEYWORD("bucketcnt", BUCKETCNT, COL_NAME_KEYWORD)
PG_KEYWORD("buckets", BUCKETS, RESERVED_KEYWORD)
+PG_KEYWORD("build", BUILD, UNRESERVED_KEYWORD)
PG_KEYWORD("by", BY, UNRESERVED_KEYWORD)
PG_KEYWORD("byte", BYTE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("byteawithoutorder", BYTEAWITHOUTORDER, COL_NAME_KEYWORD)
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 3474b9775..e73f022b9 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -292,6 +292,7 @@ typedef struct RelationData {
/* double linked list node, partition and bucket relation would be stored in fakerels list of resource owner */
dlist_node node;
+ /* only valid if has mlog and used by incremental matview */
Oid rd_mlogoid;
/* Is under the context of creating crossbucket index? */
bool newcbi;
diff --git a/src/test/regress/expected/matview_single.out b/src/test/regress/expected/matview_single.out
index edf513a5a..21a911304 100644
--- a/src/test/regress/expected/matview_single.out
+++ b/src/test/regress/expected/matview_single.out
@@ -214,3 +214,224 @@ reset role;
drop user testuser cascade;
\c regression
drop database test_imv_db;
+-- mlog基础语法
+create table mview_log_t1(c1 int, c2 varchar(10));
+create materialized view log on mview_log_t1;
+drop materialized view log on mview_log_t1;
+-- 复用手动创建的物化视图日志
+create materialized view log on mview_log_t1;
+create incremental materialized view v1 as select c1, c2 from mview_log_t1 where c1 > 1;
+create incremental materialized view v2 as select c1 from mview_log_t1;
+insert into mview_log_t1 values(1,'1');
+insert into mview_log_t1 values(2,'2');
+update mview_log_t1 set c1 = 3, c2 = '3' where c1 = 1;
+delete from mview_log_t1 where c1 = 2;
+refresh incremental materialized view v1;
+refresh incremental materialized view v2;
+select * from v1;
+ c1 | c2
+----+----
+ 3 | 3
+(1 row)
+
+select * from v2;
+ c1
+----
+ 3
+(1 row)
+
+drop materialized view v1;
+drop materialized view v2; -- 物化视图日志一起被删除
+-- 重复创建删除物化视图日志
+create incremental materialized view v1 as select c1, c2 from mview_log_t1 where c1 > 1;
+create incremental materialized view v2 as select c1 from mview_log_t1;
+insert into mview_log_t1 values(1,'1');
+drop materialized view log on mview_log_t1; -- 自动创建的物化视图日志也可手动删除
+refresh incremental materialized view v1; -- 缺少物化视图日志,增量刷新报错
+ERROR: missing mlog for incrementally refresh
+create materialized view log on mview_log_t1;
+create materialized view log on mview_log_t1; -- 物化视图日志重复,创建报错。
+ERROR: materialized view log for table "mview_log_t1" already exists.
+drop materialized view log on mview_log_t1;
+drop materialized view log on mview_log_t1; -- 物化视图日志不存在,删除报错。
+ERROR: there is no materialized view log on this table
+create materialized view log on mview_log_t1;
+refresh incremental materialized view v1; -- 物化视图日志创建时间晚于最近一次刷新时间,增量刷新报错
+ERROR: materialized view log younger than last refresh
+refresh materialized view v1;
+refresh materialized view v2;
+insert into mview_log_t1 values(2,'2');
+refresh incremental materialized view v1; -- 全量刷新后,增量刷新成功
+refresh incremental materialized view v2;
+select * from v1;
+ c1 | c2
+----+----
+ 3 | 3
+ 2 | 2
+(2 rows)
+
+select * from v2;
+ c1
+----
+ 3
+ 1
+ 2
+(3 rows)
+
+-- 删除所有对象
+drop materialized view log on mview_log_t1;
+drop materialized view v1; -- 先删除物化视图日志,再删除物化视图
+drop materialized view v2;
+drop table mview_log_t1;
+-- 延迟刷新基础功能
+create table defer_t1(c1 int);
+insert into defer_t1 values(1);
+create materialized view v1 build deferred as select * from defer_t1;
+create materialized view v2 build immediate as select * from defer_t1;
+select * from v1; -- 无数据
+ c1
+----
+(0 rows)
+
+select * from v2; -- 有数据
+ c1
+----
+ 1
+(1 row)
+
+refresh materialized view v1;
+select * from v1; -- 有数据
+ c1
+----
+ 1
+(1 row)
+
+-- 同时放开with no data,但不支持一起指定
+create incremental materialized view v3 as select * from defer_t1 with no data;
+create incremental materialized view v4 as select * from defer_t1 with data;
+create materialized view v5 as select * from defer_t1 with no data;
+create materialized view v6 as select * from defer_t1 with data;
+create materialized view v7 build deferred as select * from defer_t1 with no data; -- 语法不支持
+ERROR: syntax error at or near "with no"
+LINE 1: ... view v7 build deferred as select * from defer_t1 with no da...
+ ^
+select * from v3; -- 无数据
+ c1
+----
+(0 rows)
+
+select * from v4; -- 有数据
+ c1
+----
+ 1
+(1 row)
+
+select * from v5; -- 无数据
+ c1
+----
+(0 rows)
+
+select * from v6; -- 有数据
+ c1
+----
+ 1
+(1 row)
+
+refresh incremental materialized view v3; -- 报错,第一次需全量刷新
+ERROR: materialized view must use complete refresh first
+refresh materialized view v3;
+refresh materialized view v5;
+select * from v3; -- 有数据
+ c1
+----
+ 1
+(1 row)
+
+select * from v5; -- 有数据
+ c1
+----
+ 1
+(1 row)
+
+-- 结合手动创建删除mlog
+drop materialized view log on defer_t1;
+create materialized view log on defer_t1;
+insert into defer_t1 values(2);
+create incremental materialized view v8 as select * from defer_t1 with no data;
+select * from v8; -- 无数据
+ c1
+----
+(0 rows)
+
+refresh materialized view v8;
+select * from v8; -- 有数据
+ c1
+----
+ 1
+ 2
+(2 rows)
+
+drop table defer_t1 cascade;
+NOTICE: drop cascades to 7 other objects
+DETAIL: drop cascades to materialized view v1
+drop cascades to materialized view v2
+drop cascades to materialized view v3
+drop cascades to materialized view v4
+drop cascades to materialized view v5
+drop cascades to materialized view v6
+drop cascades to materialized view v8
+-- 多张表的情况
+create table cov_t1(c1 int);
+create table cov_t2(c1 int);
+create materialized view log on cov_t1;
+create materialized view log on cov_t2;
+drop materialized view log on cov_t2;
+create materialized view log on cov_t2;
+create materialized view log on cov_t2;
+ERROR: materialized view log for table "cov_t2" already exists.
+drop table cov_t1,cov_t2 cascade;
+-- 只有mlog没有incre matview,不维护mlog
+create table only_mlog_t1(c1 int);
+create materialized view log on only_mlog_t1;
+insert into only_mlog_t1 values (1);
+create table only_mlog_record(c1 int);
+declare
+ oid int := (select oid from pg_class where relname = 'only_mlog_t1');
+ table_name varchar(20) := 'mlog_' || oid;
+ stmt text := 'insert into only_mlog_record select count(*) from ' || table_name;
+begin
+ execute stmt;
+ commit;
+END;
+/
+select * from only_mlog_record;
+ c1
+----
+ 0
+(1 row)
+
+drop table only_mlog_t1, only_mlog_record;
+-- \help
+\h create materialized view log
+Command: CREATE MATERIALIZED VIEW LOG
+Description: define a new materialized view log
+Syntax:
+CREATE MATERIALIZED VIEW LOG ON table_name;
+
+\h drop materialized view log
+Command: DROP MATERIALIZED VIEW LOG
+Description: drop materialized view log
+Syntax:
+DROP MATERIALIZED VIEW LOG ON table_name;
+
+\h create materialized view
+Command: CREATE MATERIALIZED VIEW
+Description: define a new materialized view
+Syntax:
+CREATE [ INCREMENTAL ] MATERIALIZED VIEW table_name
+ [ (column_name [, ...] ) ]
+ [ TABLESPACE tablespace_name ]
+ [ BUILD DEFERRED | BUILD IMMEDIATE ]
+ AS query
+ [ WITH [ NO ] DATA ]
+
diff --git a/src/test/regress/input/matview_dump.source b/src/test/regress/input/matview_dump.source
new file mode 100644
index 000000000..11b625509
--- /dev/null
+++ b/src/test/regress/input/matview_dump.source
@@ -0,0 +1,10 @@
+create database matview_dump;
+\c matview_dump
+create table matview_dump_t1(a int, b int);
+create materialized view log on matview_dump_t1;
+\! @abs_bindir@/gs_dump matview_dump -p @portstring@ --include-depend-objs --exclude-self | grep -vE '^SET|^REVOKE|^GRANT|^--|^gs_dump|^COMMENT|^ALTER|^DROP|^Progress'| tr -s '\n' > @abs_bindir@/matview_dump.sql 2>&1
+\! cat @abs_bindir@/matview_dump.sql
+DROP TABLE matview_dump_t1 CASCADE;
+\i @abs_bindir@/matview_dump.sql
+\c regression
+drop database matview_dump;
diff --git a/src/test/regress/output/matview_dump.source b/src/test/regress/output/matview_dump.source
new file mode 100644
index 000000000..99a9a8038
--- /dev/null
+++ b/src/test/regress/output/matview_dump.source
@@ -0,0 +1,28 @@
+create database matview_dump;
+\c matview_dump
+create table matview_dump_t1(a int, b int);
+create materialized view log on matview_dump_t1;
+\! @abs_bindir@/gs_dump matview_dump -p @portstring@ --include-depend-objs --exclude-self | grep -vE '^SET|^REVOKE|^GRANT|^--|^gs_dump|^COMMENT|^ALTER|^DROP|^Progress'| tr -s '\n' > @abs_bindir@/matview_dump.sql 2>&1
+\! cat @abs_bindir@/matview_dump.sql
+
+CREATE TABLE matview_dump_t1 (
+ a integer,
+ b integer
+)
+WITH (orientation=row, compression=no);
+CREATE MATERIALIZED VIEW LOG ON matview_dump_t1;
+COPY public.matview_dump_t1 (a, b) FROM stdin;
+\.
+;
+DROP TABLE matview_dump_t1 CASCADE;
+\i @abs_bindir@/matview_dump.sql
+CREATE TABLE matview_dump_t1 (
+ a integer,
+ b integer
+)
+WITH (orientation=row, compression=no);
+CREATE MATERIALIZED VIEW LOG ON matview_dump_t1;
+COPY public.matview_dump_t1 (a, b) FROM stdin;
+;
+\c regression
+drop database matview_dump;
diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0
index 225d1b22f..757985487 100644
--- a/src/test/regress/parallel_schedule0
+++ b/src/test/regress/parallel_schedule0
@@ -728,7 +728,7 @@ test: string_digit_to_numeric tablesample_3 tablesample_4
# Another group of parallel tests
# ----------
#test: collate tablesample tablesample_1 tablesample_2 matview
-test: matview_single matview_with_event_trigger
+test: matview_single matview_with_event_trigger matview_dump
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/parallel_schedule0B b/src/test/regress/parallel_schedule0B
index 0bd00fc71..f54b93595 100644
--- a/src/test/regress/parallel_schedule0B
+++ b/src/test/regress/parallel_schedule0B
@@ -247,7 +247,7 @@ test: string_digit_to_numeric tablesample_3 tablesample_4
# Another group of parallel tests
# ----------
#test: collate tablesample tablesample_1 tablesample_2 matview
-test: matview_single matview_with_event_trigger
+test: matview_single matview_with_event_trigger matview_dump
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/sql/matview_single.sql b/src/test/regress/sql/matview_single.sql
index c9adb65d2..83d848d02 100644
--- a/src/test/regress/sql/matview_single.sql
+++ b/src/test/regress/sql/matview_single.sql
@@ -139,3 +139,112 @@ drop user testuser cascade;
\c regression
drop database test_imv_db;
+
+-- mlog基础语法
+create table mview_log_t1(c1 int, c2 varchar(10));
+create materialized view log on mview_log_t1;
+drop materialized view log on mview_log_t1;
+-- 复用手动创建的物化视图日志
+create materialized view log on mview_log_t1;
+create incremental materialized view v1 as select c1, c2 from mview_log_t1 where c1 > 1;
+create incremental materialized view v2 as select c1 from mview_log_t1;
+insert into mview_log_t1 values(1,'1');
+insert into mview_log_t1 values(2,'2');
+update mview_log_t1 set c1 = 3, c2 = '3' where c1 = 1;
+delete from mview_log_t1 where c1 = 2;
+refresh incremental materialized view v1;
+refresh incremental materialized view v2;
+select * from v1;
+select * from v2;
+drop materialized view v1;
+drop materialized view v2; -- 物化视图日志一起被删除
+-- 重复创建删除物化视图日志
+create incremental materialized view v1 as select c1, c2 from mview_log_t1 where c1 > 1;
+create incremental materialized view v2 as select c1 from mview_log_t1;
+insert into mview_log_t1 values(1,'1');
+drop materialized view log on mview_log_t1; -- 自动创建的物化视图日志也可手动删除
+refresh incremental materialized view v1; -- 缺少物化视图日志,增量刷新报错
+create materialized view log on mview_log_t1;
+create materialized view log on mview_log_t1; -- 物化视图日志重复,创建报错。
+drop materialized view log on mview_log_t1;
+drop materialized view log on mview_log_t1; -- 物化视图日志不存在,删除报错。
+create materialized view log on mview_log_t1;
+refresh incremental materialized view v1; -- 物化视图日志创建时间晚于最近一次刷新时间,增量刷新报错
+refresh materialized view v1;
+refresh materialized view v2;
+insert into mview_log_t1 values(2,'2');
+refresh incremental materialized view v1; -- 全量刷新后,增量刷新成功
+refresh incremental materialized view v2;
+select * from v1;
+select * from v2;
+-- 删除所有对象
+drop materialized view log on mview_log_t1;
+drop materialized view v1; -- 先删除物化视图日志,再删除物化视图
+drop materialized view v2;
+drop table mview_log_t1;
+
+-- 延迟刷新基础功能
+create table defer_t1(c1 int);
+insert into defer_t1 values(1);
+create materialized view v1 build deferred as select * from defer_t1;
+create materialized view v2 build immediate as select * from defer_t1;
+select * from v1; -- 无数据
+select * from v2; -- 有数据
+refresh materialized view v1;
+select * from v1; -- 有数据
+-- 同时放开with no data,但不支持一起指定
+create incremental materialized view v3 as select * from defer_t1 with no data;
+create incremental materialized view v4 as select * from defer_t1 with data;
+create materialized view v5 as select * from defer_t1 with no data;
+create materialized view v6 as select * from defer_t1 with data;
+create materialized view v7 build deferred as select * from defer_t1 with no data; -- 语法不支持
+select * from v3; -- 无数据
+select * from v4; -- 有数据
+select * from v5; -- 无数据
+select * from v6; -- 有数据
+refresh incremental materialized view v3; -- 报错,第一次需全量刷新
+refresh materialized view v3;
+refresh materialized view v5;
+select * from v3; -- 有数据
+select * from v5; -- 有数据
+-- 结合手动创建删除mlog
+drop materialized view log on defer_t1;
+create materialized view log on defer_t1;
+insert into defer_t1 values(2);
+create incremental materialized view v8 as select * from defer_t1 with no data;
+select * from v8; -- 无数据
+refresh materialized view v8;
+select * from v8; -- 有数据
+drop table defer_t1 cascade;
+
+-- 多张表的情况
+create table cov_t1(c1 int);
+create table cov_t2(c1 int);
+create materialized view log on cov_t1;
+create materialized view log on cov_t2;
+drop materialized view log on cov_t2;
+create materialized view log on cov_t2;
+create materialized view log on cov_t2;
+drop table cov_t1,cov_t2 cascade;
+
+-- 只有mlog没有incre matview,不维护mlog
+create table only_mlog_t1(c1 int);
+create materialized view log on only_mlog_t1;
+insert into only_mlog_t1 values (1);
+create table only_mlog_record(c1 int);
+declare
+ oid int := (select oid from pg_class where relname = 'only_mlog_t1');
+ table_name varchar(20) := 'mlog_' || oid;
+ stmt text := 'insert into only_mlog_record select count(*) from ' || table_name;
+begin
+ execute stmt;
+ commit;
+END;
+/
+select * from only_mlog_record;
+drop table only_mlog_t1, only_mlog_record;
+
+-- \help
+\h create materialized view log
+\h drop materialized view log
+\h create materialized view