From f4686aa3c938cb932ca9599ad2a28f6a2a690703 Mon Sep 17 00:00:00 2001 From: mujinqiang <1165845907@qq.com> Date: Sat, 27 Mar 2021 22:16:31 +0800 Subject: [PATCH] =?UTF-8?q?probackup=E5=A4=87=E6=9C=BA=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/pg_probackup/backup.cpp | 158 +++++++++++------ src/bin/pg_probackup/catalog.cpp | 15 +- src/bin/pg_probackup/merge.cpp | 1 + src/bin/pg_probackup/parsexlog.cpp | 9 +- src/bin/pg_probackup/pg_probackupb.h | 1 + src/bin/pg_probackup/ptrack.cpp | 9 +- src/bin/pg_probackup/show.cpp | 4 + src/common/backend/catalog/builtin_funcs.ini | 10 +- .../process/threadpool/knl_session.cpp | 4 + .../storage/access/transam/cbmfuncs.cpp | 2 + .../storage/access/transam/xlog.cpp | 19 ++ .../storage/access/transam/xlogfuncs.cpp | 164 ++++++++++++++++++ .../rollback_catalog_maindb_92_200.sql | 2 + .../rollback_catalog_otherdb_92_200.sql | 2 + .../upgrade_catalog_maindb_92_200.sql | 18 ++ .../upgrade_catalog_otherdb_92_200.sql | 18 ++ src/include/knl/knl_session.h | 8 +- src/include/storage/ipc.h | 1 + src/test/regress/expected/opr_sanity.out | 2 + .../expected/single_node_opr_sanity.out | 2 + 20 files changed, 377 insertions(+), 72 deletions(-) diff --git a/src/bin/pg_probackup/backup.cpp b/src/bin/pg_probackup/backup.cpp index 9a15ddd20..0ed9e774e 100644 --- a/src/bin/pg_probackup/backup.cpp +++ b/src/bin/pg_probackup/backup.cpp @@ -39,6 +39,8 @@ static uint32 stream_stop_timeout = 0; /* Time in which we started to wait for streaming end */ static time_t stream_stop_begin = 0; +static const uint32 archive_timeout_deno = 5; + //const char *progname = "pg_probackup"; /* list of files contained in backup */ @@ -63,6 +65,7 @@ typedef struct XLogRecPtr startpos; TimeLineID starttli; + bool renamepartial; } StreamThreadArg; static pthread_t stream_thread; @@ -268,6 +271,12 @@ static void start_stream_wal(const char *database_path, PGconn *backup_conn) stream_thread_arg.starttli = current.tli; thread_interrupted = false; + + if (current.from_replica) { + stream_thread_arg.renamepartial = true; + } else { + stream_thread_arg.renamepartial = false; + } pthread_create(&stream_thread, NULL, StreamLog, &stream_thread_arg); } @@ -442,6 +451,27 @@ static void sync_files(parray *database_map, const char *database_path, parray * { time_t start_time, end_time; char pretty_time[20]; + + /* In case of backup from replica >= 9.6 we must fix minRecPoint, + * First we must find pg_control in backup_files_list. + */ + if (current.from_replica && !exclusive_backup) + { + pgFile *pg_control = NULL; + for (int i = 0; i < parray_num(backup_files_list); i++) + { + pgFile *tmp_file = (pgFile *)parray_get(backup_files_list, (size_t)i); + if (tmp_file->external_dir_num == 0 && + (strcmp(tmp_file->rel_path, XLOG_CONTROL_FILE) == 0)) + { + pg_control = tmp_file; + break; + } + } + if (!pg_control) + elog(ERROR, "Failed to find file \"%s\" in backup filelist.", XLOG_CONTROL_FILE); + set_min_recovery_point(pg_control, database_path, current.stop_lsn); + } /* close and sync page header map */ if (current.hdr_map.fp) @@ -735,7 +765,6 @@ PGconn * pgdata_basic_setup(const ConnectionOptions conn_opt, PGNodeInfo *nodeInfo) { PGconn *cur_conn; - bool from_replica; errno_t rc = 0; /* Create connection for PostgreSQL */ @@ -743,11 +772,6 @@ pgdata_basic_setup(const ConnectionOptions conn_opt, PGNodeInfo *nodeInfo) conn_opt.pgdatabase, conn_opt.pguser); - from_replica = pg_is_in_recovery(cur_conn); - if (from_replica) { - elog(ERROR, "gs_probackup is not supported on standby\n"); - } - /* Confirm data block size and xlog block size are compatible */ confirm_block_size(cur_conn, "block_size", BLCKSZ); confirm_block_size(cur_conn, "wal_block_size", XLOG_BLCKSZ); @@ -755,10 +779,12 @@ pgdata_basic_setup(const ConnectionOptions conn_opt, PGNodeInfo *nodeInfo) nodeInfo->wal_block_size = XLOG_BLCKSZ; nodeInfo->pgpro_support = pgpro_support(cur_conn); - + current.from_replica = pg_is_in_recovery(cur_conn); nodeInfo->server_version = PQserverVersion(conn); - exclusive_backup = true; + if (!current.from_replica) { + exclusive_backup = true; + } current.checksum_version = 0; @@ -868,6 +894,10 @@ do_backup(time_t start_time, pgSetBackupParams *set_backup_params, */ backup_conn = pgdata_basic_setup(instance_config.conn_opt, &nodeInfo); + if (current.from_replica) { + elog(INFO, "Backup %s is going to be taken from standby", base36enc((unsigned long int)start_time)); + } + /* * Ensure that backup directory was initialized for the same PostgreSQL * instance we opened connection to. And that target backup database PGDATA @@ -1186,6 +1216,20 @@ bool inform_user_wal_seg_absent(bool is_start_lsn, uint32 *try_count, bool segme return true; } +/* Try to find compressed WAL file */ +void tryToFindCompressedWALFile(bool *file_exists, char *gz_wal_segment_path, char *wal_segment_path) +{ + if (!(*file_exists)) { +#ifdef HAVE_LIBZ + *file_exists = fileExists(gz_wal_segment_path, FIO_BACKUP_HOST); + if (*file_exists) + elog(LOG, "Found compressed WAL segment: %s", wal_segment_path); +#endif + } else { + elog(LOG, "Found WAL segment: %s", wal_segment_path); + } +} + /* * Wait for target LSN or WAL segment, containing target LSN. * @@ -1259,10 +1303,7 @@ wait_wal_lsn(XLogRecPtr target_lsn, bool is_start_lsn, TimeLineID tli, } /* TODO: remove this in 3.0 (it is a cludge against some old bug with archive_timeout) */ - if (instance_config.archive_timeout > 0) - timeout = instance_config.archive_timeout; - else - timeout = ARCHIVE_TIMEOUT_DEFAULT; + timeout = (instance_config.archive_timeout > 0) ? instance_config.archive_timeout : ARCHIVE_TIMEOUT_DEFAULT; if (segment_only) elog(LOG, "Looking for segment: %s", wal_segment); @@ -1282,20 +1323,9 @@ wait_wal_lsn(XLogRecPtr target_lsn, bool is_start_lsn, TimeLineID tli, if (!file_exists) { file_exists = fileExists(wal_segment_path, FIO_BACKUP_HOST); - - /* Try to find compressed WAL file */ - if (!file_exists) - { -#ifdef HAVE_LIBZ - file_exists = fileExists(gz_wal_segment_path, FIO_BACKUP_HOST); - if (file_exists) - elog(LOG, "Found compressed WAL segment: %s", wal_segment_path); -#endif - } - else - elog(LOG, "Found WAL segment: %s", wal_segment_path); - } - + tryToFindCompressedWALFile(&file_exists, gz_wal_segment_path, wal_segment_path); + } + if (file_exists) { /* Do not check for target LSN */ @@ -1313,6 +1343,32 @@ wait_wal_lsn(XLogRecPtr target_lsn, bool is_start_lsn, TimeLineID tli, elog(LOG, "Found LSN: %X/%X", (uint32) (target_lsn >> 32), (uint32) target_lsn); return target_lsn; } + + /* + * If we failed to get target LSN in a reasonable time, try + * to get LSN of last valid record prior to the target LSN. But only + * in case of a backup from a replica. + * Note, that with NullXRecOff target_lsn we do not wait + * for 'timeout / 5' seconds before going for previous record, + * because such LSN cannot be delivered at all. + * + * There are two cases for this: + * 1. Replica returned readpoint LSN which just do not exists. We want to look + * for previous record in the same(!) WAL segment which endpoint points to this LSN. + * 2. Replica returened endpoint LSN with NullXRecOff. We want to look + * for previous record which endpoint points greater or equal LSN in previous WAL segment. + */ + if (current.from_replica && + (XRecOffIsNull(target_lsn) || try_count > timeout / archive_timeout_deno)) { + XLogRecPtr res = get_prior_record_lsn(wal_segment_dir, current.start_lsn, target_lsn, tli, + in_prev_segment, instance_config.xlog_seg_size); + if (!XLogRecPtrIsInvalid(res)) { + /* LSN of the prior record was found */ + elog(LOG, "Found prior LSN: %X/%X", + (uint32) (res >> 32), (uint32) res); + return res; + } + } } sleep(1); @@ -1392,9 +1448,11 @@ static void get_valid_stop_lsn(pgBackup *backup, bool *stop_lsn_exists, XLogRecP !XRecOffIsValid(lsn_tmp) || lsn_tmp < stop_backup_lsn_tmp) { - elog(ERROR, "Failed to get next WAL record after %X/%X", - (uint32) (stop_backup_lsn_tmp >> 32), - (uint32) (stop_backup_lsn_tmp)); + /* Backup from master should error out here */ + if (!backup->from_replica) + elog(ERROR, "Failed to get next WAL record after %X/%X", + (uint32)(stop_backup_lsn_tmp >> 32), + (uint32)(stop_backup_lsn_tmp)); /* No luck, falling back to looking up for previous record */ elog(WARNING, "Failed to get next WAL record after %X/%X, " @@ -1666,8 +1724,10 @@ pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, res = pgut_execute(conn, "SET datestyle = 'ISO, DMY';", 0, NULL); PQclear(res); - /* Create restore point */ - if (backup != NULL) + /* Create restore point + * Only if backup is from master. + */ + if (backup != nullptr && !backup->from_replica) { create_restore_point(backup, conn); } @@ -1686,22 +1746,7 @@ pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, * wait for pg_stop_backup() forever. */ PgStopBackupSent(conn, &stop_backup_query); - - if (!pg_stop_backup_is_sent) - { - bool sent = false; - - stop_backup_query = "SELECT" - " pg_catalog.txid_snapshot_xmax(pg_catalog.txid_current_snapshot())," - " current_timestamp(0)::timestamptz," - " pg_catalog.pg_stop_backup() as lsn"; - - sent = pgut_send(conn, stop_backup_query, 0, NULL, WARNING); - pg_stop_backup_is_sent = true; - if (!sent) - elog(ERROR, "Failed to send pg_stop_backup query"); - } - + /* After we have sent pg_stop_backup, we don't need this callback anymore */ pgut_atexit_pop(backup_stopbackup_callback, pg_startbackup_conn); @@ -1769,9 +1814,13 @@ pg_stop_backup(pgBackup *backup, PGconn *pg_startbackup_conn, * If replica returned valid STOP_LSN of not actually existing record, * look for previous record with endpoint >= STOP_LSN. */ - if (!stop_lsn_exists) + if (!stop_lsn_exists) { + if (backup->from_replica) { + stop_backup_lsn = stop_backup_lsn_tmp; + } stop_backup_lsn = wait_wal_lsn(stop_backup_lsn_tmp, false, backup->tli, false, false, ERROR, stream_wal); + } if (stream_wal) { @@ -2068,16 +2117,14 @@ parse_filelist_filenames(parray *files, const char *root) while (unlogged_file_num >= 0 && (unlogged_file_reloid != 0) && - (unlogged_file->relOid == unlogged_file_reloid)) - { + (unlogged_file->relOid == unlogged_file_reloid)) { pgFileFree(unlogged_file); parray_remove(files, unlogged_file_num); unlogged_file_num--; i--; - unlogged_file = (pgFile *) parray_get(files, - unlogged_file_num); + unlogged_file = (pgFile *) parray_get(files, unlogged_file_num); } } } @@ -2220,8 +2267,7 @@ stop_streaming(XLogRecPtr xlogpos, uint32 timeline, bool segment_finished) if (!XLogRecPtrIsInvalid(stop_backup_lsn)) { - if (xlogpos >= stop_backup_lsn) - { + if (xlogpos >= stop_backup_lsn) { stop_stream_lsn = xlogpos; return true; } @@ -2324,12 +2370,12 @@ StreamLog(void *arg) #else if(ReceiveXlogStream(stream_arg->conn, stream_arg->startpos, stream_arg->starttli, NULL, (const char *) stream_arg->basedir, stop_streaming, - standby_message_timeout_local, false) == false) + standby_message_timeout_local, stream_arg->renamepartial) == false) elog(ERROR, "Problem in receivexlog"); #endif elog(LOG, "finished streaming WAL at %X/%X (timeline %u)", - (uint32) (stop_stream_lsn >> 32), (uint32) stop_stream_lsn, stream_arg->starttli); + (uint32) (stop_stream_lsn >> 32), (uint32) stop_stream_lsn, stream_arg->starttli); stream_arg->ret = 0; PQfinish(stream_arg->conn); diff --git a/src/bin/pg_probackup/catalog.cpp b/src/bin/pg_probackup/catalog.cpp index 89a3fcbfb..dcfb993e0 100644 --- a/src/bin/pg_probackup/catalog.cpp +++ b/src/bin/pg_probackup/catalog.cpp @@ -1472,7 +1472,14 @@ void anchor_lsn_keep_segments_timelines(InstanceConfig *instance, parray *timeli interval->begin_segno = segno; GetXLogSegNo(backup->stop_lsn, segno, instance->xlog_seg_size); - interval->end_segno = segno; + /* + * On replica it is possible to get STOP_LSN pointing to contrecord, + * so set end_segno to the next segment after STOP_LSN just to be safe. + */ + if (backup->from_replica) + interval->end_segno = segno + 1; + else + interval->end_segno = segno; GetXLogFileName(begin_segno_str, tlinfo->tli, interval->begin_segno, instance->xlog_seg_size); GetXLogFileName(end_segno_str, tlinfo->tli, interval->end_segno, instance->xlog_seg_size); @@ -1808,6 +1815,7 @@ pgBackupWriteControl(FILE *out, pgBackup *backup) fio_fprintf(out, "compress-alg = %s\n", deparse_compress_alg(backup->compress_alg)); fio_fprintf(out, "compress-level = %d\n", backup->compress_level); + fio_fprintf(out, "from-replica = %s\n", backup->from_replica ? "true" : "false"); fio_fprintf(out, "\n#Compatibility\n"); fio_fprintf(out, "block-size = %u\n", backup->block_size); @@ -2055,7 +2063,6 @@ write_backup_filelist(pgBackup *backup, parray *files, const char *root, len += nRet; } - if (file->linked) { nRet = snprintf_s(line+len, remainLen - len,remainLen - len - 1,",\"linked\":\"%s\"", file->linked); @@ -2063,7 +2070,6 @@ write_backup_filelist(pgBackup *backup, parray *files, const char *root, len += nRet; } - if (file->n_blocks > 0) { nRet = snprintf_s(line+len,remainLen - len,remainLen - len - 1, ",\"n_blocks\":\"%i\"", file->n_blocks); @@ -2089,6 +2095,7 @@ write_backup_filelist(pgBackup *backup, parray *files, const char *root, nRet = snprintf_s(line+len,remainLen - len,remainLen - len - 1, "}\n"); securec_check_ss_c(nRet, "\0", "\0"); + len += nRet; if (sync) COMP_FILE_CRC32(true, backup->content_crc, line, strlen(line)); @@ -2191,6 +2198,7 @@ readBackupControlFile(const char *path) {'s', 0, "merge-dest-id", &merge_dest_backup, SOURCE_FILE_STRICT}, {'s', 0, "compress-alg", &compress_alg, SOURCE_FILE_STRICT}, {'u', 0, "compress-level", &backup->compress_level, SOURCE_FILE_STRICT}, + {'b', 0, "from-replica", &backup->from_replica, SOURCE_FILE_STRICT}, {'s', 0, "external-dirs", &backup->external_dir_str, SOURCE_FILE_STRICT}, {'s', 0, "note", &backup->note, SOURCE_FILE_STRICT}, {'s', 0, "recovery-name", &recovery_name, SOURCE_FILE_STRICT}, @@ -2424,6 +2432,7 @@ pgBackupInit(pgBackup *backup) backup->checksum_version = 0; backup->stream = false; + backup->from_replica = false; backup->parent_backup = INVALID_BACKUP_ID; backup->merge_dest_backup = INVALID_BACKUP_ID; backup->parent_backup_link = NULL; diff --git a/src/bin/pg_probackup/merge.cpp b/src/bin/pg_probackup/merge.cpp index bc91d8ca7..5e17e38db 100644 --- a/src/bin/pg_probackup/merge.cpp +++ b/src/bin/pg_probackup/merge.cpp @@ -882,6 +882,7 @@ static void threads_handle(pgBackup *dest_backup, full_backup->recovery_time = dest_backup->recovery_time; full_backup->recovery_xid = dest_backup->recovery_xid; full_backup->tli = dest_backup->tli; + full_backup->from_replica = dest_backup->from_replica; pfree(full_backup->external_dir_str); full_backup->external_dir_str = pgut_strdup(dest_backup->external_dir_str); diff --git a/src/bin/pg_probackup/parsexlog.cpp b/src/bin/pg_probackup/parsexlog.cpp index 00316c0be..ae4ee2b63 100644 --- a/src/bin/pg_probackup/parsexlog.cpp +++ b/src/bin/pg_probackup/parsexlog.cpp @@ -634,9 +634,10 @@ wal_contains_lsn(const char *archivedir, XLogRecPtr target_lsn, res = XLogReadRecord(xlogreader, target_lsn, &errormsg) != NULL; /* Didn't find 'target_lsn' and there is no error, return false */ - if (errormsg) - elog(WARNING, "Could not read WAL record at %X/%X: %s", - (uint32) (target_lsn >> 32), (uint32) (target_lsn), errormsg); + if (!current.from_replica) + if (errormsg) + elog(WARNING, "Could not read WAL record at %X/%X: %s", + (uint32) (target_lsn >> 32), (uint32) (target_lsn), errormsg); CleanupXLogPageRead(xlogreader); XLogReaderFree(xlogreader); @@ -1459,7 +1460,7 @@ XLogThreadWorker(void *arg) * Usually SimpleXLogPageRead_local() does it by itself. But here we need * to do it manually to support threads. */ - if (reader_data->need_switch && errormsg == NULL) + if (reader_data->need_switch) { if (SwitchThreadToNextWal(xlogreader, thread_arg)) continue; diff --git a/src/bin/pg_probackup/pg_probackupb.h b/src/bin/pg_probackup/pg_probackupb.h index 823597167..599f90896 100644 --- a/src/bin/pg_probackup/pg_probackupb.h +++ b/src/bin/pg_probackup/pg_probackupb.h @@ -254,6 +254,7 @@ struct pgBackup bool stream; /* Was this backup taken in stream mode? * i.e. does it include all needed WAL files? */ + bool from_replica; /* Was this backup taken from replica */ time_t parent_backup; /* Identifier of the previous backup. * Which is basic backup for this * incremental backup. */ diff --git a/src/bin/pg_probackup/ptrack.cpp b/src/bin/pg_probackup/ptrack.cpp index 3ff05a8e2..52f1458c0 100644 --- a/src/bin/pg_probackup/ptrack.cpp +++ b/src/bin/pg_probackup/ptrack.cpp @@ -69,8 +69,10 @@ pg_ptrack_get_pagemapset(PGconn *backup_conn, XLogRecPtr lsn) securec_check_ss_c(rc, "\0", "\0"); params[0] = gs_pstrdup(start_lsn); - res = pgut_execute(backup_conn, "CHECKPOINT;", 0, NULL); - PQclear(res); + if (!current.from_replica) { + res = pgut_execute(backup_conn, "CHECKPOINT;", 0, NULL); + PQclear(res); + } res = pgut_execute(backup_conn, "SELECT pg_cbm_tracked_location()", 0, NULL); if (PQnfields(res) != 1) { @@ -94,8 +96,7 @@ pg_ptrack_get_pagemapset(PGconn *backup_conn, XLogRecPtr lsn) pagemap.bitmapsize = 0; /* Construct database map */ - for (i = 0; i < PQntuples(res); i++) - { + for (i = 0; i < PQntuples(res); i++) { page_map_entry *pm_entry = (page_map_entry *) pgut_malloc(sizeof(page_map_entry)); /* get path */ diff --git a/src/bin/pg_probackup/show.cpp b/src/bin/pg_probackup/show.cpp index 4f71fdb52..a2bf196f7 100644 --- a/src/bin/pg_probackup/show.cpp +++ b/src/bin/pg_probackup/show.cpp @@ -373,6 +373,10 @@ print_backup_json_object(PQExpBuffer buf, pgBackup *backup) json_add_key(buf, "compress-level", json_level); appendPQExpBuffer(buf, "%d", backup->compress_level); + json_add_value(buf, "from-replica", + backup->from_replica ? "true" : "false", json_level, + true); + json_add_key(buf, "block-size", json_level); appendPQExpBuffer(buf, "%u", backup->block_size); diff --git a/src/common/backend/catalog/builtin_funcs.ini b/src/common/backend/catalog/builtin_funcs.ini index f8d91a291..c7bd13d79 100755 --- a/src/common/backend/catalog/builtin_funcs.ini +++ b/src/common/backend/catalog/builtin_funcs.ini @@ -7127,8 +7127,9 @@ AddBuiltinFunc(_0(2626), _1("pg_sleep"), _2(1), _3(true), _4(false), _5(pg_sleep), _6(2278), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1, 701), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("pg_sleep"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f')) ), AddFuncGroup( - "pg_start_backup", 1, - AddBuiltinFunc(_0(2172), _1("pg_start_backup"), _2(2), _3(true), _4(false), _5(pg_start_backup), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(1), _20(2, 25, 16), _21(NULL), _22(NULL), _23(2, "label", "fast"), _24("({CONST :consttype 16 :consttypmod -1 :constcollid 0 :constlen 1 :constbyval true :constisnull false :ismaxvalue false :location 72803 :constvalue 1 [ 0 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno 0 :is_open false :found false :not_found false :null_open false :null_fetch false})"), _25("pg_start_backup"), _26(NULL), _27(NULL), _28(NULL), _29(1, 1), _30(false), _31(false), _32(false), _33(NULL), _34('f')) + "pg_start_backup", 2, + AddBuiltinFunc(_0(2172), _1("pg_start_backup"), _2(2), _3(true), _4(false), _5(pg_start_backup), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(1), _20(2, 25, 16), _21(NULL), _22(NULL), _23(2, "label", "fast"), _24("({CONST :consttype 16 :consttypmod -1 :constcollid 0 :constlen 1 :constbyval true :constisnull false :ismaxvalue false :location 72803 :constvalue 1 [ 0 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno 0 :is_open false :found false :not_found false :null_open false :null_fetch false})"), _25("pg_start_backup"), _26(NULL), _27(NULL), _28(NULL), _29(1, 1), _30(false), _31(false), _32(false), _33(NULL), _34('f')), + AddBuiltinFunc(_0(6203), _1("pg_start_backup"), _2(3), _3(true), _4(false), _5(pg_start_backup_v2), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(3, 25, 16, 16), _21(NULL), _22(NULL), _23(3, "label", "fast", "exclusive"), _24(NULL), _25("pg_start_backup_v2"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f')) ), AddFuncGroup( "pg_stat_bad_block", 1, @@ -7693,8 +7694,9 @@ AddBuiltinFunc(_0(5601), _1("pg_stat_set_last_data_changed_time"), _2(1), _3(true), _4(false), _5(pg_stat_set_last_data_changed_time), _6(2278), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(1, 26), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("pg_stat_set_last_data_changed_time"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f')) ), AddFuncGroup( - "pg_stop_backup", 1, - AddBuiltinFunc(_0(2173), _1("pg_stop_backup"), _2(0), _3(true), _4(false), _5(pg_stop_backup), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(0), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("pg_stop_backup"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f')) + "pg_stop_backup", 2, + AddBuiltinFunc(_0(2173), _1("pg_stop_backup"), _2(0), _3(true), _4(false), _5(pg_stop_backup), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(0), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("pg_stop_backup"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f')), + AddBuiltinFunc(_0(6204), _1("pg_stop_backup"), _2(1), _3(true), _4(true), _5(pg_stop_backup_v2), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1,16), _21(4, 16, 25, 25, 25), _22(4, 'i', 'o', 'o', 'o'), _23(4, "exclusive", "lsn","labelfile", "spcmapfile"), _24(NULL), _25("pg_stop_backup_v2"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f')) ), AddFuncGroup( "pg_switch_xlog", 1, diff --git a/src/gausskernel/process/threadpool/knl_session.cpp b/src/gausskernel/process/threadpool/knl_session.cpp index 4242d2bb8..958562eeb 100644 --- a/src/gausskernel/process/threadpool/knl_session.cpp +++ b/src/gausskernel/process/threadpool/knl_session.cpp @@ -500,6 +500,10 @@ static void knl_u_proc_init(knl_u_proc_context* proc_cxt) proc_cxt->clientIsGsroach = false; proc_cxt->IsBinaryUpgrade = false; proc_cxt->IsWLMWhiteList = false; + proc_cxt->sessionBackupState = SESSION_BACKUP_NONE; + proc_cxt->LabelFile = NULL; + proc_cxt->TblspcMapFile = NULL; + proc_cxt->registerAbortBackupHandlerdone = false; proc_cxt->gsRewindAddCount = false; proc_cxt->PassConnLimit = false; proc_cxt->sessionBackupState = SESSION_BACKUP_NONE; diff --git a/src/gausskernel/storage/access/transam/cbmfuncs.cpp b/src/gausskernel/storage/access/transam/cbmfuncs.cpp index 5fee403b9..eef0f30e7 100644 --- a/src/gausskernel/storage/access/transam/cbmfuncs.cpp +++ b/src/gausskernel/storage/access/transam/cbmfuncs.cpp @@ -156,11 +156,13 @@ Datum pg_cbm_get_changed_block(PG_FUNCTION_ARGS) text *start_lsn_arg = PG_GETARG_TEXT_P(0); text *end_lsn_arg = PG_GETARG_TEXT_P(1); +#ifdef ENABLE_MULTIPLE_NODES /* At present, we only allow merging CBM files on master */ if (RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("recovery is in progress"), errhint("pg_cbm_get_changed_block() cannot be executed" "during recovery."))); +#endif char *start_lsn_str = text_to_cstring(start_lsn_arg); char *end_lsn_str = text_to_cstring(end_lsn_arg); diff --git a/src/gausskernel/storage/access/transam/xlog.cpp b/src/gausskernel/storage/access/transam/xlog.cpp index 65f00b63d..db9a6b0d4 100644 --- a/src/gausskernel/storage/access/transam/xlog.cpp +++ b/src/gausskernel/storage/access/transam/xlog.cpp @@ -13334,6 +13334,17 @@ XLogRecPtr do_pg_stop_backup(char *labelfile, bool waitforarchive) } u_sess->proc_cxt.sessionBackupState = SESSION_BACKUP_NONE; StopSuspendWalInsert(lastlrc); + + /* + * Clean up session-level lock. + * + * You might think that WALInsertLockRelease() can be called before + * cleaning up session-level lock because session-level lock doesn't need + * to be protected with WAL insertion lock. But since + * CHECK_FOR_INTERRUPTS() can occur in it, session-level lock must be + * cleaned up before it. + */ + u_sess->proc_cxt.sessionBackupState = SESSION_BACKUP_NONE; if (exclusive) { /* @@ -13642,6 +13653,13 @@ void do_pg_abort_backup(void) int32 lastlrc = 0; StartSuspendWalInsert(&lastlrc); + /* + * Quick exit if session is not keeping around a non-exclusive backup + * already started. + */ + if (u_sess->proc_cxt.sessionBackupState != SESSION_BACKUP_NON_EXCLUSIVE) + return; + Assert(t_thrd.shemem_ptr_cxt.XLogCtl->Insert.nonExclusiveBackups > 0); t_thrd.shemem_ptr_cxt.XLogCtl->Insert.nonExclusiveBackups--; @@ -13652,6 +13670,7 @@ void do_pg_abort_backup(void) u_sess->proc_cxt.sessionBackupState = SESSION_BACKUP_NONE; StopSuspendWalInsert(lastlrc); + u_sess->proc_cxt.sessionBackupState = SESSION_BACKUP_NONE; } /* diff --git a/src/gausskernel/storage/access/transam/xlogfuncs.cpp b/src/gausskernel/storage/access/transam/xlogfuncs.cpp index 24e95026b..26d6a9944 100644 --- a/src/gausskernel/storage/access/transam/xlogfuncs.cpp +++ b/src/gausskernel/storage/access/transam/xlogfuncs.cpp @@ -75,6 +75,13 @@ Datum pg_start_backup(PG_FUNCTION_ARGS) char startxlogstr[MAXFNAMELEN]; errno_t errorno = EOK; + SessionBackupState status = u_sess->proc_cxt.sessionBackupState; + + if (status == SESSION_BACKUP_NON_EXCLUSIVE) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("a non-exclusive backup is already in progress in this session"))); + backupidstr = text_to_cstring(backupid); dir = AllocateDir("pg_tblspc"); if (!dir) { @@ -114,6 +121,13 @@ Datum pg_stop_backup(PG_FUNCTION_ARGS) char stopxlogstr[MAXFNAMELEN]; errno_t errorno = EOK; + SessionBackupState status = u_sess->proc_cxt.sessionBackupState; + + if (status == SESSION_BACKUP_NON_EXCLUSIVE) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("a non-exclusive backup is already in progress in this session"))); + /* when delay xlog recycle is true, we do not copy xlog from archive */ stoppoint = do_pg_stop_backup(NULL, !GetDelayXlogRecycle()); @@ -124,6 +138,156 @@ Datum pg_stop_backup(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr)); } +/* + * pg_start_backup_v2: set up for taking an on-line backup dump + * + */ +Datum pg_start_backup_v2(PG_FUNCTION_ARGS) +{ + text* backupid = PG_GETARG_TEXT_P(0); + bool fast = PG_GETARG_BOOL(1); + bool exclusive = PG_GETARG_BOOL(2); + char* backupidstr = NULL; + char* labelfile = NULL; + char* tblspcmapfile = NULL; + XLogRecPtr startpoint; + DIR *dir; + char startxlogstr[MAXFNAMELEN]; + errno_t errorno = EOK; + MemoryContext oldContext; + + u_sess->probackup_context = AllocSetContextCreate(u_sess->top_mem_cxt, "probackup context", + ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + oldContext = MemoryContextSwitchTo(u_sess->probackup_context); + + SessionBackupState status = u_sess->proc_cxt.sessionBackupState; + + if (status == SESSION_BACKUP_NON_EXCLUSIVE) + ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("a non-exclusive backup is already in progress in this session"))); + + backupidstr = text_to_cstring(backupid); + dir = AllocateDir("pg_tblspc"); + if (!dir) { + ereport(ERROR, (errmsg("could not open directory \"%s\": %m", "pg_tblspc"))); + } + + if (exclusive) { + startpoint = do_pg_start_backup(backupidstr, fast, NULL, dir, NULL, NULL, false, true); + } else { + if (status == SESSION_BACKUP_EXCLUSIVE) + ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("a backup is already in progress in this session"))); + + startpoint = do_pg_start_backup(backupidstr, fast, &labelfile,dir, &tblspcmapfile, NULL,false,true); + u_sess->proc_cxt.LabelFile = MemoryContextStrdup(u_sess->probackup_context, labelfile); + if (tblspcmapfile != NULL) { + u_sess->proc_cxt.TblspcMapFile = MemoryContextStrdup(u_sess->probackup_context, tblspcmapfile); + } else { + u_sess->proc_cxt.TblspcMapFile = NULL; + } + } + + errorno = snprintf_s(startxlogstr, sizeof(startxlogstr), sizeof(startxlogstr) - 1, "%X/%X", + (uint32)(startpoint >> 32), (uint32)startpoint); + securec_check_ss(errorno, "", ""); + + PG_RETURN_TEXT_P(cstring_to_text(startxlogstr)); + + MemoryContextSwitchTo(oldContext); +} + +/* + * pg_stop_backup_v2: finish taking an on-line backup dump + * + */ +Datum pg_stop_backup_v2(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *)fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext perqueryctx, oldcontext, oldcontext2; + Datum values[3]; + bool nulls[3]; + XLogRecPtr stoppoint; + char stopxlogstr[MAXFNAMELEN]; + errno_t errorno = EOK; + bool exclusive = PG_GETARG_BOOL(0); + errno_t rc; + + SessionBackupState status = u_sess->proc_cxt.sessionBackupState; + + oldcontext2 = MemoryContextSwitchTo(u_sess->probackup_context); + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type must be a row type"))); + + perqueryctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(perqueryctx); + + tupstore = tuplestore_begin_heap(true, false, u_sess->attr.attr_memory.work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + 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"); + + if (exclusive) { + if (status == SESSION_BACKUP_NON_EXCLUSIVE) + ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("non-exclusive backup in progress"), errhint("Did you mean to use pg_stop_backup('f')?"))); + /* when delay xlog recycle is true, we do not copy xlog from archive */ + stoppoint = do_pg_stop_backup(NULL, !GetDelayXlogRecycle()); + nulls[1] = true; + nulls[2] = true; + } else { + if (status != SESSION_BACKUP_NON_EXCLUSIVE) + ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("non-exclusive backup is not in progress"))); + + stoppoint = do_pg_stop_backup(u_sess->proc_cxt.LabelFile, !GetDelayXlogRecycle()); + + values[1] = CStringGetTextDatum(u_sess->proc_cxt.LabelFile); + pfree(u_sess->proc_cxt.LabelFile); + u_sess->proc_cxt.LabelFile = NULL; + + if (u_sess->proc_cxt.TblspcMapFile) { + values[2] = CStringGetTextDatum(u_sess->proc_cxt.TblspcMapFile); + pfree(u_sess->proc_cxt.TblspcMapFile); + u_sess->proc_cxt.TblspcMapFile = NULL; + } else { + nulls[2] = true; + } + } + + errorno = snprintf_s(stopxlogstr, sizeof(stopxlogstr), sizeof(stopxlogstr) - 1, "%X/%X", + (uint32)(stoppoint >> 32), (uint32)stoppoint); + securec_check_ss(errorno, "", ""); + values[0] = CStringGetTextDatum(stopxlogstr); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + tuplestore_donestoring(tupstore); + + MemoryContextSwitchTo(oldcontext2); + + return (Datum) 0; +} + /* * gs_roach_stop_backup: stop roach backup with passed-in backup name * diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_200.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_200.sql index e69de29bb..e830e0a43 100644 --- a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_200.sql +++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_92_200.sql @@ -0,0 +1,2 @@ +DROP FUNCTION IF EXISTS pg_catalog.pg_start_backup(IN backupid TEXT, IN fast BOOL, IN exclusive BOOL) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pg_stop_backup(IN exclusive BOOL) CASCADE; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_200.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_200.sql index e69de29bb..3363217c1 100644 --- a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_200.sql +++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_92_200.sql @@ -0,0 +1,2 @@ +DROP FUNCTION IF EXISTS pg_catalog.pg_start_backup(IN BACKUPID TEXT, IN FAST BOOL, IN EXCLUSIVE BOOL) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pg_stop_backup(IN EXCLUSIVE BOOL) CASCADE; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_92_200.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_92_200.sql index caa5bf385..6a62dd17b 100644 --- a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_92_200.sql +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_92_200.sql @@ -2360,3 +2360,21 @@ out system_identifier pg_catalog.int8, out pg_control_last_modified pg_catalog.timestamptz) RETURNS SETOF record LANGUAGE INTERNAL VOLATILE STRICT as 'pg_control_system'; +DROP FUNCTION IF EXISTS pg_catalog.pg_start_backup(IN backupid TEXT, IN fast BOOL, IN exclusive BOOL) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pg_stop_backup(IN exclusive BOOL) CASCADE; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 6203; +CREATE OR REPLACE FUNCTION pg_catalog.pg_start_backup +(IN backupid pg_catalog.text, +IN fast pg_catalog.bool, +IN exclusive pg_catalog.bool) +RETURNS SETOF record LANGUAGE INTERNAL VOLATILE STRICT as 'pg_start_backup_v2'; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 6204; +CREATE OR REPLACE FUNCTION pg_catalog.pg_stop_backup +(IN exclusive pg_catalog.bool, +out lsn pg_catalog.text, +out labelfile pg_catalog.text, +out spcmapfile pg_catalog.text) +RETURNS SETOF record LANGUAGE INTERNAL VOLATILE STRICT as 'pg_stop_backup_v2'; + diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_92_200.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_92_200.sql index 3159d3fdc..4ca47977b 100644 --- a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_92_200.sql +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_92_200.sql @@ -2393,3 +2393,21 @@ out catalog_version_no pg_catalog.int4, out system_identifier pg_catalog.int8, out pg_control_last_modified pg_catalog.timestamptz) RETURNS SETOF record LANGUAGE INTERNAL VOLATILE STRICT as 'pg_control_system'; + +DROP FUNCTION IF EXISTS pg_catalog.pg_start_backup(IN backupid TEXT, IN fast BOOL, IN exclusive BOOL) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pg_stop_backup(IN exclusive BOOL) CASCADE; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 6203; +CREATE OR REPLACE FUNCTION pg_catalog.pg_start_backup +(IN backupid pg_catalog.text, +IN fast pg_catalog.bool, +IN exclusive pg_catalog.bool) +RETURNS SETOF record LANGUAGE INTERNAL VOLATILE STRICT as 'pg_start_backup_v2'; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 6204; +CREATE OR REPLACE FUNCTION pg_catalog.pg_stop_backup +(IN exclusive pg_catalog.bool, +out lsn pg_catalog.text, +out labelfile pg_catalog.text, +out spcmapfile pg_catalog.text) +RETURNS SETOF record LANGUAGE INTERNAL VOLATILE STRICT as 'pg_stop_backup_v2'; diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 024ec4ced..b8e45baef 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -994,7 +994,12 @@ typedef struct knl_u_proc_context { * */ enum SessionBackupState sessionBackupState; bool registerExclusiveHandlerdone; - + /* + * Store label file and tablespace map during non-exclusive backups. + */ + char* LabelFile; + char* TblspcMapFile; + bool registerAbortBackupHandlerdone; /* unterminated backups handler flag */ } knl_u_proc_context; /* maximum possible number of fields in a date string */ @@ -2278,6 +2283,7 @@ typedef struct knl_session_context { MemoryContext top_transaction_mem_cxt; MemoryContext self_mem_cxt; MemoryContext top_portal_cxt; + MemoryContext probackup_context; MemoryContextGroup* mcxt_group; /* temp_mem_cxt is a context which will be reset when the session attach to a thread */ MemoryContext temp_mem_cxt; diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h index 318afeaed..002803a5b 100644 --- a/src/include/storage/ipc.h +++ b/src/include/storage/ipc.h @@ -73,6 +73,7 @@ extern void proc_exit(int code); extern void sess_exit(int code); extern void shmem_exit(int code); extern void on_proc_exit(pg_on_exit_callback function, Datum arg); +extern void before_shmem_exit(pg_on_exit_callback function, Datum arg); extern void on_shmem_exit(pg_on_exit_callback function, Datum arg); extern void cancel_shmem_exit(pg_on_exit_callback function, Datum arg); extern void on_exit_reset(void); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 937f46b2a..49eb15196 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -2686,6 +2686,8 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 6116 | int1smaller 6117 | int1inc 6118 | set_hashbucket_info + 6203 | pg_start_backup + 6204 | pg_stop_backup 6224 | gs_get_next_xid_csn 6321 | pg_stat_file_recursive 7777 | sysdate diff --git a/src/test/regress/expected/single_node_opr_sanity.out b/src/test/regress/expected/single_node_opr_sanity.out index 7c9f3d78d..a35bd49d5 100644 --- a/src/test/regress/expected/single_node_opr_sanity.out +++ b/src/test/regress/expected/single_node_opr_sanity.out @@ -2773,6 +2773,8 @@ WHERE d.classoid IS NULL AND p1.oid <= 9999 order by 1; 6200 | mot_session_memory_detail 6201 | mot_global_memory_detail 6202 | mot_local_memory_detail + 6203 | pg_start_backup + 6204 | pg_stop_backup 6224 | gs_get_next_xid_csn 6321 | pg_stat_file_recursive 7777 | sysdate