1238 lines
38 KiB
C++
1238 lines
38 KiB
C++
/*-------------------------------------------------------------------------
|
|
*
|
|
* show.c: show backup information.
|
|
*
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
|
* Portions Copyright (c) 2015-2019, Postgres Professional
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "pg_probackup.h"
|
|
|
|
#include <time.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "json.h"
|
|
#include "common/fe_memutils.h"
|
|
|
|
#define half_rounded(x) (((x) + ((x) < 0 ? 0 : 1)) / 2)
|
|
|
|
/* struct to align fields printed in plain format */
|
|
typedef struct ShowBackendRow
|
|
{
|
|
const char *instance;
|
|
const char *version;
|
|
char backup_id[20];
|
|
char recovery_time[100];
|
|
const char *mode;
|
|
const char *wal_mode;
|
|
char tli[20];
|
|
char duration[20];
|
|
char data_bytes[20];
|
|
char wal_bytes[20];
|
|
char zratio[20];
|
|
char start_lsn[20];
|
|
char stop_lsn[20];
|
|
char type[20];
|
|
const char *status;
|
|
} ShowBackendRow;
|
|
|
|
/* struct to align fields printed in plain format */
|
|
typedef struct ShowArchiveRow
|
|
{
|
|
char tli[20];
|
|
char parent_tli[20];
|
|
char switchpoint[20];
|
|
char min_segno[MAXFNAMELEN];
|
|
char max_segno[MAXFNAMELEN];
|
|
char n_segments[20];
|
|
char size[20];
|
|
char zratio[20];
|
|
const char *status;
|
|
char n_backups[20];
|
|
} ShowArchiveRow;
|
|
|
|
static void show_instance_start(void);
|
|
static void show_instance_end(void);
|
|
static void show_instance(InstanceConfig *instance, time_t requested_backup_id, bool show_name);
|
|
static void print_backup_json_object(PQExpBuffer buf, pgBackup *backup);
|
|
static int show_backup(const char *instance_name, time_t requested_backup_id);
|
|
|
|
static void show_instance_plain(const char *instance_name, device_type_t instance_type,
|
|
parray *backup_list, bool show_name);
|
|
static void show_instance_json(const char *instance_name, parray *backup_list);
|
|
|
|
static void show_instance_archive(InstanceConfig *instance);
|
|
static void show_archive_plain(const char *instance_name, uint32 xlog_seg_size,
|
|
parray *timelines_list, bool show_name);
|
|
static void show_archive_json(const char *instance_name, uint32 xlog_seg_size,
|
|
parray *tli_list);
|
|
|
|
static PQExpBufferData show_buf;
|
|
static bool first_instance = true;
|
|
static int32 json_level = 0;
|
|
|
|
/*
|
|
* Entry point of pg_probackup SHOW subcommand.
|
|
*/
|
|
int
|
|
do_show(const char *instance_name, time_t requested_backup_id, bool show_archive)
|
|
{
|
|
size_t i;
|
|
|
|
if (instance_name == NULL &&
|
|
requested_backup_id != INVALID_BACKUP_ID)
|
|
elog(ERROR, "You must specify --instance to use (-i, --backup-id) option");
|
|
|
|
if (show_archive &&
|
|
requested_backup_id != INVALID_BACKUP_ID)
|
|
elog(ERROR, "You cannot specify --archive and (-i, --backup-id) options together");
|
|
|
|
/*
|
|
* if instance_name is not specified,
|
|
* show information about all instances in this backup catalog
|
|
*/
|
|
if (instance_name == NULL)
|
|
{
|
|
parray *instances = catalog_get_instance_list();
|
|
|
|
show_instance_start();
|
|
for (i = 0; i < parray_num(instances); i++)
|
|
{
|
|
InstanceConfig *instance = (InstanceConfig *)parray_get(instances, i);
|
|
char backup_instance_path[MAXPGPATH];
|
|
|
|
errno_t rc = sprintf_s(backup_instance_path, MAXPGPATH, "%s/%s/%s",
|
|
backup_path, BACKUPS_DIR, instance->name);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
|
|
if (show_archive)
|
|
show_instance_archive(instance);
|
|
else
|
|
show_instance(instance, INVALID_BACKUP_ID, true);
|
|
}
|
|
show_instance_end();
|
|
|
|
parray_walk(instances, pfree);
|
|
parray_free(instances);
|
|
|
|
return 0;
|
|
}
|
|
/* always use */
|
|
else if (show_format == SHOW_JSON ||
|
|
requested_backup_id == INVALID_BACKUP_ID)
|
|
{
|
|
show_instance_start();
|
|
InstanceConfig *instance = readInstanceConfigFile(instance_name);
|
|
|
|
if (show_archive)
|
|
{
|
|
if (instance == NULL) {
|
|
return 0;
|
|
}
|
|
show_instance_archive(instance);
|
|
}
|
|
else
|
|
show_instance(instance, requested_backup_id, false);
|
|
|
|
show_instance_end();
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if (show_archive)
|
|
{
|
|
InstanceConfig *instance = readInstanceConfigFile(instance_name);
|
|
if (instance == NULL) {
|
|
return 0;
|
|
}
|
|
show_instance_archive(instance);
|
|
}
|
|
else
|
|
show_backup(instance_name, requested_backup_id);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
pretty_size(int64 size, char *buf, size_t len)
|
|
{
|
|
int64 limit = 10 * 1024;
|
|
int64 limit2 = limit * 2 - 1;
|
|
errno_t rc = 0;
|
|
|
|
if (len == 0) {
|
|
return;
|
|
}
|
|
if (size <= 0)
|
|
{
|
|
rc = strncpy_s(buf, len, "0", len - 1);
|
|
securec_check_c(rc, "", "");
|
|
return;
|
|
}
|
|
|
|
if (Abs(size) < limit)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%dB", (int) size);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
else
|
|
{
|
|
size >>= 9;
|
|
if (Abs(size) < limit2)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%dkB", (int) half_rounded(size));
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
else
|
|
{
|
|
size >>= 10;
|
|
if (Abs(size) < limit2)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%dMB", (int) half_rounded(size));
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
else
|
|
{
|
|
size >>= 10;
|
|
if (Abs(size) < limit2)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%dGB", (int) half_rounded(size));
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
else
|
|
{
|
|
size >>= 10;
|
|
rc = snprintf_s(buf, len, len - 1, "%dTB", (int) half_rounded(size));
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
pretty_time_interval(double time, char *buf, size_t len)
|
|
{
|
|
int num_seconds = 0;
|
|
int milliseconds = 0;
|
|
int seconds = 0;
|
|
int minutes = 0;
|
|
int hours = 0;
|
|
int days = 0;
|
|
errno_t rc = 0;
|
|
|
|
num_seconds = (int) time;
|
|
if (len == 0) {
|
|
return;
|
|
}
|
|
if (time <= 0)
|
|
{
|
|
rc = strncpy_s(buf, len, "0", len - 1);
|
|
securec_check_c(rc, "", "");
|
|
return;
|
|
}
|
|
|
|
days = num_seconds / (24 * 3600);
|
|
num_seconds %= (24 * 3600);
|
|
|
|
hours = num_seconds / 3600;
|
|
num_seconds %= 3600;
|
|
|
|
minutes = num_seconds / 60;
|
|
num_seconds %= 60;
|
|
|
|
seconds = num_seconds;
|
|
milliseconds = (int)((time - (int) time) * 1000.0);
|
|
|
|
if (days > 0)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%dd:%dh", days, hours);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
return;
|
|
}
|
|
|
|
if (hours > 0)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%dh:%dm", hours, minutes);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
return;
|
|
}
|
|
|
|
if (minutes > 0)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%dm:%ds", minutes, seconds);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
return;
|
|
}
|
|
|
|
if (seconds > 0)
|
|
{
|
|
if (milliseconds > 0)
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%ds:%dms", seconds, milliseconds);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
else
|
|
{
|
|
rc = snprintf_s(buf, len, len - 1, "%ds", seconds);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
return;
|
|
}
|
|
|
|
rc = snprintf_s(buf, len, len - 1, "%dms", milliseconds);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Initialize instance visualization.
|
|
*/
|
|
static void
|
|
show_instance_start(void)
|
|
{
|
|
initPQExpBuffer(&show_buf);
|
|
|
|
if (show_format == SHOW_PLAIN)
|
|
return;
|
|
|
|
first_instance = true;
|
|
json_level = 0;
|
|
|
|
appendPQExpBufferChar(&show_buf, '[');
|
|
json_level++;
|
|
}
|
|
|
|
/*
|
|
* Finalize instance visualization.
|
|
*/
|
|
static void
|
|
show_instance_end(void)
|
|
{
|
|
if (show_format == SHOW_JSON)
|
|
appendPQExpBufferStr(&show_buf, "\n]\n");
|
|
|
|
fputs(show_buf.data, stdout);
|
|
termPQExpBuffer(&show_buf);
|
|
}
|
|
|
|
/*
|
|
* Show brief meta information about all backups in the backup instance.
|
|
*/
|
|
static void
|
|
show_instance(InstanceConfig *instance, time_t requested_backup_id, bool show_name)
|
|
{
|
|
parray *backup_list;
|
|
const char *instance_name;
|
|
device_type_t instance_type;
|
|
|
|
instance_name = instance->name;
|
|
instance_type = instance->dss.enable_dss ? DEV_TYPE_DSS : DEV_TYPE_FILE;
|
|
backup_list = catalog_get_backup_list(instance_name, requested_backup_id);
|
|
|
|
if (show_format == SHOW_PLAIN)
|
|
show_instance_plain(instance_name, instance_type, backup_list, show_name);
|
|
else if (show_format == SHOW_JSON)
|
|
show_instance_json(instance_name, backup_list);
|
|
else
|
|
elog(ERROR, "Invalid show format %d", (int) show_format);
|
|
|
|
/* cleanup */
|
|
parray_walk(backup_list, pgBackupFree);
|
|
parray_free(backup_list);
|
|
}
|
|
|
|
/* helper routine to print backup info as json object */
|
|
static void
|
|
print_backup_json_object(PQExpBuffer buf, pgBackup *backup)
|
|
{
|
|
TimeLineID parent_tli = 0;
|
|
char timestamp[100] = "----";
|
|
char lsn[20];
|
|
errno_t rc = 0;
|
|
|
|
json_add(buf, JT_BEGIN_OBJECT, &json_level);
|
|
|
|
json_add_value(buf, "id", base36enc(backup->start_time), json_level,
|
|
true);
|
|
|
|
if (backup->parent_backup != 0)
|
|
json_add_value(buf, "parent-backup-id",
|
|
base36enc(backup->parent_backup), json_level, true);
|
|
|
|
json_add_value(buf, "backup-mode", pgBackupGetBackupMode(backup),
|
|
json_level, true);
|
|
|
|
json_add_value(buf, "wal", backup->stream ? "STREAM": "ARCHIVE",
|
|
json_level, true);
|
|
|
|
json_add_value(buf, "compress-alg",
|
|
deparse_compress_alg(backup->compress_alg), json_level,
|
|
true);
|
|
|
|
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);
|
|
|
|
json_add_key(buf, "xlog-block-size", json_level);
|
|
appendPQExpBuffer(buf, "%u", backup->wal_block_size);
|
|
|
|
json_add_key(buf, "checksum-version", json_level);
|
|
appendPQExpBuffer(buf, "%u", backup->checksum_version);
|
|
|
|
json_add_value(buf, "program-version", backup->program_version,
|
|
json_level, true);
|
|
json_add_value(buf, "server-version", backup->server_version,
|
|
json_level, true);
|
|
|
|
json_add_key(buf, "current-tli", json_level);
|
|
appendPQExpBuffer(buf, "%u", backup->tli);
|
|
|
|
json_add_key(buf, "parent-tli", json_level);
|
|
|
|
/* Only incremental backup can have Parent TLI */
|
|
if (backup->parent_backup_link)
|
|
parent_tli = backup->parent_backup_link->tli;
|
|
|
|
appendPQExpBuffer(buf, "%u", parent_tli);
|
|
|
|
rc = snprintf_s(lsn, lengthof(lsn), lengthof(lsn) - 1, "%X/%X",
|
|
(uint32) (backup->start_lsn >> 32), (uint32) backup->start_lsn);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
json_add_value(buf, "start-lsn", lsn, json_level, true);
|
|
|
|
rc = snprintf_s(lsn, lengthof(lsn), lengthof(lsn) - 1, "%X/%X",
|
|
(uint32) (backup->stop_lsn >> 32), (uint32) backup->stop_lsn);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
json_add_value(buf, "stop-lsn", lsn, json_level, true);
|
|
|
|
time2iso(timestamp, lengthof(timestamp), backup->start_time);
|
|
json_add_value(buf, "start-time", timestamp, json_level, true);
|
|
|
|
if (backup->end_time)
|
|
{
|
|
time2iso(timestamp, lengthof(timestamp), backup->end_time);
|
|
json_add_value(buf, "end-time", timestamp, json_level, true);
|
|
}
|
|
|
|
json_add_key(buf, "recovery-xid", json_level);
|
|
appendPQExpBuffer(buf, XID_FMT, backup->recovery_xid);
|
|
|
|
if (backup->recovery_time > 0)
|
|
{
|
|
time2iso(timestamp, lengthof(timestamp), backup->recovery_time);
|
|
json_add_value(buf, "recovery-time", timestamp, json_level, true);
|
|
}
|
|
|
|
if (backup->recovery_name)
|
|
{
|
|
json_add_value(buf, "recovery-name", backup->recovery_name, json_level, true);
|
|
}
|
|
|
|
if (backup->expire_time > 0)
|
|
{
|
|
time2iso(timestamp, lengthof(timestamp), backup->expire_time);
|
|
json_add_value(buf, "expire-time", timestamp, json_level, true);
|
|
}
|
|
|
|
if (backup->data_bytes != BYTES_INVALID)
|
|
{
|
|
json_add_key(buf, "data-bytes", json_level);
|
|
appendPQExpBuffer(buf, INT64_FORMAT, backup->data_bytes);
|
|
}
|
|
|
|
if (backup->wal_bytes != BYTES_INVALID)
|
|
{
|
|
json_add_key(buf, "wal-bytes", json_level);
|
|
appendPQExpBuffer(buf, INT64_FORMAT, backup->wal_bytes);
|
|
}
|
|
|
|
if (backup->uncompressed_bytes >= 0)
|
|
{
|
|
json_add_key(buf, "uncompressed-bytes", json_level);
|
|
appendPQExpBuffer(buf, INT64_FORMAT, backup->uncompressed_bytes);
|
|
}
|
|
|
|
if (backup->uncompressed_bytes >= 0)
|
|
{
|
|
json_add_key(buf, "pgdata-bytes", json_level);
|
|
appendPQExpBuffer(buf, INT64_FORMAT, backup->pgdata_bytes);
|
|
}
|
|
|
|
if (backup->uncompressed_bytes >= 0)
|
|
{
|
|
json_add_key(buf, "dssdata-bytes", json_level);
|
|
appendPQExpBuffer(buf, INT64_FORMAT, backup->dssdata_bytes);
|
|
}
|
|
|
|
if (backup->external_dir_str)
|
|
json_add_value(buf, "external-dirs", backup->external_dir_str,
|
|
json_level, true);
|
|
|
|
json_add_value(buf, "status", status2str(backup->status), json_level,
|
|
true);
|
|
|
|
if (backup->note)
|
|
json_add_value(buf, "note", backup->note,
|
|
json_level, true);
|
|
|
|
if (backup->content_crc != 0)
|
|
{
|
|
json_add_key(buf, "content-crc", json_level);
|
|
appendPQExpBuffer(buf, "%u", backup->content_crc);
|
|
}
|
|
|
|
json_add(buf, JT_END_OBJECT, &json_level);
|
|
}
|
|
|
|
/*
|
|
* Show detailed meta information about specified backup.
|
|
*/
|
|
static int
|
|
show_backup(const char *instance_name, time_t requested_backup_id)
|
|
{
|
|
size_t i;
|
|
pgBackup *backup = NULL;
|
|
parray *backups;
|
|
|
|
backups = catalog_get_backup_list(instance_name, INVALID_BACKUP_ID);
|
|
|
|
/* Find requested backup */
|
|
for (i = 0; i < parray_num(backups); i++)
|
|
{
|
|
pgBackup *tmp_backup = (pgBackup *) parray_get(backups, i);
|
|
|
|
/* found target */
|
|
if (tmp_backup->start_time == requested_backup_id)
|
|
{
|
|
backup = tmp_backup;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (backup == NULL)
|
|
{
|
|
// TODO for 3.0: we should ERROR out here.
|
|
elog(INFO, "Requested backup \"%s\" is not found.",
|
|
/* We do not need free base36enc's result, we exit anyway */
|
|
base36enc(requested_backup_id));
|
|
/* This is not error */
|
|
parray_walk(backups, pgBackupFree);
|
|
parray_free(backups);
|
|
return 0;
|
|
}
|
|
|
|
if (show_format == SHOW_PLAIN)
|
|
pgBackupWriteControl(stdout, backup);
|
|
else
|
|
elog(ERROR, "Invalid show format %d", (int) show_format);
|
|
|
|
/* cleanup */
|
|
parray_walk(backups, pgBackupFree);
|
|
parray_free(backups);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void process_time(pgBackup *backup, ShowBackendRow *row)
|
|
{
|
|
if (backup->status == BACKUP_STATUS_RUNNING)
|
|
pretty_time_interval(difftime(current_time, backup->start_time),
|
|
row->duration, lengthof(row->duration));
|
|
else if (backup->merge_time != (time_t) 0)
|
|
pretty_time_interval(difftime(backup->end_time, backup->merge_time),
|
|
row->duration, lengthof(row->duration));
|
|
else if (backup->end_time != (time_t) 0)
|
|
pretty_time_interval(difftime(backup->end_time, backup->start_time),
|
|
row->duration, lengthof(row->duration));
|
|
else
|
|
strncpy_s(row->duration, sizeof(row->duration), "----", sizeof(row->duration));
|
|
}
|
|
|
|
/*
|
|
* Show instance backups in plain format.
|
|
*/
|
|
static void
|
|
show_instance_plain(const char *instance_name, device_type_t instance_type, parray *backup_list, bool show_name)
|
|
{
|
|
#define SHOW_FIELDS_COUNT 15
|
|
int i;
|
|
const char *names[SHOW_FIELDS_COUNT] =
|
|
{ "Instance", "Version", "ID", "Recovery Time",
|
|
"Mode", "WAL Mode", "TLI", "Time", "Data", "WAL",
|
|
"Zratio", "Start LSN", "Stop LSN", "Type", "Status" };
|
|
const char *field_formats[SHOW_FIELDS_COUNT] =
|
|
{ " %-*s ", " %-*s ", " %-*s ", " %-*s ",
|
|
" %-*s ", " %-*s ", " %-*s ", " %*s ", " %*s ", " %*s ",
|
|
" %*s ", " %-*s ", " %-*s ", " %-*s ", " %-*s "};
|
|
uint32 widths[SHOW_FIELDS_COUNT];
|
|
uint32 widths_sum = 0;
|
|
ShowBackendRow *rows = NULL;
|
|
TimeLineID parent_tli = 0;
|
|
|
|
for (i = 0; i < SHOW_FIELDS_COUNT; i++)
|
|
widths[i] = strlen(names[i]);
|
|
|
|
rows = (ShowBackendRow *) palloc(parray_num(backup_list) *
|
|
sizeof(ShowBackendRow));
|
|
if (rows == NULL) {
|
|
elog(ERROR, "Out of memory");
|
|
}
|
|
|
|
/*
|
|
* Fill row values and calculate maximum width of each field.
|
|
*/
|
|
for (i = 0; (size_t)i < parray_num(backup_list); i++)
|
|
{
|
|
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
|
|
ShowBackendRow *row = &rows[i];
|
|
int cur = 0;
|
|
float zratio = 1;
|
|
|
|
/* Instance */
|
|
row->instance = instance_name;
|
|
widths[cur] = Max(widths[cur], strlen(row->instance));
|
|
cur++;
|
|
|
|
/* Version */
|
|
row->version = backup->server_version[0] ?
|
|
backup->server_version : "----";
|
|
widths[cur] = Max(widths[cur], strlen(row->version));
|
|
cur++;
|
|
|
|
/* ID */
|
|
errno_t rc = snprintf_s(row->backup_id, lengthof(row->backup_id), lengthof(row->backup_id) - 1, "%s",
|
|
base36enc(backup->start_time));
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->backup_id));
|
|
cur++;
|
|
|
|
/* Recovery Time */
|
|
if (backup->recovery_time != (time_t) 0)
|
|
time2iso(row->recovery_time, lengthof(row->recovery_time),
|
|
backup->recovery_time);
|
|
else {
|
|
rc = strncpy_s(row->recovery_time, sizeof(row->recovery_time), "----", sizeof(row->recovery_time) - 1);
|
|
securec_check_c(rc, "\0", "\0");
|
|
}
|
|
widths[cur] = Max(widths[cur], strlen(row->recovery_time));
|
|
cur++;
|
|
|
|
/* Mode */
|
|
row->mode = pgBackupGetBackupMode(backup);
|
|
widths[cur] = Max(widths[cur], strlen(row->mode));
|
|
cur++;
|
|
|
|
/* WAL mode*/
|
|
row->wal_mode = backup->stream ? "STREAM": "ARCHIVE";
|
|
widths[cur] = Max(widths[cur], strlen(row->wal_mode));
|
|
cur++;
|
|
|
|
/* Current/Parent TLI */
|
|
if (backup->parent_backup_link != NULL)
|
|
parent_tli = backup->parent_backup_link->tli;
|
|
|
|
rc = snprintf_s(row->tli, lengthof(row->tli), lengthof(row->tli) - 1, "%u/%u",
|
|
backup->tli,
|
|
backup->backup_mode == BACKUP_MODE_FULL ? 0 : parent_tli);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->tli));
|
|
cur++;
|
|
|
|
/* Time */
|
|
process_time(backup, row);
|
|
widths[cur] = Max(widths[cur], strlen(row->duration));
|
|
cur++;
|
|
|
|
/* Data */
|
|
pretty_size(backup->data_bytes, row->data_bytes,
|
|
lengthof(row->data_bytes));
|
|
widths[cur] = Max(widths[cur], strlen(row->data_bytes));
|
|
cur++;
|
|
|
|
/* WAL */
|
|
pretty_size(backup->wal_bytes, row->wal_bytes,
|
|
lengthof(row->wal_bytes));
|
|
widths[cur] = Max(widths[cur], strlen(row->wal_bytes));
|
|
cur++;
|
|
|
|
/* Zratio (compression ratio) */
|
|
if (backup->uncompressed_bytes != BYTES_INVALID &&
|
|
(backup->uncompressed_bytes > 0 && backup->data_bytes > 0))
|
|
{
|
|
zratio = (float)backup->uncompressed_bytes / (backup->data_bytes);
|
|
rc = snprintf_s(row->zratio, lengthof(row->zratio), lengthof(row->zratio) - 1, "%.2f", zratio);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
else {
|
|
rc = snprintf_s(row->zratio, lengthof(row->zratio), lengthof(row->zratio) - 1, "%.2f", zratio);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
|
|
widths[cur] = Max(widths[cur], strlen(row->zratio));
|
|
cur++;
|
|
|
|
/* Start LSN */
|
|
rc = snprintf_s(row->start_lsn, lengthof(row->start_lsn), lengthof(row->start_lsn) - 1,
|
|
"%X/%X",
|
|
(uint32) (backup->start_lsn >> 32),
|
|
(uint32) backup->start_lsn);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->start_lsn));
|
|
cur++;
|
|
|
|
/* Stop LSN */
|
|
rc = snprintf_s(row->stop_lsn, lengthof(row->stop_lsn), lengthof(row->stop_lsn) - 1,
|
|
"%X/%X",
|
|
(uint32) (backup->stop_lsn >> 32),
|
|
(uint32) backup->stop_lsn);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->stop_lsn));
|
|
cur++;
|
|
|
|
/* Type (FILE OR DSS) */
|
|
rc = snprintf_s(row->type, lengthof(row->type), lengthof(row->type) - 1, "%s", dev2str(instance_type));
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], (uint32)strlen(row->type));
|
|
cur++;
|
|
|
|
/* Status */
|
|
row->status = status2str(backup->status);
|
|
widths[cur] = Max(widths[cur], strlen(row->status));
|
|
}
|
|
|
|
for (i = 0; i < SHOW_FIELDS_COUNT; i++)
|
|
widths_sum += widths[i] + 2 /* two space */;
|
|
|
|
if (show_name)
|
|
appendPQExpBuffer(&show_buf, "\nBACKUP INSTANCE '%s'\n", instance_name);
|
|
|
|
/*
|
|
* Print header.
|
|
*/
|
|
for (i = 0; (uint32)i < widths_sum; i++)
|
|
appendPQExpBufferChar(&show_buf, '=');
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
|
|
for (i = 0; i < SHOW_FIELDS_COUNT; i++)
|
|
{
|
|
appendPQExpBuffer(&show_buf, field_formats[i], widths[i], names[i]);
|
|
}
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
|
|
for (i = 0; (uint32)i < widths_sum; i++)
|
|
appendPQExpBufferChar(&show_buf, '=');
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
|
|
/*
|
|
* Print values.
|
|
*/
|
|
for (i = 0; (size_t)i < parray_num(backup_list); i++)
|
|
{
|
|
ShowBackendRow *row = &rows[i];
|
|
int cur = 0;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->instance);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->version);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->backup_id);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->recovery_time);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->mode);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->wal_mode);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->tli);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->duration);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->data_bytes);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->wal_bytes);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->zratio);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->start_lsn);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->stop_lsn);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->type);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->status);
|
|
cur++;
|
|
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
}
|
|
|
|
pfree(rows);
|
|
}
|
|
|
|
/*
|
|
* Show instance backups in json format.
|
|
*/
|
|
static void
|
|
show_instance_json(const char *instance_name, parray *backup_list)
|
|
{
|
|
int i;
|
|
PQExpBuffer buf = &show_buf;
|
|
|
|
if (!first_instance)
|
|
appendPQExpBufferChar(buf, ',');
|
|
|
|
/* Begin of instance object */
|
|
json_add(buf, JT_BEGIN_OBJECT, &json_level);
|
|
|
|
json_add_value(buf, "instance", instance_name, json_level, true);
|
|
json_add_key(buf, "backups", json_level);
|
|
|
|
/*
|
|
* List backups.
|
|
*/
|
|
json_add(buf, JT_BEGIN_ARRAY, &json_level);
|
|
|
|
for (i = 0; (size_t)i < parray_num(backup_list); i++)
|
|
{
|
|
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
|
|
|
|
if (i != 0)
|
|
appendPQExpBufferChar(buf, ',');
|
|
|
|
print_backup_json_object(buf, backup);
|
|
}
|
|
|
|
/* End of backups */
|
|
json_add(buf, JT_END_ARRAY, &json_level);
|
|
|
|
/* End of instance object */
|
|
json_add(buf, JT_END_OBJECT, &json_level);
|
|
|
|
first_instance = false;
|
|
}
|
|
|
|
/*
|
|
* show information about WAL archive of the instance
|
|
*/
|
|
static void
|
|
show_instance_archive(InstanceConfig *instance)
|
|
{
|
|
parray *timelineinfos = nullptr;
|
|
|
|
if (!instance) {
|
|
return;
|
|
}
|
|
|
|
timelineinfos = catalog_get_timelines(instance);
|
|
|
|
if (show_format == SHOW_PLAIN)
|
|
show_archive_plain(instance->name, instance->xlog_seg_size, timelineinfos, true);
|
|
else if (show_format == SHOW_JSON)
|
|
show_archive_json(instance->name, instance->xlog_seg_size, timelineinfos);
|
|
else
|
|
elog(ERROR, "Invalid show format %d", (int) show_format);
|
|
|
|
parray_walk(timelineinfos, timelineInfoFree);
|
|
parray_free(timelineinfos);
|
|
}
|
|
|
|
static void
|
|
show_archive_plain(const char *instance_name, uint32 xlog_seg_size,
|
|
parray *tli_list, bool show_name)
|
|
{
|
|
char segno_tmp[MAXFNAMELEN];
|
|
parray *actual_tli_list = parray_new();
|
|
#define SHOW_ARCHIVE_FIELDS_COUNT 10
|
|
int i;
|
|
const char *names[SHOW_ARCHIVE_FIELDS_COUNT] =
|
|
{ "TLI", "Parent TLI", "Switchpoint",
|
|
"Min Segno", "Max Segno", "N segments", "Size", "Zratio", "N backups", "Status"};
|
|
const char *field_formats[SHOW_ARCHIVE_FIELDS_COUNT] =
|
|
{ " %-*s ", " %-*s ", " %-*s ", " %-*s ",
|
|
" %-*s ", " %-*s ", " %-*s ", " %-*s ", " %-*s ", " %-*s "};
|
|
uint32 widths[SHOW_ARCHIVE_FIELDS_COUNT];
|
|
uint32 widths_sum = 0;
|
|
ShowArchiveRow *rows;
|
|
|
|
for (i = 0; i < SHOW_ARCHIVE_FIELDS_COUNT; i++)
|
|
widths[i] = strlen(names[i]);
|
|
|
|
/* Ignore empty timelines */
|
|
for (i = 0; (size_t)i < parray_num(tli_list); i++)
|
|
{
|
|
timelineInfo *tlinfo = (timelineInfo *) parray_get(tli_list, i);
|
|
|
|
if (tlinfo->n_xlog_files > 0)
|
|
parray_append(actual_tli_list, tlinfo);
|
|
}
|
|
|
|
rows = (ShowArchiveRow *) gs_palloc0(parray_num(actual_tli_list) *
|
|
sizeof(ShowArchiveRow));
|
|
|
|
/*
|
|
* Fill row values and calculate maximum width of each field.
|
|
*/
|
|
for (i = 0; (size_t)i < parray_num(actual_tli_list); i++)
|
|
{
|
|
timelineInfo *tlinfo = (timelineInfo *) parray_get(actual_tli_list, i);
|
|
ShowArchiveRow *row = &rows[i];
|
|
int cur = 0;
|
|
float zratio = 0;
|
|
|
|
/* TLI */
|
|
errno_t rc = snprintf_s(row->tli, lengthof(row->tli), lengthof(row->tli) - 1, "%u",
|
|
tlinfo->tli);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->tli));
|
|
cur++;
|
|
|
|
/* Parent TLI */
|
|
rc = snprintf_s(row->parent_tli, lengthof(row->parent_tli), lengthof(row->parent_tli) - 1,
|
|
"%u",
|
|
tlinfo->parent_tli);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->parent_tli));
|
|
cur++;
|
|
|
|
/* Switchpoint LSN */
|
|
rc = snprintf_s(row->switchpoint, lengthof(row->switchpoint),
|
|
lengthof(row->switchpoint) - 1,
|
|
"%X/%X",
|
|
(uint32) (tlinfo->switchpoint >> 32),
|
|
(uint32) tlinfo->switchpoint);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->switchpoint));
|
|
cur++;
|
|
|
|
/* Min Segno */
|
|
GetXLogFileName(segno_tmp, MAXFNAMELEN, tlinfo->tli, tlinfo->begin_segno, xlog_seg_size);
|
|
rc = snprintf_s(row->min_segno, lengthof(row->min_segno), lengthof(row->min_segno) - 1, "%s",
|
|
segno_tmp);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
|
|
widths[cur] = Max(widths[cur], strlen(row->min_segno));
|
|
cur++;
|
|
|
|
/* Max Segno */
|
|
GetXLogFileName(segno_tmp, MAXFNAMELEN, tlinfo->tli, tlinfo->end_segno, xlog_seg_size);
|
|
rc = snprintf_s(row->max_segno, lengthof(row->max_segno), lengthof(row->max_segno) - 1, "%s",
|
|
segno_tmp);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
|
|
widths[cur] = Max(widths[cur], strlen(row->max_segno));
|
|
cur++;
|
|
|
|
/* N files */
|
|
rc = snprintf_s(row->n_segments, lengthof(row->n_segments), lengthof(row->n_segments) - 1, "%lu",
|
|
tlinfo->n_xlog_files);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->n_segments));
|
|
cur++;
|
|
|
|
/* Size */
|
|
pretty_size(tlinfo->size, row->size,
|
|
lengthof(row->size));
|
|
widths[cur] = Max(widths[cur], strlen(row->size));
|
|
cur++;
|
|
|
|
/* Zratio (compression ratio) */
|
|
if (tlinfo->size != 0)
|
|
zratio = ((float)xlog_seg_size*tlinfo->n_xlog_files) / tlinfo->size;
|
|
|
|
rc = snprintf_s(row->zratio, lengthof(row->zratio), lengthof(row->zratio) - 1, "%.2f", zratio);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->zratio));
|
|
cur++;
|
|
|
|
/* N backups */
|
|
rc = snprintf_s(row->n_backups, lengthof(row->n_backups), lengthof(row->n_backups) - 1, "%lu",
|
|
tlinfo->backups?parray_num(tlinfo->backups):0);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
widths[cur] = Max(widths[cur], strlen(row->n_backups));
|
|
cur++;
|
|
|
|
/* Status */
|
|
if (tlinfo->lost_segments == NULL)
|
|
row->status = "OK";
|
|
else
|
|
row->status = "DEGRADED";
|
|
widths[cur] = Max(widths[cur], strlen(row->status));
|
|
cur++;
|
|
}
|
|
|
|
for (i = 0; i < SHOW_ARCHIVE_FIELDS_COUNT; i++)
|
|
widths_sum += widths[i] + 2 /* two space */;
|
|
|
|
if (show_name)
|
|
appendPQExpBuffer(&show_buf, "\nARCHIVE INSTANCE '%s'\n", instance_name);
|
|
|
|
/*
|
|
* Print header.
|
|
*/
|
|
for (i = 0; (uint32)i < widths_sum; i++)
|
|
appendPQExpBufferChar(&show_buf, '=');
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
|
|
for (i = 0; i < SHOW_ARCHIVE_FIELDS_COUNT; i++)
|
|
{
|
|
appendPQExpBuffer(&show_buf, field_formats[i], widths[i], names[i]);
|
|
}
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
|
|
for (i = 0; (uint32)i < widths_sum; i++)
|
|
appendPQExpBufferChar(&show_buf, '=');
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
|
|
/*
|
|
* Print values.
|
|
*/
|
|
for (i = parray_num(actual_tli_list) - 1; i >= 0; i--)
|
|
{
|
|
ShowArchiveRow *row = &rows[i];
|
|
int cur = 0;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->tli);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->parent_tli);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->switchpoint);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->min_segno);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->max_segno);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->n_segments);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->size);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->zratio);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->n_backups);
|
|
cur++;
|
|
|
|
appendPQExpBuffer(&show_buf, field_formats[cur], widths[cur],
|
|
row->status);
|
|
cur++;
|
|
appendPQExpBufferChar(&show_buf, '\n');
|
|
}
|
|
|
|
pfree(rows);
|
|
//TODO: free timelines
|
|
parray_free(actual_tli_list);
|
|
}
|
|
|
|
static void
|
|
show_archive_json(const char *instance_name, uint32 xlog_seg_size,
|
|
parray *tli_list)
|
|
{
|
|
int i,j;
|
|
PQExpBuffer buf = &show_buf;
|
|
parray *actual_tli_list = parray_new();
|
|
char segno_tmp[MAXFNAMELEN];
|
|
|
|
if (!first_instance)
|
|
appendPQExpBufferChar(buf, ',');
|
|
|
|
/* Begin of instance object */
|
|
json_add(buf, JT_BEGIN_OBJECT, &json_level);
|
|
|
|
json_add_value(buf, "instance", instance_name, json_level, true);
|
|
json_add_key(buf, "timelines", json_level);
|
|
|
|
/* Ignore empty timelines */
|
|
|
|
for (i = 0; (size_t)i < parray_num(tli_list); i++)
|
|
{
|
|
timelineInfo *tlinfo = (timelineInfo *) parray_get(tli_list, i);
|
|
|
|
if (tlinfo->n_xlog_files > 0)
|
|
parray_append(actual_tli_list, tlinfo);
|
|
}
|
|
|
|
/*
|
|
* List timelines.
|
|
*/
|
|
json_add(buf, JT_BEGIN_ARRAY, &json_level);
|
|
|
|
for (i = parray_num(actual_tli_list) - 1; i >= 0; i--)
|
|
{
|
|
timelineInfo *tlinfo = (timelineInfo *) parray_get(actual_tli_list, i);
|
|
char tmp_buf[MAXFNAMELEN];
|
|
float zratio = 0;
|
|
|
|
if ((size_t)i != (parray_num(actual_tli_list) - 1))
|
|
appendPQExpBufferChar(buf, ',');
|
|
|
|
json_add(buf, JT_BEGIN_OBJECT, &json_level);
|
|
|
|
json_add_key(buf, "tli", json_level);
|
|
appendPQExpBuffer(buf, "%u", tlinfo->tli);
|
|
|
|
json_add_key(buf, "parent-tli", json_level);
|
|
appendPQExpBuffer(buf, "%u", tlinfo->parent_tli);
|
|
|
|
errno_t rc = snprintf_s(tmp_buf, lengthof(tmp_buf), lengthof(tmp_buf) - 1, "%X/%X",
|
|
(uint32) (tlinfo->switchpoint >> 32), (uint32) tlinfo->switchpoint);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
json_add_value(buf, "switchpoint", tmp_buf, json_level, true);
|
|
|
|
GetXLogFileName(segno_tmp, MAXFNAMELEN, tlinfo->tli, tlinfo->begin_segno, xlog_seg_size);
|
|
rc = snprintf_s(tmp_buf, lengthof(tmp_buf), lengthof(tmp_buf) - 1, "%s", segno_tmp);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
json_add_value(buf, "min-segno", tmp_buf, json_level, true);
|
|
|
|
GetXLogFileName(segno_tmp, MAXFNAMELEN, tlinfo->tli, tlinfo->end_segno, xlog_seg_size);
|
|
rc = snprintf_s(tmp_buf, lengthof(tmp_buf), lengthof(tmp_buf) - 1, "%s", segno_tmp);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
json_add_value(buf, "max-segno", tmp_buf, json_level, true);
|
|
|
|
json_add_key(buf, "n-segments", json_level);
|
|
appendPQExpBuffer(buf, "%lu", tlinfo->n_xlog_files);
|
|
|
|
json_add_key(buf, "size", json_level);
|
|
appendPQExpBuffer(buf, "%lu", tlinfo->size);
|
|
|
|
json_add_key(buf, "zratio", json_level);
|
|
if (tlinfo->size != 0)
|
|
zratio = ((float)xlog_seg_size*tlinfo->n_xlog_files) / tlinfo->size;
|
|
appendPQExpBuffer(buf, "%.2f", zratio);
|
|
|
|
if (tlinfo->closest_backup != NULL) {
|
|
rc = snprintf_s(tmp_buf, lengthof(tmp_buf), lengthof(tmp_buf) - 1, "%s",
|
|
base36enc(tlinfo->closest_backup->start_time));
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
else {
|
|
rc = snprintf_s(tmp_buf, lengthof(tmp_buf), lengthof(tmp_buf) - 1, "%s", "");
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
}
|
|
|
|
json_add_value(buf, "closest-backup-id", tmp_buf, json_level, true);
|
|
|
|
if (tlinfo->lost_segments == NULL)
|
|
json_add_value(buf, "status", "OK", json_level, true);
|
|
else
|
|
json_add_value(buf, "status", "DEGRADED", json_level, true);
|
|
|
|
json_add_key(buf, "lost-segments", json_level);
|
|
|
|
if (tlinfo->lost_segments != NULL)
|
|
{
|
|
json_add(buf, JT_BEGIN_ARRAY, &json_level);
|
|
|
|
for (j = 0; (size_t)j < parray_num(tlinfo->lost_segments); j++)
|
|
{
|
|
xlogInterval *lost_segments = (xlogInterval *) parray_get(tlinfo->lost_segments, j);
|
|
|
|
if (j != 0)
|
|
appendPQExpBufferChar(buf, ',');
|
|
|
|
json_add(buf, JT_BEGIN_OBJECT, &json_level);
|
|
|
|
GetXLogFileName(segno_tmp, MAXFNAMELEN, tlinfo->tli, lost_segments->begin_segno, xlog_seg_size);
|
|
rc = snprintf_s(tmp_buf, lengthof(tmp_buf), lengthof(tmp_buf) - 1, "%s", segno_tmp);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
json_add_value(buf, "begin-segno", tmp_buf, json_level, true);
|
|
|
|
GetXLogFileName(segno_tmp, MAXFNAMELEN, tlinfo->tli, lost_segments->end_segno, xlog_seg_size);
|
|
rc = snprintf_s(tmp_buf, lengthof(tmp_buf), lengthof(tmp_buf) - 1, "%s", segno_tmp);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
json_add_value(buf, "end-segno", tmp_buf, json_level, true);
|
|
json_add(buf, JT_END_OBJECT, &json_level);
|
|
}
|
|
json_add(buf, JT_END_ARRAY, &json_level);
|
|
}
|
|
else
|
|
appendPQExpBuffer(buf, "[]");
|
|
|
|
json_add_key(buf, "backups", json_level);
|
|
|
|
if (tlinfo->backups != NULL)
|
|
{
|
|
json_add(buf, JT_BEGIN_ARRAY, &json_level);
|
|
for (j = 0; (size_t)j < parray_num(tlinfo->backups); j++)
|
|
{
|
|
pgBackup *backup = (pgBackup *)parray_get(tlinfo->backups, j);
|
|
|
|
if (j != 0)
|
|
appendPQExpBufferChar(buf, ',');
|
|
|
|
print_backup_json_object(buf, backup);
|
|
}
|
|
|
|
json_add(buf, JT_END_ARRAY, &json_level);
|
|
}
|
|
else
|
|
appendPQExpBuffer(buf, "[]");
|
|
|
|
/* End of timeline */
|
|
json_add(buf, JT_END_OBJECT, &json_level);
|
|
}
|
|
|
|
/* End of timelines object */
|
|
json_add(buf, JT_END_ARRAY, &json_level);
|
|
|
|
/* End of instance object */
|
|
json_add(buf, JT_END_OBJECT, &json_level);
|
|
|
|
first_instance = false;
|
|
}
|