| /* |
| * pg_controldata |
| * |
| * reads the data from $PGDATA/global/pg_control |
| * |
| * copyright (c) Oliver Elphick <olly@lfix.co.uk>, 2001; |
| * license: BSD |
| * |
| * src/bin/pg_controldata/pg_controldata.c |
| */ |
| |
| /* |
| * We have to use postgres.h not postgres_fe.h here, because there's so much |
| * backend-only stuff in the XLOG include files we need. But we need a |
| * frontend-ish environment otherwise. Hence this ugly hack. |
| */ |
| #define FRONTEND 1 |
| |
| #include "postgres.h" |
| |
| #include <time.h> |
| |
| #include "access/transam.h" |
| #include "access/xlog.h" |
| #include "access/xlog_internal.h" |
| #include "catalog/pg_control.h" |
| #include "common/controldata_utils.h" |
| #include "common/kmgr_utils.h" |
| #include "common/logging.h" |
| #include "getopt_long.h" |
| #include "pg_getopt.h" |
| |
| static void |
| usage(const char *progname) |
| { |
| printf(_("%s displays control information of a PostgreSQL database cluster.\n\n"), progname); |
| printf(_("Usage:\n")); |
| printf(_(" %s [OPTION] [DATADIR]\n"), progname); |
| printf(_("\nOptions:\n")); |
| printf(_(" [-D, --pgdata=]DATADIR data directory\n")); |
| printf(_(" -V, --version output version information, then exit\n")); |
| printf(_(" -?, --help show this help, then exit\n")); |
| printf(_(" --gp-version output Cloudberry version information, then exit\n")); |
| printf(_("\nIf no data directory (DATADIR) is specified, " |
| "the environment variable PGDATA\nis used.\n\n")); |
| printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT); |
| printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); |
| } |
| |
| |
| static const char * |
| dbState(DBState state) |
| { |
| switch (state) |
| { |
| case DB_STARTUP: |
| return _("starting up"); |
| case DB_SHUTDOWNED: |
| return _("shut down"); |
| case DB_SHUTDOWNED_IN_RECOVERY: |
| return _("shut down in recovery"); |
| case DB_SHUTDOWNING: |
| return _("shutting down"); |
| case DB_IN_CRASH_RECOVERY: |
| return _("in crash recovery"); |
| case DB_IN_ARCHIVE_RECOVERY: |
| return _("in archive recovery"); |
| case DB_IN_PRODUCTION: |
| return _("in production"); |
| } |
| return _("unrecognized status code"); |
| } |
| |
| static const char * |
| wal_level_str(WalLevel wal_level) |
| { |
| switch (wal_level) |
| { |
| case WAL_LEVEL_MINIMAL: |
| return "minimal"; |
| case WAL_LEVEL_REPLICA: |
| return "replica"; |
| case WAL_LEVEL_LOGICAL: |
| return "logical"; |
| } |
| return _("unrecognized wal_level"); |
| } |
| |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| static struct option long_options[] = { |
| {"pgdata", required_argument, NULL, 'D'}, |
| {NULL, 0, NULL, 0} |
| }; |
| |
| ControlFileData *ControlFile; |
| bool crc_ok; |
| char *DataDir = NULL; |
| time_t time_tmp; |
| char pgctime_str[128]; |
| char ckpttime_str[128]; |
| char mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1]; |
| const char *strftime_fmt = "%c"; |
| const char *progname; |
| char xlogfilename[MAXFNAMELEN]; |
| int c; |
| int i; |
| int WalSegSz; |
| |
| pg_logging_init(argv[0]); |
| set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata")); |
| progname = get_progname(argv[0]); |
| |
| if (argc > 1) |
| { |
| if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) |
| { |
| usage(progname); |
| exit(0); |
| } |
| if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) |
| { |
| puts("pg_controldata (Apache Cloudberry) " PG_VERSION); |
| exit(0); |
| } |
| if (strcmp(argv[1], "--gp-version") == 0) |
| { |
| puts("pg_controldata (Apache Cloudberry) " GP_VERSION); |
| exit(0); |
| } |
| |
| } |
| |
| while ((c = getopt_long(argc, argv, "D:", long_options, NULL)) != -1) |
| { |
| switch (c) |
| { |
| case 'D': |
| DataDir = optarg; |
| break; |
| |
| default: |
| fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); |
| exit(1); |
| } |
| } |
| |
| if (DataDir == NULL) |
| { |
| if (optind < argc) |
| DataDir = argv[optind++]; |
| else |
| DataDir = getenv("PGDATA"); |
| } |
| |
| /* Complain if any arguments remain */ |
| if (optind < argc) |
| { |
| pg_log_error("too many command-line arguments (first is \"%s\")", |
| argv[optind]); |
| fprintf(stderr, _("Try \"%s --help\" for more information.\n"), |
| progname); |
| exit(1); |
| } |
| |
| if (DataDir == NULL) |
| { |
| pg_log_error("no data directory specified"); |
| fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); |
| exit(1); |
| } |
| |
| /* get a copy of the control file */ |
| ControlFile = get_controlfile(DataDir, &crc_ok); |
| if (!crc_ok) |
| printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n" |
| "Either the file is corrupt, or it has a different layout than this program\n" |
| "is expecting. The results below are untrustworthy.\n\n")); |
| |
| /* set wal segment size */ |
| WalSegSz = ControlFile->xlog_seg_size; |
| |
| if (!IsValidWalSegSize(WalSegSz)) |
| { |
| printf(_("WARNING: invalid WAL segment size\n")); |
| printf(ngettext("The WAL segment size stored in the file, %d byte, is not a power of two\n" |
| "between 1 MB and 1 GB. The file is corrupt and the results below are\n" |
| "untrustworthy.\n\n", |
| "The WAL segment size stored in the file, %d bytes, is not a power of two\n" |
| "between 1 MB and 1 GB. The file is corrupt and the results below are\n" |
| "untrustworthy.\n\n", |
| WalSegSz), |
| WalSegSz); |
| } |
| |
| /* |
| * This slightly-chintzy coding will work as long as the control file |
| * timestamps are within the range of time_t; that should be the case in |
| * all foreseeable circumstances, so we don't bother importing the |
| * backend's timezone library into pg_controldata. |
| * |
| * Use variable for format to suppress overly-anal-retentive gcc warning |
| * about %c |
| */ |
| time_tmp = (time_t) ControlFile->time; |
| strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt, |
| localtime(&time_tmp)); |
| time_tmp = (time_t) ControlFile->checkPointCopy.time; |
| strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt, |
| localtime(&time_tmp)); |
| |
| /* |
| * Calculate name of the WAL file containing the latest checkpoint's REDO |
| * start point. |
| * |
| * A corrupted control file could report a WAL segment size of 0, and to |
| * guard against division by zero, we need to treat that specially. |
| */ |
| if (WalSegSz != 0) |
| { |
| XLogSegNo segno; |
| |
| XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz); |
| XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, |
| segno, WalSegSz); |
| } |
| else |
| strcpy(xlogfilename, _("???")); |
| |
| for (i = 0; i < MOCK_AUTH_NONCE_LEN; i++) |
| snprintf(&mock_auth_nonce_str[i * 2], 3, "%02x", |
| (unsigned char) ControlFile->mock_authentication_nonce[i]); |
| |
| printf(_("pg_control version number: %u\n"), |
| ControlFile->pg_control_version); |
| printf(_("Catalog version number: %u\n"), |
| ControlFile->catalog_version_no); |
| printf(_("Database system identifier: %llu\n"), |
| (unsigned long long) ControlFile->system_identifier); |
| printf(_("Database cluster state: %s\n"), |
| dbState(ControlFile->state)); |
| printf(_("pg_control last modified: %s\n"), |
| pgctime_str); |
| printf(_("Latest checkpoint location: %X/%X\n"), |
| LSN_FORMAT_ARGS(ControlFile->checkPoint)); |
| printf(_("Latest checkpoint's REDO location: %X/%X\n"), |
| LSN_FORMAT_ARGS(ControlFile->checkPointCopy.redo)); |
| printf(_("Latest checkpoint's REDO WAL file: %s\n"), |
| xlogfilename); |
| printf(_("Latest checkpoint's TimeLineID: %u\n"), |
| ControlFile->checkPointCopy.ThisTimeLineID); |
| printf(_("Latest checkpoint's PrevTimeLineID: %u\n"), |
| ControlFile->checkPointCopy.PrevTimeLineID); |
| printf(_("Latest checkpoint's full_page_writes: %s\n"), |
| ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off")); |
| printf(_("Latest checkpoint's NextXID: %u:%u\n"), |
| EpochFromFullTransactionId(ControlFile->checkPointCopy.nextXid), |
| XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid)); |
| printf(_("Latest checkpoint's NextGxid: "UINT64_FORMAT"\n"), |
| ControlFile->checkPointCopy.nextGxid); |
| printf(_("Latest checkpoint's NextOID: %u\n"), |
| ControlFile->checkPointCopy.nextOid); |
| printf(_("Latest checkpoint's NextRelfilenode: %u\n"), |
| ControlFile->checkPointCopy.nextRelfilenode); |
| printf(_("Latest checkpoint's NextMultiXactId: %u\n"), |
| ControlFile->checkPointCopy.nextMulti); |
| printf(_("Latest checkpoint's NextMultiOffset: %u\n"), |
| ControlFile->checkPointCopy.nextMultiOffset); |
| printf(_("Latest checkpoint's oldestXID: %u\n"), |
| ControlFile->checkPointCopy.oldestXid); |
| printf(_("Latest checkpoint's oldestXID's DB: %u\n"), |
| ControlFile->checkPointCopy.oldestXidDB); |
| printf(_("Latest checkpoint's oldestActiveXID: %u\n"), |
| ControlFile->checkPointCopy.oldestActiveXid); |
| printf(_("Latest checkpoint's oldestMultiXid: %u\n"), |
| ControlFile->checkPointCopy.oldestMulti); |
| printf(_("Latest checkpoint's oldestMulti's DB: %u\n"), |
| ControlFile->checkPointCopy.oldestMultiDB); |
| printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"), |
| ControlFile->checkPointCopy.oldestCommitTsXid); |
| printf(_("Latest checkpoint's newestCommitTsXid:%u\n"), |
| ControlFile->checkPointCopy.newestCommitTsXid); |
| printf(_("Time of latest checkpoint: %s\n"), |
| ckpttime_str); |
| printf(_("Fake LSN counter for unlogged rels: %X/%X\n"), |
| LSN_FORMAT_ARGS(ControlFile->unloggedLSN)); |
| printf(_("Minimum recovery ending location: %X/%X\n"), |
| LSN_FORMAT_ARGS(ControlFile->minRecoveryPoint)); |
| printf(_("Min recovery ending loc's timeline: %u\n"), |
| ControlFile->minRecoveryPointTLI); |
| printf(_("Backup start location: %X/%X\n"), |
| LSN_FORMAT_ARGS(ControlFile->backupStartPoint)); |
| printf(_("Backup end location: %X/%X\n"), |
| LSN_FORMAT_ARGS(ControlFile->backupEndPoint)); |
| printf(_("End-of-backup record required: %s\n"), |
| ControlFile->backupEndRequired ? _("yes") : _("no")); |
| printf(_("wal_level setting: %s\n"), |
| wal_level_str(ControlFile->wal_level)); |
| printf(_("wal_log_hints setting: %s\n"), |
| ControlFile->wal_log_hints ? _("on") : _("off")); |
| printf(_("max_connections setting: %d\n"), |
| ControlFile->MaxConnections); |
| printf(_("max_worker_processes setting: %d\n"), |
| ControlFile->max_worker_processes); |
| printf(_("max_wal_senders setting: %d\n"), |
| ControlFile->max_wal_senders); |
| printf(_("max_prepared_xacts setting: %d\n"), |
| ControlFile->max_prepared_xacts); |
| printf(_("max_locks_per_xact setting: %d\n"), |
| ControlFile->max_locks_per_xact); |
| printf(_("track_commit_timestamp setting: %s\n"), |
| ControlFile->track_commit_timestamp ? _("on") : _("off")); |
| printf(_("Maximum data alignment: %u\n"), |
| ControlFile->maxAlign); |
| /* we don't print floatFormat since can't say much useful about it */ |
| printf(_("Database block size: %u\n"), |
| ControlFile->blcksz); |
| printf(_("Blocks per segment of large relation: %u\n"), |
| ControlFile->relseg_size); |
| printf(_("WAL block size: %u\n"), |
| ControlFile->xlog_blcksz); |
| printf(_("Bytes per WAL segment: %u\n"), |
| ControlFile->xlog_seg_size); |
| printf(_("Maximum length of identifiers: %u\n"), |
| ControlFile->nameDataLen); |
| printf(_("Maximum columns in an index: %u\n"), |
| ControlFile->indexMaxKeys); |
| printf(_("Maximum size of a TOAST chunk: %u\n"), |
| ControlFile->toast_max_chunk_size); |
| printf(_("Size of a large-object chunk: %u\n"), |
| ControlFile->loblksize); |
| /* This is no longer configurable, but users may still expect to see it: */ |
| printf(_("Date/time type storage: %s\n"), |
| _("64-bit integers")); |
| printf(_("Float8 argument passing: %s\n"), |
| (ControlFile->float8ByVal ? _("by value") : _("by reference"))); |
| printf(_("Data page checksum version: %u\n"), |
| ControlFile->data_checksum_version); |
| printf(_("Mock authentication nonce: %s\n"), |
| mock_auth_nonce_str); |
| printf(_("File encryption method: %s\n"), |
| encryption_methods[ControlFile->file_encryption_method].name); |
| return 0; |
| } |