-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 08/24/2015 08:52 AM, Joe Conway wrote: > On 08/24/2015 06:50 AM, Tom Lane wrote: >> Andrew Dunstan <and...@dunslane.net> writes: >>> On 08/23/2015 08:58 PM, Michael Paquier wrote: >>>> I think that's a good thing to have, now I have concerns >>>> about making this data readable for non-superusers. Cloud >>>> deployments of Postgres are logically going to block the >>>> access of this view. > >>> I don't think it exposes any information of great security >>> value. > >> We just had that kerfuffle about whether WAL compression posed a >> security risk; doesn't that imply that at least the data >> relevant to WAL position has to be unreadable by non-superusers? > > So pg_config might be fully unrestricted, but pg_controldata might > need certain rows filtered based on superuser status? Do you think > those rows should be present but redacted, or completely filtered > out?
Here is the next installment on this. It addresses most of the previous comments, but does not yet address the issue raised here by Tom . Changes: 1.) pg_controldata() function and pg_controldata view added 2.) cleanup_path() moved to port/path.c 3.) extra PG_FUNCTION_INFO_V1() noise removed Issues needing comment: a.) Which items need hiding from non-superusers and should the value be redacted or the entire result set row be suppressed? b.) There is a difference in the formatting of timestamps between the pg_controldata binary and the builtin function. To see it do: diff -c <(pg_controldata) \ <(psql -qAt -c "select rpad(name || ':', 38) || setting from pg_controldata") What I see is: pg_controldata ! pg_control last modified: Tue 25 Aug 2015 08:10:42 PM PDT pg_controldata() ! pg_control last modified: Tue Aug 25 20:10:42 2015 Does it matter? c.) There is some common code between pg_controldata.c in bin and pg_controldata.c in backend/utils/misc. Specifically the string definitions in printf statements match those in ControlData[], and dbState() and wal_level_str() are in both places. Any opinions on consolidating them in src/common somewhere? d.) Still no docs yet - will get to it eventually. Thanks, Joe - -- Crunchy Data - http://crunchydata.com PostgreSQL Support for Secure Enterprises Consulting, Training, & Open Source Development -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAEBAgAGBQJV3TNnAAoJEDfy90M199hl0OsQAIyTr0hqxwjrGenDpnS4qE8u UJWVeCpqehFIobxcJ0TICTaQX835fzPGJIiTUwI1Dmz9sgjipvSG1wBmD4bRT93X WO4e/+Yr5onZ9vNVXlUswPK2kuzehImhmzMSnJ6KDXKkfw2MOZmz56wBb3yIB3lq K44FDukZ01w9RCGM8H5B/MPNMHIqfBB4wdlKARJZhqeUyPvTJ71iMiqeE77v3AIH JLmW6kRw8c3NVu/Wa+GVz4FGjIZKR5oazlFYfDTeHXrxV8NIDUFNrKikAW1ScdVK qSPVjFxoUlbX4W2dd1L1ciGeq83DktYbdKtpZZScQGXwhuq7Y1fHZQwzlxlraB/c UiqNdxmi7IeUdOIncsKPDmjs7C5yeNj1CRnWHTAQRW98RM42A3TvT2Qlkxm0CVLQ lZjFVOOMIf4pXYQv6PfiicO6QWYTUSXCa891s/10H2xkS/sMK1yHz3DWSZxVdDdI dbh5gie/GFro1nwWd8gjkn5KCe917GDBAUBn+QE5TgUPnRhserq6FQBSyVXfZtOQ o6nRM8vuv9Y06CRoeIgagtDWxippl0OAw442wHyme/PBQZ2842PW8GNNqw+B1HWz Ir0V5FiZdLLQipwiKT152+8OsOa/NU6wxGFuJr8It/4471h3jU5dxuHO+wQqMDEb xCn6ebwZaa9oSjHFrfy3 =oMOO -----END PGP SIGNATURE-----
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index ccc030f..7b5db32 100644 *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** CREATE VIEW pg_timezone_abbrevs AS *** 425,430 **** --- 425,436 ---- CREATE VIEW pg_timezone_names AS SELECT * FROM pg_timezone_names(); + CREATE VIEW pg_config AS + SELECT * FROM pg_config(); + + CREATE VIEW pg_controldata AS + SELECT * FROM pg_controldata(); + -- Statistics views CREATE VIEW pg_stat_all_tables AS diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index 7889101..13b60f7 100644 *** a/src/backend/utils/misc/Makefile --- b/src/backend/utils/misc/Makefile *************** include $(top_builddir)/src/Makefile.glo *** 14,21 **** override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) ! OBJS = guc.o help_config.o pg_rusage.o ps_status.o rls.o \ ! sampling.o superuser.o timeout.o tzparser.o # This location might depend on the installation directories. Therefore # we can't subsitute it into pg_config.h. --- 14,34 ---- override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) ! # don't include subdirectory-path-dependent -I and -L switches ! STD_CPPFLAGS := $(filter-out -I$(top_srcdir)/src/include -I$(top_builddir)/src/include,$(CPPFLAGS)) ! STD_LDFLAGS := $(filter-out -L$(top_builddir)/src/port,$(LDFLAGS)) ! override CPPFLAGS += -DVAL_CONFIGURE="\"$(configure_args)\"" ! override CPPFLAGS += -DVAL_CC="\"$(CC)\"" ! override CPPFLAGS += -DVAL_CPPFLAGS="\"$(STD_CPPFLAGS)\"" ! override CPPFLAGS += -DVAL_CFLAGS="\"$(CFLAGS)\"" ! override CPPFLAGS += -DVAL_CFLAGS_SL="\"$(CFLAGS_SL)\"" ! override CPPFLAGS += -DVAL_LDFLAGS="\"$(STD_LDFLAGS)\"" ! override CPPFLAGS += -DVAL_LDFLAGS_EX="\"$(LDFLAGS_EX)\"" ! override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\"" ! override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\"" ! ! OBJS = guc.o help_config.o pg_config.o pg_controldata.o pg_rusage.o \ ! ps_status.o rls.o sampling.o superuser.o timeout.o tzparser.o # This location might depend on the installation directories. Therefore # we can't subsitute it into pg_config.h. diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c index ...475a9c5 . *** a/src/backend/utils/misc/pg_config.c --- b/src/backend/utils/misc/pg_config.c *************** *** 0 **** --- 1,280 ---- + /*------------------------------------------------------------------------- + * + * pg_config.c + * Expose same output as pg_config except as an SRF + * + * Copyright (c) 2015, PostgreSQL Global Development Group + * ALL RIGHTS RESERVED; + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without a written agreement + * is hereby granted, provided that the above copyright notice and this + * paragraph and the following two paragraphs appear in all copies. + * + * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS + * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + + #include "postgres.h" + + #include "funcapi.h" + #include "miscadmin.h" + #include "catalog/pg_type.h" + #include "utils/builtins.h" + #include "utils/elog.h" + #include "port.h" + + static void get_configdata(void); + + #ifdef PGDLLIMPORT + /* Postgres global */ + extern PGDLLIMPORT char my_exec_path[]; + #else + /* Postgres global */ + extern DLLIMPORT char my_exec_path[]; + #endif /* PGDLLIMPORT */ + + struct configdata + { + char *name; + char *setting; + }; + + static struct configdata ConfigData[] = + { + {"BINDIR", NULL}, + {"DOCDIR", NULL}, + {"HTMLDIR", NULL}, + {"INCLUDEDIR", NULL}, + {"PKGINCLUDEDIR", NULL}, + {"INCLUDEDIR-SERVER", NULL}, + {"LIBDIR", NULL}, + {"PKGLIBDIR", NULL}, + {"LOCALEDIR", NULL}, + {"MANDIR", NULL}, + {"SHAREDIR", NULL}, + {"SYSCONFDIR", NULL}, + {"PGXS", NULL}, + {"CONFIGURE", NULL}, + {"CC", NULL}, + {"CPPFLAGS", NULL}, + {"CFLAGS", NULL}, + {"CFLAGS_SL", NULL}, + {"LDFLAGS", NULL}, + {"LDFLAGS_EX", NULL}, + {"LDFLAGS_SL", NULL}, + {"LIBS", NULL}, + {"VERSION", NULL}, + {NULL, NULL} + }; + + static void get_configdata(void); + + Datum + pg_config(PG_FUNCTION_ARGS) + { + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + Tuplestorestate *tupstore; + HeapTuple tuple; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + char *values[2]; + int i = 0; + + /* check to see if caller supports us returning a tuplestore */ + if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("materialize mode required, but it is not " + "allowed in this context"))); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + /* get the requested return tuple description */ + tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); + + /* + * Check to make sure we have a reasonable tuple descriptor + */ + if (tupdesc->natts != 2 || + tupdesc->attrs[0]->atttypid != TEXTOID || + tupdesc->attrs[1]->atttypid != TEXTOID) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("query-specified return tuple and " + "function return type are not compatible"))); + + /* OK to use it */ + attinmeta = TupleDescGetAttInMetadata(tupdesc); + + /* let the caller know we're sending back a tuplestore */ + rsinfo->returnMode = SFRM_Materialize; + + /* initialize our tuplestore */ + tupstore = tuplestore_begin_heap(true, false, work_mem); + + get_configdata(); + while (ConfigData[i].name) + { + values[0] = ConfigData[i].name; + values[1] = ConfigData[i].setting; + + tuple = BuildTupleFromCStrings(attinmeta, values); + tuplestore_puttuple(tupstore, tuple); + ++i; + } + + /* + * no longer need the tuple descriptor reference created by + * TupleDescGetAttInMetadata() + */ + ReleaseTupleDesc(tupdesc); + + tuplestore_donestoring(tupstore); + rsinfo->setResult = tupstore; + + /* + * SFRM_Materialize mode expects us to return a NULL Datum. The actual + * tuples are in our tuplestore and passed back through + * rsinfo->setResult. rsinfo->setDesc is set to the tuple description + * that we actually used to build our tuples with, so the caller can + * verify we did what it was expecting. + */ + rsinfo->setDesc = tupdesc; + MemoryContextSwitchTo(oldcontext); + + return (Datum) 0; + } + + + static void + get_configdata(void) + { + char path[MAXPGPATH]; + char *lastsep; + + strcpy(path, my_exec_path); + lastsep = strrchr(path, '/'); + if (lastsep) + *lastsep = '\0'; + cleanup_path(path); + ConfigData[0].setting = pstrdup(path); + + get_doc_path(my_exec_path, path); + cleanup_path(path); + ConfigData[1].setting = pstrdup(path); + + get_html_path(my_exec_path, path); + cleanup_path(path); + ConfigData[2].setting = pstrdup(path); + + get_include_path(my_exec_path, path); + cleanup_path(path); + ConfigData[3].setting = pstrdup(path); + + get_pkginclude_path(my_exec_path, path); + cleanup_path(path); + ConfigData[4].setting = pstrdup(path); + + get_includeserver_path(my_exec_path, path); + cleanup_path(path); + ConfigData[5].setting = pstrdup(path); + + get_lib_path(my_exec_path, path); + cleanup_path(path); + ConfigData[6].setting = pstrdup(path); + + get_pkglib_path(my_exec_path, path); + cleanup_path(path); + ConfigData[7].setting = pstrdup(path); + + get_locale_path(my_exec_path, path); + cleanup_path(path); + ConfigData[8].setting = pstrdup(path); + + get_man_path(my_exec_path, path); + cleanup_path(path); + ConfigData[9].setting = pstrdup(path); + + get_share_path(my_exec_path, path); + cleanup_path(path); + ConfigData[10].setting = pstrdup(path); + + get_etc_path(my_exec_path, path); + cleanup_path(path); + ConfigData[11].setting = pstrdup(path); + + get_pkglib_path(my_exec_path, path); + strlcat(path, "/pgxs/src/makefiles/pgxs.mk", sizeof(path)); + cleanup_path(path); + ConfigData[12].setting = pstrdup(path); + + #ifdef VAL_CONFIGURE + ConfigData[13].setting = pstrdup(VAL_CONFIGURE); + #else + ConfigData[13].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_CC + ConfigData[14].setting = pstrdup(VAL_CC); + #else + ConfigData[14].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_CPPFLAGS + ConfigData[15].setting = pstrdup(VAL_CPPFLAGS); + #else + ConfigData[15].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_CFLAGS + ConfigData[16].setting = pstrdup(VAL_CFLAGS); + #else + ConfigData[16].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_CFLAGS_SL + ConfigData[17].setting = pstrdup(VAL_CFLAGS_SL); + #else + ConfigData[17].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_LDFLAGS + ConfigData[18].setting = pstrdup(VAL_LDFLAGS); + #else + ConfigData[18].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_LDFLAGS_EX + ConfigData[19].setting = pstrdup(VAL_LDFLAGS_EX); + #else + ConfigData[19].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_LDFLAGS_SL + ConfigData[20].setting = pstrdup(VAL_LDFLAGS_SL); + #else + ConfigData[20].setting = pstrdup(_("not recorded")); + #endif + + #ifdef VAL_LIBS + ConfigData[21].setting = pstrdup(VAL_LIBS); + #else + ConfigData[21].setting = pstrdup(_("not recorded")); + #endif + + ConfigData[22].setting = pstrdup("PostgreSQL " PG_VERSION); + } diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c index ...e8aa367 . *** a/src/backend/utils/misc/pg_controldata.c --- b/src/backend/utils/misc/pg_controldata.c *************** *** 0 **** --- 1,515 ---- + /*------------------------------------------------------------------------- + * + * pg_controldata.c + * Expose same output as pg_controldata except as an SRF + * + * Copyright (c) 2015, PostgreSQL Global Development Group + * ALL RIGHTS RESERVED; + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without a written agreement + * is hereby granted, provided that the above copyright notice and this + * paragraph and the following two paragraphs appear in all copies. + * + * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS + * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + + #include "postgres.h" + + #include <unistd.h> + #include <time.h> + #include <sys/stat.h> + #include <fcntl.h> + + #include "funcapi.h" + #include "miscadmin.h" + #include "access/xlog.h" + #include "access/xlog_internal.h" + #include "catalog/pg_control.h" + #include "catalog/pg_type.h" + #include "port/pg_crc32c.h" + #include "utils/builtins.h" + + struct controldata + { + char *name; + char *setting; + }; + + static struct controldata ControlData[] = + { + {gettext_noop("pg_control version number"), NULL}, + {gettext_noop("Catalog version number"), NULL}, + {gettext_noop("Database system identifier"), NULL}, + {gettext_noop("Database cluster state"), NULL}, + {gettext_noop("pg_control last modified"), NULL}, + {gettext_noop("Latest checkpoint location"), NULL}, + {gettext_noop("Prior checkpoint location"), NULL}, + {gettext_noop("Latest checkpoint's REDO location"), NULL}, + {gettext_noop("Latest checkpoint's REDO WAL file"), NULL}, + {gettext_noop("Latest checkpoint's TimeLineID"), NULL}, + {gettext_noop("Latest checkpoint's PrevTimeLineID"), NULL}, + {gettext_noop("Latest checkpoint's full_page_writes"), NULL}, + {gettext_noop("Latest checkpoint's NextXID"), NULL}, + {gettext_noop("Latest checkpoint's NextOID"), NULL}, + {gettext_noop("Latest checkpoint's NextMultiXactId"), NULL}, + {gettext_noop("Latest checkpoint's NextMultiOffset"), NULL}, + {gettext_noop("Latest checkpoint's oldestXID"), NULL}, + {gettext_noop("Latest checkpoint's oldestXID's DB"), NULL}, + {gettext_noop("Latest checkpoint's oldestActiveXID"), NULL}, + {gettext_noop("Latest checkpoint's oldestMultiXid"), NULL}, + {gettext_noop("Latest checkpoint's oldestMulti's DB"), NULL}, + {gettext_noop("Latest checkpoint's oldestCommitTs"), NULL}, + {gettext_noop("Time of latest checkpoint"), NULL}, + {gettext_noop("Fake LSN counter for unlogged rels"), NULL}, + {gettext_noop("Minimum recovery ending location"), NULL}, + {gettext_noop("Min recovery ending loc's timeline"), NULL}, + {gettext_noop("Backup start location"), NULL}, + {gettext_noop("Backup end location"), NULL}, + {gettext_noop("End-of-backup record required"), NULL}, + {gettext_noop("wal_level setting"), NULL}, + {gettext_noop("wal_log_hints setting"), NULL}, + {gettext_noop("max_connections setting"), NULL}, + {gettext_noop("max_worker_processes setting"), NULL}, + {gettext_noop("max_prepared_xacts setting"), NULL}, + {gettext_noop("max_locks_per_xact setting"), NULL}, + {gettext_noop("track_commit_timestamp setting"), NULL}, + {gettext_noop("Maximum data alignment"), NULL}, + {gettext_noop("Database block size"), NULL}, + {gettext_noop("Blocks per segment of large relation"), NULL}, + {gettext_noop("WAL block size"), NULL}, + {gettext_noop("Bytes per WAL segment"), NULL}, + {gettext_noop("Maximum length of identifiers"), NULL}, + {gettext_noop("Maximum columns in an index"), NULL}, + {gettext_noop("Maximum size of a TOAST chunk"), NULL}, + {gettext_noop("Size of a large-object chunk"), NULL}, + {gettext_noop("Date/time type storage"), NULL}, + {gettext_noop("Float4 argument passing"), NULL}, + {gettext_noop("Float8 argument passing"), NULL}, + {gettext_noop("Data page checksum version"), NULL}, + {NULL, NULL} + }; + + + static const char *dbState(DBState state); + static void get_controldata(void); + + Datum + pg_controldata(PG_FUNCTION_ARGS) + { + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + Tuplestorestate *tupstore; + HeapTuple tuple; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + char *values[2]; + int i = 0; + + /* check to see if caller supports us returning a tuplestore */ + if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("materialize mode required, but it is not " + "allowed in this context"))); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + /* get the requested return tuple description */ + tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); + + /* + * Check to make sure we have a reasonable tuple descriptor + */ + if (tupdesc->natts != 2 || + tupdesc->attrs[0]->atttypid != TEXTOID || + tupdesc->attrs[1]->atttypid != TEXTOID) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("query-specified return tuple and " + "function return type are not compatible"))); + + /* OK to use it */ + attinmeta = TupleDescGetAttInMetadata(tupdesc); + + /* let the caller know we're sending back a tuplestore */ + rsinfo->returnMode = SFRM_Materialize; + + /* initialize our tuplestore */ + tupstore = tuplestore_begin_heap(true, false, work_mem); + + get_controldata(); + while (ControlData[i].name) + { + values[0] = _(ControlData[i].name); + values[1] = ControlData[i].setting; + + tuple = BuildTupleFromCStrings(attinmeta, values); + tuplestore_puttuple(tupstore, tuple); + ++i; + } + + /* + * no longer need the tuple descriptor reference created by + * TupleDescGetAttInMetadata() + */ + ReleaseTupleDesc(tupdesc); + + tuplestore_donestoring(tupstore); + rsinfo->setResult = tupstore; + + /* + * SFRM_Materialize mode expects us to return a NULL Datum. The actual + * tuples are in our tuplestore and passed back through + * rsinfo->setResult. rsinfo->setDesc is set to the tuple description + * that we actually used to build our tuples with, so the caller can + * verify we did what it was expecting. + */ + rsinfo->setDesc = tupdesc; + MemoryContextSwitchTo(oldcontext); + + return (Datum) 0; + } + + 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_ARCHIVE: + return "archive"; + case WAL_LEVEL_HOT_STANDBY: + return "hot_standby"; + case WAL_LEVEL_LOGICAL: + return "logical"; + } + return _("unrecognized wal_level"); + } + + static void + get_controldata(void) + { + ControlFileData ControlFile; + int fd; + char ControlFilePath[MAXPGPATH]; + pg_crc32c crc; + time_t time_tmp; + char pgctime_str[128]; + char ckpttime_str[128]; + char sysident_str[32]; + const char *strftime_fmt = "%c"; + XLogSegNo segno; + char xlogfilename[MAXFNAMELEN]; + StringInfoData buf; + + snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir); + + if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1) + elog(ERROR, "could not open file \"%s\" for reading: %s", + ControlFilePath, strerror(errno)); + + if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData)) + elog(ERROR, "could not read file \"%s\": %s", + ControlFilePath, strerror(errno)); + + close(fd); + + /* Check the CRC. */ + INIT_CRC32C(crc); + COMP_CRC32C(crc, + (char *) &ControlFile, + offsetof(ControlFileData, crc)); + FIN_CRC32C(crc); + + if (!EQ_CRC32C(crc, ControlFile.crc)) + elog(ERROR, "calculated CRC checksum does not match value stored in file"); + + /* + * 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. + * + * 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. + */ + XLByteToSeg(ControlFile.checkPointCopy.redo, segno); + XLogFileName(xlogfilename, ControlFile.checkPointCopy.ThisTimeLineID, segno); + + /* + * Format system_identifier separately to keep platform-dependent format + * code out of the translatable message string. + */ + snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, + ControlFile.system_identifier); + + initStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.pg_control_version); + ControlData[0].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.catalog_version_no); + ControlData[1].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", sysident_str); + ControlData[2].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", dbState(ControlFile.state)); + ControlData[3].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", pgctime_str); + ControlData[4].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%X/%X", + (uint32) (ControlFile.checkPoint >> 32), + (uint32) ControlFile.checkPoint); + ControlData[5].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%X/%X", + (uint32) (ControlFile.prevCheckPoint >> 32), + (uint32) ControlFile.prevCheckPoint); + ControlData[6].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%X/%X", + (uint32) (ControlFile.checkPointCopy.redo >> 32), + (uint32) ControlFile.checkPointCopy.redo); + ControlData[7].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", xlogfilename); + ControlData[8].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.ThisTimeLineID); + ControlData[9].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.PrevTimeLineID); + ControlData[10].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", + ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off")); + ControlData[11].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u/%u", + ControlFile.checkPointCopy.nextXidEpoch, + ControlFile.checkPointCopy.nextXid); + ControlData[12].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.nextOid); + ControlData[13].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.nextMulti); + ControlData[14].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.nextMultiOffset); + ControlData[15].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestXid); + ControlData[16].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestXidDB); + ControlData[17].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestActiveXid); + ControlData[18].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestMulti); + ControlData[19].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestMultiDB); + ControlData[20].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestCommitTs); + ControlData[21].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", ckpttime_str); + ControlData[22].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%X/%X", + (uint32) (ControlFile.unloggedLSN >> 32), + (uint32) ControlFile.unloggedLSN); + ControlData[23].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%X/%X", + (uint32) (ControlFile.minRecoveryPoint >> 32), + (uint32) ControlFile.minRecoveryPoint); + ControlData[24].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.minRecoveryPointTLI); + ControlData[25].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%X/%X", + (uint32) (ControlFile.backupStartPoint >> 32), + (uint32) ControlFile.backupStartPoint); + ControlData[26].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%X/%X", + (uint32) (ControlFile.backupEndPoint >> 32), + (uint32) ControlFile.backupEndPoint); + ControlData[27].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", + ControlFile.backupEndRequired ? _("yes") : _("no")); + ControlData[28].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", wal_level_str(ControlFile.wal_level)); + ControlData[29].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", + ControlFile.wal_log_hints ? _("on") : _("off")); + ControlData[30].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%d", ControlFile.MaxConnections); + ControlData[31].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%d", ControlFile.max_worker_processes); + ControlData[32].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%d", ControlFile.max_prepared_xacts); + ControlData[33].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%d", ControlFile.max_locks_per_xact); + ControlData[34].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", + ControlFile.track_commit_timestamp ? _("on") : _("off")); + ControlData[35].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.maxAlign); + ControlData[36].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.blcksz); + ControlData[37].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.relseg_size); + ControlData[38].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.xlog_blcksz); + ControlData[39].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.xlog_seg_size); + ControlData[40].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.nameDataLen); + ControlData[41].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.indexMaxKeys); + ControlData[42].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.toast_max_chunk_size); + ControlData[43].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.loblksize); + ControlData[44].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", + (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers"))); + ControlData[45].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", + (ControlFile.float4ByVal ? _("by value") : _("by reference"))); + ControlData[46].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%s", + (ControlFile.float8ByVal ? _("by value") : _("by reference"))); + ControlData[47].setting = pstrdup(buf.data); + resetStringInfo(&buf); + + appendStringInfo(&buf, "%u", ControlFile.data_checksum_version); + ControlData[48].setting = pstrdup(buf.data); + resetStringInfo(&buf); + } diff --git a/src/bin/pg_config/pg_config.c b/src/bin/pg_config/pg_config.c index 9b1f95d..25efc71 100644 *** a/src/bin/pg_config/pg_config.c --- b/src/bin/pg_config/pg_config.c *************** *** 29,65 **** static const char *progname; static char mypath[MAXPGPATH]; - - /* - * This function cleans up the paths for use with either cmd.exe or Msys - * on Windows. We need them to use filenames without spaces, for which a - * short filename is the safest equivalent, eg: - * C:/Progra~1/ - */ - static void - cleanup_path(char *path) - { - #ifdef WIN32 - char *ptr; - - /* - * GetShortPathName() will fail if the path does not exist, or short names - * are disabled on this file system. In both cases, we just return the - * original path. This is particularly useful for --sysconfdir, which - * might not exist. - */ - GetShortPathName(path, path, MAXPGPATH - 1); - - /* Replace '\' with '/' */ - for (ptr = path; *ptr; ptr++) - { - if (*ptr == '\\') - *ptr = '/'; - } - #endif - } - - /* * For each piece of information known to pg_config, we define a subroutine * to print it. This is probably overkill, but it avoids code duplication --- 29,34 ---- diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ddf7c67..e0ba271 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DESCR("get an individual replication ori *** 5331,5336 **** --- 5331,5344 ---- DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ )); DESCR("get progress for all replication origins"); + /* pg_config */ + DATA(insert OID = 3300 ( pg_config PGNSP PGUID 12 1 1000 0 0 f f f f t t i 0 0 2249 "" "{25,25}" "{o,o}" "{name,setting}" _null_ _null_ pg_config _null_ _null_ _null_ )); + DESCR("pg_config binary as a function"); + + /* pg_controldata */ + DATA(insert OID = 3308 ( pg_controldata PGNSP PGUID 12 1 1000 0 0 f f f f t t i 0 0 2249 "" "{25,25}" "{o,o}" "{name,setting}" _null_ _null_ pg_controldata _null_ _null_ _null_ )); + DESCR("pg_controldata binary as a function"); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, diff --git a/src/include/port.h b/src/include/port.h index 3787cbf..ab38f62 100644 *** a/src/include/port.h --- b/src/include/port.h *************** extern void join_path_components(char *r *** 42,47 **** --- 42,48 ---- const char *head, const char *tail); extern void canonicalize_path(char *path); extern void make_native_path(char *path); + extern void cleanup_path(char *path); extern bool path_contains_parent_reference(const char *path); extern bool path_is_relative_and_below_cwd(const char *path); extern bool path_is_prefix_of_path(const char *path1, const char *path2); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index fc1679e..4a67a12 100644 *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** extern Datum set_config_by_name(PG_FUNCT *** 1121,1126 **** --- 1121,1132 ---- extern Datum show_all_settings(PG_FUNCTION_ARGS); extern Datum show_all_file_settings(PG_FUNCTION_ARGS); + /* pg_config.c */ + extern Datum pg_config(PG_FUNCTION_ARGS); + + /* pg_controldata.c */ + extern Datum pg_controldata(PG_FUNCTION_ARGS); + /* rls.c */ extern Datum row_security_active(PG_FUNCTION_ARGS); extern Datum row_security_active_name(PG_FUNCTION_ARGS); diff --git a/src/port/path.c b/src/port/path.c index d0f72df..7613666 100644 *** a/src/port/path.c --- b/src/port/path.c *************** make_native_path(char *filename) *** 172,177 **** --- 172,207 ---- /* + * This function cleans up the paths for use with either cmd.exe or Msys + * on Windows. We need them to use filenames without spaces, for which a + * short filename is the safest equivalent, eg: + * C:/Progra~1/ + */ + void + cleanup_path(char *path) + { + #ifdef WIN32 + char *ptr; + + /* + * GetShortPathName() will fail if the path does not exist, or short names + * are disabled on this file system. In both cases, we just return the + * original path. This is particularly useful for --sysconfdir, which + * might not exist. + */ + GetShortPathName(path, path, MAXPGPATH - 1); + + /* Replace '\' with '/' */ + for (ptr = path; *ptr; ptr++) + { + if (*ptr == '\\') + *ptr = '/'; + } + #endif + } + + + /* * join_path_components - join two path components, inserting a slash * * We omit the slash if either given component is empty. diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 44c6740..85b4082 100644 *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** pg_available_extensions| SELECT e.name, *** 1305,1310 **** --- 1305,1316 ---- e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); + pg_config| SELECT pg_config.name, + pg_config.setting + FROM pg_config() pg_config(name, setting); + pg_controldata| SELECT pg_controldata.name, + pg_controldata.setting + FROM pg_controldata() pg_controldata(name, setting); pg_cursors| SELECT c.name, c.statement, c.is_holdable,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers