8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 15:23:03 +01:00

Improvement #7186 : FB4 Nbackup RDB$BACKUP_HISTORY issue

This commit is contained in:
Vlad Khorsun 2022-10-06 12:24:06 +03:00
parent fe3972b3c4
commit 4ab49be70c
11 changed files with 191 additions and 6 deletions

View File

@ -265,3 +265,20 @@ Samples of use of new parameter in fbsvcmgr utility (supposing login and
password are set using some other method):
fbsvcmgr - action_nfix dbname /tmp/ecopy.fdb
9) Services API extension - support of new nbackup feature to clean history table.
(Khorsun Vlad, hvlad@users.sourceforge.net, 2022)
Action isc_action_svc_nbak get new parameter tags
isc_spb_nbk_clean_history : tell nbackup to clean RDB$HISTORY table
isc_spb_nbk_keep_days <int> : specify how many recent rows should be kept in the history
isc_spb_nbk_keep_rows <int> : specify how days back from today should be kept in the history
Examples:
- make backup of level 1, clean RDB$HISTORY table and keep 1 recent row in it:
fbsvcmgr action_nbak dbfile db.fdb nbk_file db.nbk nbk_level 1 nbk_clean_history nbk_keep_rows 1
- make backup of level 2, clean RDB$HISTORY table and keep rows for the last 7 days in it:
fbsvcmgr action_nbak dbfile db.fdb nbk_file db.nbk nbk_level 2 nbk_clean_history nbk_keep_days 7

View File

@ -458,7 +458,11 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const
return StringSpb;
case isc_spb_nbk_level:
case isc_spb_options:
case isc_spb_nbk_keep_days:
case isc_spb_nbk_keep_rows:
return IntSpb;
case isc_spb_nbk_clean_history:
return SingleTpb;
}
invalid_structure("unknown parameter for nbackup", tag);
break;

View File

@ -627,6 +627,9 @@
#define isc_spb_nbk_file 6
#define isc_spb_nbk_direct 7
#define isc_spb_nbk_guid 8
#define isc_spb_nbk_clean_history 9
#define isc_spb_nbk_keep_days 10
#define isc_spb_nbk_keep_rows 11
#define isc_spb_nbk_no_triggers 0x01
#define isc_spb_nbk_inplace 0x02
#define isc_spb_nbk_sequence 0x04

View File

@ -67,7 +67,7 @@ FB_IMPL_MSG(NBACKUP, 66, nbackup_err_eofhdr_restdb, -901, "00", "000", "Unexpect
FB_IMPL_MSG(NBACKUP, 67, nbackup_lostguid_l0bk, -901, "00", "000", "Cannot get backup guid clumplet from L0 backup")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 68, "Physical Backup Manager version @1")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 69, "Enter name of the backup file of level @1 (\".\" - do not restore further):")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 70, " -D(IRECT) [ON | OFF] Use or not direct I/O when backing up database")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 70, " -D(IRECT) <ON | OFF> Use or not direct I/O when backing up database")
FB_IMPL_MSG(NBACKUP, 71, nbackup_switchd_parameter, -901, "00", "000", "Wrong parameter @1 for switch -D, need ON or OFF")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 72, "special options are:")
FB_IMPL_MSG(NBACKUP, 73, nbackup_user_stop, -901, "08", "006", "Terminated due to user request")
@ -79,3 +79,10 @@ FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 78, " -I(NPLACE) Res
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 79, " -INPLACE option could corrupt the database that has changed since previous restore.")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 80, " -SEQ(UENCE) Preserve original replication sequence")
FB_IMPL_MSG(NBACKUP, 81, nbackup_seq_misuse, -901, "00", "000", "Switch -SEQ(UENCE) can be used only with -FIXUP or -RESTORE")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 82, " -CLEAN_HIST(ORY) Clean old records from backup history")
FB_IMPL_MSG_NO_SYMBOL(NBACKUP, 83, " -K(EEP) <N> <(R)OWS | (D)AYS> How many recent rows (or days back from today) should be kept in the history")
FB_IMPL_MSG(NBACKUP, 84, nbackup_wrong_param, -901, "00", "000", "Wrong parameter value for switch @1")
FB_IMPL_MSG(NBACKUP, 85, nbackup_clean_hist_misuse, -901, "00", "000", "Switch -CLEAN_HISTORY can be used only with -BACKUP")
FB_IMPL_MSG(NBACKUP, 86, nbackup_clean_hist_missed, -901, "00", "000", "-KEEP can be used only with -CLEAN_HISTORY")
FB_IMPL_MSG(NBACKUP, 87, nbackup_keep_hist_missed, -901, "00", "000", "-KEEP is required with -CLEAN_HISTORY")
FB_IMPL_MSG(NBACKUP, 88, nbackup_second_keep_switch, -901, "00", "000", "-KEEP can be used one time only")

