From 51c38f94f0f7254f859875bcbb63ed2eb00a3295 Mon Sep 17 00:00:00 2001 From: TinyBag Date: Thu, 12 Sep 2024 15:09:40 +0800 Subject: [PATCH] support create/drop materialized view log --- .../sgml/ref/create_materialized_view.sgmlin | 2 + .../ref/create_materialized_view_log.sgmlin | 16 + .../ref/drop_materialized_view_log.sgmlin | 16 + src/bin/pg_dump/pg_backup_archiver.cpp | 2 +- src/bin/pg_dump/pg_dump.cpp | 46 +++ src/common/backend/catalog/CMakeLists.txt | 2 +- src/common/backend/catalog/Makefile | 2 +- src/common/backend/catalog/gs_matview.cpp | 195 ++++++++++- src/common/backend/nodes/copyfuncs.cpp | 24 ++ src/common/backend/nodes/equalfuncs.cpp | 20 ++ src/common/backend/nodes/nodes.cpp | 4 +- src/common/backend/nodes/outfuncs.cpp | 18 + src/common/backend/nodes/readfuncs.cpp | 18 + src/common/backend/parser/gram.y | 91 +++++- src/common/backend/parser/parser.cpp | 20 ++ src/common/backend/utils/cache/relcache.cpp | 18 +- src/common/backend/utils/init/globals.cpp | 2 +- .../interfaces/libpq/frontend_parser/gram.y | 3 +- .../optimizer/commands/event_trigger.cpp | 1 + .../optimizer/commands/matview.cpp | 308 ++++++++++++++++-- .../optimizer/commands/tablecmds.cpp | 2 +- src/gausskernel/process/tcop/utility.cpp | 16 + src/include/catalog/catversion.h | 2 +- src/include/catalog/gs_matview.h | 5 + src/include/catalog/gs_matview_log.h | 23 ++ src/include/catalog/indexing.h | 4 + .../rollback_catalog_maindb_93_002.sql | 4 + .../rollback_catalog_otherdb_93_002.sql | 4 + .../upgrade_catalog_maindb_93_002.sql | 18 + .../upgrade_catalog_otherdb_93_002.sql | 18 + src/include/commands/matview.h | 4 + src/include/commands/tablecmds.h | 1 + src/include/nodes/nodes.h | 2 + src/include/nodes/parsenodes.h | 20 ++ src/include/parser/kwlist.h | 1 + src/include/utils/rel.h | 1 + src/test/regress/expected/matview_single.out | 221 +++++++++++++ src/test/regress/input/matview_dump.source | 10 + src/test/regress/output/matview_dump.source | 28 ++ src/test/regress/parallel_schedule0 | 2 +- src/test/regress/parallel_schedule0B | 2 +- src/test/regress/sql/matview_single.sql | 109 +++++++ 42 files changed, 1255 insertions(+), 50 deletions(-) create mode 100644 doc/src/sgml/ref/create_materialized_view_log.sgmlin create mode 100644 doc/src/sgml/ref/drop_materialized_view_log.sgmlin create mode 100644 src/include/catalog/gs_matview_log.h create mode 100644 src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_93_002.sql create mode 100644 src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_93_002.sql create mode 100644 src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_93_002.sql create mode 100644 src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_93_002.sql create mode 100644 src/test/regress/input/matview_dump.source create mode 100644 src/test/regress/output/matview_dump.source 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