View File

@ -4256,6 +4256,9 @@ const
isc_spb_nbk_file = byte(6);
isc_spb_nbk_direct = byte(7);
isc_spb_nbk_guid = byte(8);
isc_spb_nbk_clean_history = byte(9);
isc_spb_nbk_keep_days = byte(10);
isc_spb_nbk_keep_rows = byte(11);
isc_spb_nbk_no_triggers = $01;
isc_spb_nbk_inplace = $02;
isc_spb_nbk_sequence = $04;
@ -5780,6 +5783,11 @@ const
isc_nbackup_deco_parse = 337117259;
isc_nbackup_lostrec_guid_db = 337117261;
isc_nbackup_seq_misuse = 337117265;
isc_nbackup_wrong_param = 337117268;
isc_nbackup_clean_hist_misuse = 337117269;
isc_nbackup_clean_hist_missed = 337117270;
isc_nbackup_keep_hist_missed = 337117271;
isc_nbackup_second_keep_switch = 337117272;
isc_trace_conflict_acts = 337182750;
isc_trace_act_notfound = 337182751;
isc_trace_switch_once = 337182752;

View File

@ -52,6 +52,7 @@ using Jrd::idx_metadata;
using Jrd::idx_numeric;
using Jrd::idx_string;
using Jrd::idx_descending;
using Jrd::idx_timestamp_tz;
#define INDEX(id, rel, unique, count) {(id), (UCHAR) (rel), (unique), (count), {
#define SEGMENT(fld, type) {(fld), (type)}
@ -304,6 +305,10 @@ static const struct ini_idx_t indices[] =
SEGMENT(f_pubtab_tab_name, idx_string), // table name
SEGMENT(f_pubtab_pub_name, idx_string) // publication name
}},
// define index RDB$INDEX_57 for RDB$BACKUP_HISTORY RDB$TIMESTAMP;
INDEX(57, rel_backup_history, idx_descending, 1)
SEGMENT(f_backup_time, idx_timestamp_tz) // backup timestamp
}},
};
#define SYSTEM_INDEX_COUNT FB_NELEM(indices)

View File

@ -2606,6 +2606,8 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
string nbk_database, nbk_file, nbk_guid;
int nbk_level = -1;
bool cleanHistory = false, keepHistory = false;
bool val_database = false;
bool found = false;
string::size_type userPos = string::npos;
@ -2676,6 +2678,30 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
get_action_svc_string(spb, switches);
break;
case isc_spb_nbk_clean_history:
if (cleanHistory)
{
(Arg::Gds(isc_unexp_spb_form) << Arg::Str("only one isc_spb_nbk_clean_history")).raise();
}
if (!get_action_svc_parameter(spb.getClumpTag(), nbackup_action_in_sw_table, switches))
{
return false;
}
cleanHistory = true;
break;
case isc_spb_nbk_keep_days:
case isc_spb_nbk_keep_rows:
if (keepHistory)
{
(Arg::Gds(isc_unexp_spb_form) << Arg::Str("only one isc_spb_nbk_keep_days or isc_spb_nbk_keep_rows")).raise();
}
switches += "-KEEP ";
get_action_svc_data(spb, switches, false);
switches += spb.getClumpTag() == isc_spb_nbk_keep_days ? "DAYS " : "ROWS ";
keepHistory = true;
break;
default:
return false;
}
@ -3175,6 +3201,16 @@ bool Service::process_switches(ClumpletReader& spb, string& switches)
}
else
switches += nbk_guid;
if (!cleanHistory && keepHistory)
{
(Arg::Gds(isc_missing_required_spb) << Arg::Str("isc_spb_nbk_clean_history")).raise();
}
if (cleanHistory && !keepHistory)
{
(Arg::Gds(isc_missing_required_spb) << Arg::Str("isc_spb_nbk_keep_days or isc_spb_nbk_keep_rows")).raise();
}
}
switches += nbk_database;
switches += nbk_file;

View File

@ -2005,7 +2005,6 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
{
case rel_database:
case rel_log:
case rel_backup_history:
case rel_global_auth_mapping:
protect_system_table_delupd(tdbb, relation, "DELETE", true);
break;
@ -2293,6 +2292,11 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
DFW_post_work(transaction, dfw_grant, &desc, id);
break;
case rel_backup_history:
if (!tdbb->getAttachment()->locksmith(tdbb, USE_NBACKUP_UTILITY))
protect_system_table_delupd(tdbb, relation, "DELETE", true);
break;
case rel_pub_tables:
protect_system_table_delupd(tdbb, relation, "DELETE");
DFW_post_work(transaction, dfw_change_repl_state, "", 1);

View File

@ -559,6 +559,9 @@ const SvcSwitches nbackOptions[] =
{"nbk_guid", putStringArgument, 0, isc_spb_nbk_guid, 0},
{"nbk_no_triggers", putOption, 0, isc_spb_nbk_no_triggers, 0},
{"nbk_direct", putStringArgument, 0, isc_spb_nbk_direct, 0},
{"nbk_clean_history", putSingleTag, 0, isc_spb_nbk_clean_history, 0},
{"nbk_keep_days", putIntArgument, 0, isc_spb_nbk_keep_days, 0},
{"nbk_keep_rows", putIntArgument, 0, isc_spb_nbk_keep_rows, 0},
{0, 0, 0, 0, 0}
};

View File

@ -271,13 +271,17 @@ struct inc_header
class NBackup
{
public:
enum CLEAN_HISTORY_KIND { NONE, DAYS, ROWS };
NBackup(UtilSvc* _uSvc, const PathName& _database, const string& _username, const string& _role,
const string& _password, bool _run_db_triggers, bool _direct_io, const string& _deco)
const string& _password, bool _run_db_triggers, bool _direct_io, const string& _deco,
CLEAN_HISTORY_KIND cleanHistKind, int keepHistValue)
: uSvc(_uSvc), newdb(0), trans(0), database(_database),
username(_username), role(_role), password(_password),
run_db_triggers(_run_db_triggers), direct_io(_direct_io),
dbase(INVALID_HANDLE_VALUE), backup(INVALID_HANDLE_VALUE),
decompress(_deco), childId(0), db_size_pages(0),
decompress(_deco), m_cleanHistKind(cleanHistKind), m_keepHistValue(keepHistValue),
childId(0), db_size_pages(0),
m_odsNumber(0), m_silent(false), m_printed(false), m_flash_map(false)
{
// Recognition of local prefix allows to work with
@ -334,6 +338,8 @@ private:
FILE_HANDLE dbase;
FILE_HANDLE backup;
string decompress;
const CLEAN_HISTORY_KIND m_cleanHistKind;
const int m_keepHistValue;
#ifdef WIN_NT
HANDLE childId;
HANDLE childStdErr;
@ -361,6 +367,7 @@ private:
void attach_database();
void detach_database();
string to_system(const PathName& from);
void cleanHistory();
// Create/open database and backup
void open_database_write(bool exclusive = false);
@ -576,11 +583,16 @@ void NBackup::create_database()
void NBackup::close_database()
{
if (dbase == INVALID_HANDLE_VALUE)
return;
#ifdef WIN_NT
CloseHandle(dbase);
#else
close(dbase);
#endif
dbase = INVALID_HANDLE_VALUE;
}
string NBackup::to_system(const PathName& from)
@ -793,6 +805,10 @@ void NBackup::close_backup()
{
if (bakname == "stdout")
return;
if (backup == INVALID_HANDLE_VALUE)
return;
#ifdef WIN_NT
CloseHandle(backup);
if (childId != 0)
@ -817,6 +833,7 @@ void NBackup::close_backup()
childId = 0;
}
#endif
backup = INVALID_HANDLE_VALUE;
}
void NBackup::fixup_database(bool repl_seq, bool set_readonly)
@ -1056,6 +1073,31 @@ void NBackup::internal_lock_database()
pr_error(status, "begin backup: commit");
}
void NBackup::cleanHistory()
{
if (m_cleanHistKind == NONE)
return;
string sql;
if (m_cleanHistKind == DAYS)
{
sql.printf(
"DELETE FROM RDB$BACKUP_HISTORY WHERE RDB$TIMESTAMP < DATEADD(1 - %i DAY TO CURRENT_DATE)",
m_keepHistValue);
}
else
{
sql.printf(
"DELETE FROM RDB$BACKUP_HISTORY WHERE RDB$TIMESTAMP <= "
"(SELECT RDB$TIMESTAMP FROM RDB$BACKUP_HISTORY ORDER BY RDB$TIMESTAMP DESC "
"OFFSET %i ROWS FETCH FIRST 1 ROW ONLY)",
m_keepHistValue);
}
if (isc_dsql_execute_immediate(status, &newdb, &trans, 0, sql.c_str(), SQL_DIALECT_CURRENT, NULL))
pr_error(status, "execute history delete");
}
void NBackup::get_database_size()
{
db_size_pages = 0;
@ -1243,7 +1285,7 @@ void NBackup::backup_database(int level, Guid& guid, const PathName& fname)
default:
pr_error(status, "fetch history query");
}
isc_dsql_free_statement(status, &stmt, DSQL_close);
isc_dsql_free_statement(status, &stmt, DSQL_drop);
if (isc_commit_transaction(status, &trans))
pr_error(status, "commit history query");
}
@ -1560,6 +1602,9 @@ void NBackup::backup_database(int level, Guid& guid, const PathName& fname)
in_sqlda->sqlvar[3].sqlind = &null_flag;
if (isc_dsql_execute(status, &trans, &stmt, 1, in_sqlda))
pr_error(status, "execute history insert");
cleanHistory();
isc_dsql_free_statement(status, &stmt, DSQL_drop);
if (isc_commit_transaction(status, &trans))
pr_error(status, "commit history insert");
@ -1871,6 +1916,9 @@ void nbackup(UtilSvc* uSvc)
Guid guid;
bool print_size = false, version = false, inc_rest = false, repl_seq = false;
string onOff;
bool cleanHistory = false;
NBackup::CLEAN_HISTORY_KIND cleanHistKind = NBackup::CLEAN_HISTORY_KIND::NONE;
int keepHistValue = 0;
const Switches switches(nbackup_action_in_sw_table, FB_NELEM(nbackup_action_in_sw_table),
false, true);
@ -2054,6 +2102,37 @@ void nbackup(UtilSvc* uSvc)
repl_seq = true;
break;
case IN_SW_NBK_CLEAN_HISTORY:
cleanHistory = true;
break;
case IN_SW_NBK_KEEP:
if (cleanHistKind != NBackup::CLEAN_HISTORY_KIND::NONE)
usage(uSvc, isc_nbackup_second_keep_switch);
if (++itr >= argc)
missingParameterForSwitch(uSvc, argv[itr - 1]);
keepHistValue = atoi(argv[itr]);
if (keepHistValue < 1)
usage(uSvc, isc_nbackup_wrong_param, argv[itr - 1]);
if (++itr >= argc)
missingParameterForSwitch(uSvc, argv[itr - 1]);
{ // scope
string keepUnit = argv[itr];
keepUnit.upper();
if (string("DAYS").find(keepUnit) == 0)
cleanHistKind = NBackup::CLEAN_HISTORY_KIND::DAYS;
else if (string("ROWS").find(keepUnit) == 0)
cleanHistKind = NBackup::CLEAN_HISTORY_KIND::ROWS;
else
usage(uSvc, isc_nbackup_wrong_param, argv[itr - 2]);
}
break;
default:
usage(uSvc, isc_nbackup_unknown_switch, argv[itr]);
break;
@ -2084,7 +2163,22 @@ void nbackup(UtilSvc* uSvc)
usage(uSvc, isc_nbackup_seq_misuse);
}
NBackup nbk(uSvc, database, username, role, password, run_db_triggers, direct_io, decompress);
if (cleanHistory)
{
// CLEAN_HISTORY could be used with BACKUP only
if (op != nbBackup)
usage(uSvc, isc_nbackup_clean_hist_misuse);
if (cleanHistKind == NBackup::CLEAN_HISTORY_KIND::NONE)
usage(uSvc, isc_nbackup_keep_hist_missed);
}
else if (cleanHistKind != NBackup::CLEAN_HISTORY_KIND::NONE)
{
usage(uSvc, isc_nbackup_clean_hist_missed);
}
NBackup nbk(uSvc, database, username, role, password, run_db_triggers, direct_io,
decompress, cleanHistKind, keepHistValue);
try
{
switch (op)

View File

@ -49,6 +49,8 @@ const int IN_SW_NBK_DECOMPRESS = 14;
const int IN_SW_NBK_ROLE = 15;
const int IN_SW_NBK_INPLACE = 16;
const int IN_SW_NBK_SEQUENCE = 17;
const int IN_SW_NBK_CLEAN_HISTORY = 18;
const int IN_SW_NBK_KEEP = 19;
static const struct Switches::in_sw_tab_t nbackup_in_sw_table [] =
@ -75,6 +77,8 @@ static const struct Switches::in_sw_tab_t nbackup_action_in_sw_table [] =
{IN_SW_NBK_SIZE, 0, "SIZE", 0, 0, 0, false, false, 17, 1, NULL, nboSpecial},
{IN_SW_NBK_DECOMPRESS, 0, "DECOMPRESS", 0, 0, 0, false, false, 74, 2, NULL, nboSpecial},
{IN_SW_NBK_SEQUENCE, 0, "SEQUENCE", 0, 0, 0, false, false, 80, 3, NULL, nboSpecial},
{IN_SW_NBK_CLEAN_HISTORY, isc_spb_nbk_clean_history, "CLEAN_HISTORY", 0, 0, 0, false, false, 82, 10, NULL, nboSpecial},
{IN_SW_NBK_KEEP, 0, "KEEP", 0, 0, 0, false, false, 83, 1, NULL, nboSpecial},
{IN_SW_NBK_NODBTRIG, 0, "T", 0, 0, 0, false, false, 0, 1, NULL, nboGeneral},
{IN_SW_NBK_NODBTRIG, 0, "NODBTRIGGERS", 0, 0, 0, false, false, 16, 3, NULL, nboGeneral},
{IN_SW_NBK_USER_NAME, 0, "USER", 0, 0, 0, false, false, 13, 1, NULL, nboGeneral},