From: Mark Dilger <mark.dilger@enterprisedb.com>
To: Thomas Munro <thomas.munro@gmail.com>
Cc: Robert Haas <robertmhaas@gmail.com>
Cc: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Peter Geoghegan <pg@bowt.ie>
Cc: Andrey M. Borodin <x4mmm@yandex-team.ru>
Cc: Stephen Frost <sfrost@snowman.net>
Cc: Michael Paquier <michael@paquier.xyz>
Cc: Amul Sul <sulamul@gmail.com>
Cc: Dilip Kumar <dilipbalaut@gmail.com>
Cc: Andres Freund <andres@anarazel.de>
Cc: PostgreSQL-development <pgsql-hackers@postgresql.org>
Subject: Re: new heapcheck contrib module
Date: Mon, 11 Jan 2021 10:16:07 -0800
Message-ID: <A4C2DFB8-436F-4802-BE8D-7A30290D52FA@enterprisedb.com> (raw)
In-Reply-To: <CA+hUKG+mX5sb6=ZG9=zHaYofPeLOxO910XHkahcDgP-XzEMLxQ@mail.gmail.com>
References: <12ED3DA8-25F0-4B68-937D-D907CFBF08E7@enterprisedb.com>
<20200420194210.sevnpi4lyghoanxi@alap3.anarazel.de>
<23E60C3C-C20A-4508-84AC-E0CEA59E784A@enterprisedb.com>
<CA+TgmobAm8vB1MNQ1taO1et+HxTN8ULnQvEG+grNDqNQTBjbtA@mail.gmail.com>
<F845AC50-A479-43F3-ADFB-861A4D2AD287@enterprisedb.com>
<5F743835-3399-419C-8324-2D424237E999@enterprisedb.com>
<CAFiTN-syyHc3jZoou51v0SR8z0POoNfktqEO6MaGig4YS8mosA@mail.gmail.com>
<E6F4258A-B661-4F2C-8A35-278C9436930A@enterprisedb.com>
<CAFiTN-s-VX3DLfqv7OOo0zGCTOZ3xVU9UZ6GgzBZdJTPNnYFNg@mail.gmail.com>
<885D2EC9-7D06-4903-A17D-72C184A1E1C5@enterprisedb.com>
<CAFiTN-uYg_PiZ854=5R-D9skratapAHKhaDAG7fhJv-Vn5UhaQ@mail.gmail.com>
<5CC85F28-F210-47AC-8DA8-EC4E82AF3FD5@enterprisedb.com>
<CAFiTN-sP5iFJGERTq4a4OLTCzjZKhLC5P6_k2bxQKXnuvdmzGA@mail.gmail.com>
<CAFiTN-tTg0R5AHvXC1ro9XphroX1wUtc5t91hU_+7DeGTpE5VQ@mail.gmail.com>
<0C0349EB-DD85-4DA8-82C7-DF70AB714592@enterprisedb.com>
<BA6E870F-241C-4DDB-B92D-DB39E679C7D4@enterprisedb.com>
<C4A3E5AE-CBB2-4E3F-8BAE-29EE216762C2@enterprisedb.com>
<CAAJ_b96_Xr2a+D0j8xKbOnCzURcVQm0V2DS+T_pfc+wt3BpL8g@mail.gmail.com>
<9DF93B83-FE46-4EC4-A99D-77A60729FD2D@enterprisedb.com>
<CA+TgmoZT3kCOaKq6=m5wHGKh_9XQDbCC1LZXL6w_+NMB-n1+mw@mail.gmail.com>
<CAAJ_b94hsrF4x7UKQj03416fAEXJorUpZ9WhsZZjqgv-4+Wg9A@mail.gmail.com>
<50F6468F-5A5E-412A-86EC-DF24B549D733@enterprisedb.com>
<CAAJ_b94+A1Gk5r5sBYbYM2K5hH_gyp_o=+gwJuJhn5ZEm5_rqw@mail.gmail.com>
<CAAJ_b94Wh_7kM9JuJk7wnNsYSNPdSzz35cOEaE_ZRnPcqvupCg@mail.gmail.com>
<7D603B53-8078-4AF0-851B-803FF4416501@enterprisedb.com>
<CA+TgmoZbPtTgkQBGoco39uqKUWuv=0+TOkiNcCuqWWvUkJVuxQ@mail.gmail.com>
<A4C9E0AE-46BA-457F-A445-4527E5AA5E3C@enterprisedb.com>
<98265AA9-7BDC-4708-9348-CC1B173BBE1D@enterprisedb.com>
<CA+Tgmoam+pnGKhWS+RAYCk1eqjhbAPX7Vm69g1VF56xwyxXw+g@mail.gmail.com>
<209783.1603313248@sss.pgh.pa.us>
<77A60E99-6054-40FD-A5D6-769C287D4A0F@enterprisedb.com>
<CA+TgmobGxdm8Tsxawt7LW-qigHRHxD+b17PC+ft9EE+6pMhC9w@mail.gmail.com>
<721BFA83-EA6F-463D-A9DF-CE78A318B73A@enterprisedb.com>
<2A7DA1A8-C4AA-43DF-A985-3CA52F4DC775@enterprisedb.com>
<146814.1603463293@sss.pgh.pa.us>
<221029.1603476265@sss.pgh.pa.us>
<CA+TgmoZsuhZroXdwCNUb5+9QEWSFQMH9=aA1hx-SDeArppH76Q@mail.gmail.com>
<C001DE91-CF4A-44A7-85B6-8BBAB1881196@enterprisedb.com>
<CA+TgmoZJ53=joSyJuR7WmXEOfhbBK+0hQBrOO4mWftwekaHS6w@mail.gmail.com>
<153EEBAD-2673-4367-B36D-4A6AE8EE7A9F@enterprisedb.com>
<85BE72E5-A926-4F23-9FD5-64239C0C6BA5@enterprisedb.com>
<CA+Tgmoa7sNcw15=soG1pLBgByMnn-w70YdfKZmhmYOb43KzzRw@mail.gmail.com>
<B5CF5BFF-0A92-4792-868E-3DB4E8F1C389@enterprisedb.com>
<10677BFE-3B45-4EF3-82E5-E45CA1445CA6@enterprisedb.com>
<CA+hUKG+mX5sb6=ZG9=zHaYofPeLOxO910XHkahcDgP-XzEMLxQ@mail.gmail.com>
> On Jan 10, 2021, at 12:41 PM, Thomas Munro <thomas.munro@gmail.com> wrote:
>
> On Fri, Jan 8, 2021 at 6:33 AM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
>> The attached patches, v31, are mostly the same, but with "getopt_long.h" included from pg_amcheck.c per Thomas's review, and a .gitignore file added in contrib/pg_amcheck/
>
> I couple more little things from Windows CI:
>
> C:\projects\postgresql\src\include\fe_utils/option_utils.h(19):
> fatal error C1083: Cannot open include file: 'libpq-fe.h': No such
> file or directory [C:\projects\postgresql\pg_amcheck.vcxproj]
>
> Does contrib/amcheck/Makefile need to say "SHLIB_PREREQS =
> submake-libpq" like other contrib modules that use libpq?
Added in v32.
> pg_backup_utils.obj : error LNK2001: unresolved external symbol
> exit_nicely [C:\projects\postgresql\pg_dump.vcxproj]
>
> I think this is probably because additions to src/fe_utils/Makefile's
> OBJS list need to be manually replicated in
> src/tools/msvc/Mkvcbuild.pm's @pgfeutilsfiles list. (If I'm right
> about that, perhaps it needs a comment to remind us Unix hackers of
> that, or perhaps it should be automated...)
Added in v32, along with adding pg_amcheck to @contrib_uselibpq, @contrib_uselibpgport, and @contrib_uselibpgcommon
There are also a few additions in v32 to typedefs.list, and some whitespace changes due to running pgindent.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:download all (9 files, .tar.gz)v32-0001-Moving-exit_nicely-and-fatal-into-fe_utils.patch (application/octet-stream, 8.7K) download
From b4a3769891e3669c2b6e5c4b0db757f70fff6d84 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 30 Dec 2020 12:50:41 -0800
Subject: [PATCH v32 1/9] Moving exit_nicely and fatal into fe_utils
In preparation for moving other pg_dump functionality into fe_utils,
moving the functions "on_exit_nicely" and "exit_nicely", and the
macro "fatal" from pg_dump into fe_utils.
Various frontend executables in src/bin, src/bin/scripts, and
contrib/ have logic for logging and exiting under error conditions.
The logging code itself is already under common/, but executables
differ in their calls to exit() vs. exit_nicely(), with
exit_nicely() not uniformly defined, and sometimes all of this
wrapped up under a macro named fatal(), the definition of that macro
also not uniformly defined. This makes it harder to move code out
of these executables into a shared library under fe_utils/.
Standardizing all executables to define these things the same way or
to use a single fe_utils/ library is beyond the scope of this patch,
but this patch should get the ball rolling in that direction.
---
src/bin/pg_dump/pg_backup_archiver.h | 1 +
src/bin/pg_dump/pg_backup_utils.c | 59 -----------------------
src/bin/pg_dump/pg_backup_utils.h | 8 ----
src/fe_utils/Makefile | 1 +
src/fe_utils/exit_utils.c | 71 ++++++++++++++++++++++++++++
src/include/fe_utils/exit_utils.h | 25 ++++++++++
6 files changed, 98 insertions(+), 67 deletions(-)
create mode 100644 src/fe_utils/exit_utils.c
create mode 100644 src/include/fe_utils/exit_utils.h
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.hindex a8ea5c7eae..37d157b7ad 100644--- a/src/bin/pg_dump/pg_backup_archiver.h+++ b/src/bin/pg_dump/pg_backup_archiver.h@@ -26,6 +26,7 @@
#include <time.h>
+#include "fe_utils/exit_utils.h"
#include "libpq-fe.h"
#include "pg_backup.h"
#include "pqexpbuffer.h"
diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.cindex c709a40e06..631e88f7db 100644--- a/src/bin/pg_dump/pg_backup_utils.c+++ b/src/bin/pg_dump/pg_backup_utils.c@@ -19,16 +19,6 @@
/* Globals exported by this file */
const char *progname = NULL;
-#define MAX_ON_EXIT_NICELY 20--static struct-{- on_exit_nicely_callback function;- void *arg;-} on_exit_nicely_list[MAX_ON_EXIT_NICELY];--static int on_exit_nicely_index;-
/*
* Parse a --section=foo command line argument.
*
@@ -57,52 +47,3 @@ set_dump_section(const char *arg, int *dumpSections)
exit_nicely(1);
}
}
---/* Register a callback to be run when exit_nicely is invoked. */-void-on_exit_nicely(on_exit_nicely_callback function, void *arg)-{- if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)- {- pg_log_fatal("out of on_exit_nicely slots");- exit_nicely(1);- }- on_exit_nicely_list[on_exit_nicely_index].function = function;- on_exit_nicely_list[on_exit_nicely_index].arg = arg;- on_exit_nicely_index++;-}--/*- * Run accumulated on_exit_nicely callbacks in reverse order and then exit- * without printing any message.- *- * If running in a parallel worker thread on Windows, we only exit the thread,- * not the whole process.- *- * Note that in parallel operation on Windows, the callback(s) will be run- * by each thread since the list state is necessarily shared by all threads;- * each callback must contain logic to ensure it does only what's appropriate- * for its thread. On Unix, callbacks are also run by each process, but only- * for callbacks established before we fork off the child processes. (It'd- * be cleaner to reset the list after fork(), and let each child establish- * its own callbacks; but then the behavior would be completely inconsistent- * between Windows and Unix. For now, just be sure to establish callbacks- * before forking to avoid inconsistency.)- */-void-exit_nicely(int code)-{- int i;-- for (i = on_exit_nicely_index - 1; i >= 0; i--)- on_exit_nicely_list[i].function(code,- on_exit_nicely_list[i].arg);--#ifdef WIN32- if (parallel_init_done && GetCurrentThreadId() != mainThreadId)- _endthreadex(code);-#endif-- exit(code);-}diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.hindex 306798f9ac..ee4409c274 100644--- a/src/bin/pg_dump/pg_backup_utils.h+++ b/src/bin/pg_dump/pg_backup_utils.h@@ -15,22 +15,14 @@
#ifndef PG_BACKUP_UTILS_H
#define PG_BACKUP_UTILS_H
-#include "common/logging.h"-
/* bits returned by set_dump_section */
#define DUMP_PRE_DATA 0x01
#define DUMP_DATA 0x02
#define DUMP_POST_DATA 0x04
#define DUMP_UNSECTIONED 0xff
-typedef void (*on_exit_nicely_callback) (int code, void *arg);-
extern const char *progname;
extern void set_dump_section(const char *arg, int *dumpSections);
-extern void on_exit_nicely(on_exit_nicely_callback function, void *arg);-extern void exit_nicely(int code) pg_attribute_noreturn();--#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0)
#endif /* PG_BACKUP_UTILS_H */
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefileindex 10d6838cf9..d6c328faf1 100644--- a/src/fe_utils/Makefile+++ b/src/fe_utils/Makefile@@ -23,6 +23,7 @@ OBJS = \
archive.o \
cancel.o \
conditional.o \
+ exit_utils.o \
mbprint.o \
print.o \
psqlscan.o \
diff --git a/src/fe_utils/exit_utils.c b/src/fe_utils/exit_utils.cnew file mode 100644index 0000000000..e61bd438fc--- /dev/null+++ b/src/fe_utils/exit_utils.c@@ -0,0 +1,71 @@+/*-------------------------------------------------------------------------+ *+ * Exiting with cleanup callback facilities for frontend code+ *+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/fe_utils/exit_utils.c+ *+ *-------------------------------------------------------------------------+ */++#include "fe_utils/exit_utils.h"++#define MAX_ON_EXIT_NICELY 20++static struct+{+ on_exit_nicely_callback function;+ void *arg;+} on_exit_nicely_list[MAX_ON_EXIT_NICELY];++static int on_exit_nicely_index;++/* Register a callback to be run when exit_nicely is invoked. */+void+on_exit_nicely(on_exit_nicely_callback function, void *arg)+{+ if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)+ {+ pg_log_fatal("out of on_exit_nicely slots");+ exit_nicely(1);+ }+ on_exit_nicely_list[on_exit_nicely_index].function = function;+ on_exit_nicely_list[on_exit_nicely_index].arg = arg;+ on_exit_nicely_index++;+}++/*+ * Run accumulated on_exit_nicely callbacks in reverse order and then exit+ * without printing any message.+ *+ * If running in a parallel worker thread on Windows, we only exit the thread,+ * not the whole process.+ *+ * Note that in parallel operation on Windows, the callback(s) will be run+ * by each thread since the list state is necessarily shared by all threads;+ * each callback must contain logic to ensure it does only what's appropriate+ * for its thread. On Unix, callbacks are also run by each process, but only+ * for callbacks established before we fork off the child processes. (It'd+ * be cleaner to reset the list after fork(), and let each child establish+ * its own callbacks; but then the behavior would be completely inconsistent+ * between Windows and Unix. For now, just be sure to establish callbacks+ * before forking to avoid inconsistency.)+ */+void+exit_nicely(int code)+{+ int i;++ for (i = on_exit_nicely_index - 1; i >= 0; i--)+ on_exit_nicely_list[i].function(code,+ on_exit_nicely_list[i].arg);++#ifdef WIN32+ if (parallel_init_done && GetCurrentThreadId() != mainThreadId)+ _endthreadex(code);+#endif++ exit(code);+}diff --git a/src/include/fe_utils/exit_utils.h b/src/include/fe_utils/exit_utils.hnew file mode 100644index 0000000000..948d2fdb51--- /dev/null+++ b/src/include/fe_utils/exit_utils.h@@ -0,0 +1,25 @@+/*-------------------------------------------------------------------------+ *+ * Exiting with cleanup callback facilities for frontend code+ *+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/fe_utils/exit_utils.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef EXIT_UTILS_H+#define EXIT_UTILS_H++#include "postgres_fe.h"+#include "common/logging.h"++typedef void (*on_exit_nicely_callback) (int code, void *arg);++extern void on_exit_nicely(on_exit_nicely_callback function, void *arg);+extern void exit_nicely(int code) pg_attribute_noreturn();++#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0)++#endif /* EXIT_UTILS_H */--
2.21.1 (Apple Git-122.3)
=
v32-0002-Refactoring-ExecuteSqlQuery-and-related-function.patch (application/octet-stream, 46.7K) download
From 19a006bf7841fa4405e42be790dd5fe6f24b14a8 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 4 Jan 2021 12:44:20 -0800
Subject: [PATCH v32 2/9] Refactoring ExecuteSqlQuery and related functions.
ExecuteSqlQuery, ExecuteSqlQueryForSingleRow, and
ExecuteSqlStatement in the pg_dump project were defined to take a
pointer to struct Archive, which is a struct unused outside pg_dump.
In preparation for moving these functions to fe_utils, refactoring
these functions to take a PGconn pointer. These functions also
embedded pg_dump assumptions about the correct error handling
behavior, specifically to do with logging error messages before
calling exit_nicely(). Refactoring the error handling logic into a
handler function. The full design of the handler is not yet
present, as it will be developed further after moving to fe_utils,
but the idea is that callers will ultimately be able to override the
error handling behavior by defining alternate handlers.
To minimize changes to pg_dump and friends, creating thin wrappers
around these functions that take an Archive pointer. It might be
marginally cleaner in the long run to refactor pg_dump.c to call
with a PGconn pointer in all relevant call sites, but that would
result in a nontrivially larger patch and more code churn, so not
doing that here. Another option might be to define the thin
wrappers as static inline functions, but that seems inconsistent
with the rest of the pg_dump project style, so not doing that
either. Should we?
---
src/bin/pg_dump/pg_backup_db.c | 144 ++++++++++++++-----
src/bin/pg_dump/pg_backup_db.h | 26 +++-
src/bin/pg_dump/pg_dump.c | 248 ++++++++++++++++-----------------
3 files changed, 253 insertions(+), 165 deletions(-)
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.cindex 5ba43441f5..b55a968da2 100644--- a/src/bin/pg_dump/pg_backup_db.c+++ b/src/bin/pg_dump/pg_backup_db.c@@ -61,7 +61,7 @@ _check_database_version(ArchiveHandle *AH)
*/
if (remoteversion >= 90000)
{
- res = ExecuteSqlQueryForSingleRow((Archive *) AH, "SELECT pg_catalog.pg_is_in_recovery()");+ res = ExecuteSqlQueryForSingleRowAH((Archive *) AH, "SELECT pg_catalog.pg_is_in_recovery()");
AH->public.isStandby = (strcmp(PQgetvalue(res, 0, 0), "t") == 0);
PQclear(res);
@@ -198,8 +198,8 @@ ConnectDatabase(Archive *AHX,
}
/* Start strict; later phases may override this. */
- PQclear(ExecuteSqlQueryForSingleRow((Archive *) AH,- ALWAYS_SECURE_SEARCH_PATH_SQL));+ PQclear(ExecuteSqlQueryForSingleRowAH((Archive *) AH,+ ALWAYS_SECURE_SEARCH_PATH_SQL));
if (password && password != AH->savedPassword)
free(password);
@@ -271,59 +271,129 @@ notice_processor(void *arg, const char *message)
pg_log_generic(PG_LOG_INFO, "%s", message);
}
-/* Like fatal(), but with a complaint about a particular query. */-static void-die_on_query_failure(ArchiveHandle *AH, const char *query)+/*+ * The exiting query result handler embeds the historical pg_dump behavior+ * under query error conditions, including exiting nicely. The 'conn' object+ * is unused here, but is included in the interface for alternate query result+ * handler implementations.+ *+ * Whether the query was successful is determined by comparing the returned+ * status code against the expected status code, and by comparing the number of+ * tuples returned from the query against expected_ntups. Special negative+ * values of expected_ntups can be used to require at least one row or to+ * disables ntup checking.+ *+ * Exits on failure. On successful query completion, returns the 'res'+ * argument as a notational convenience.+ */+PGresult *+exiting_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,+ int expected_ntups, const char *query)
{
- pg_log_error("query failed: %s",- PQerrorMessage(AH->connection));- fatal("query was: %s", query);+ if (PQresultStatus(res) != expected_status)+ {+ pg_log_error("query failed: %s", PQerrorMessage(conn));+ pg_log_error("query was: %s", query);+ PQfinish(conn);+ exit_nicely(1);+ }+ if (expected_ntups == POSITIVE_NTUPS || expected_ntups >= 0)+ {+ int ntups = PQntuples(res);++ if (expected_ntups == POSITIVE_NTUPS)+ {+ if (ntups == 0)+ fatal("query returned no rows: %s", query);+ }+ else if (ntups != expected_ntups)+ {+ /*+ * Preserve historical message behavior of spelling "one" as the+ * expected row count.+ */+ if (expected_ntups == 1)+ fatal(ngettext("query returned %d row instead of one: %s",+ "query returned %d rows instead of one: %s",+ ntups),+ ntups, query);+ fatal(ngettext("query returned %d row instead of %d: %s",+ "query returned %d rows instead of %d: %s",+ ntups),+ ntups, expected_ntups, query);+ }+ }+ return res;
}
+/*+ * Executes the given SQL query statement.+ *+ * Invokes the exiting handler for any but PGRES_COMMAND_OK status.+ */
void
-ExecuteSqlStatement(Archive *AHX, const char *query)+ExecuteSqlStatement(PGconn *conn, const char *query)
{
- ArchiveHandle *AH = (ArchiveHandle *) AHX;- PGresult *res;+ PQclear(exiting_handler(PQexec(conn, query),+ conn,+ PGRES_COMMAND_OK,+ ANY_NTUPS,+ query));+}- res = PQexec(AH->connection, query);- if (PQresultStatus(res) != PGRES_COMMAND_OK)- die_on_query_failure(AH, query);- PQclear(res);+/*+ * Executes the given SQL query.+ *+ * Invokes the exiting handler unless the given 'status' results.+ *+ * If successful, returns the query result.+ */+PGresult *+ExecuteSqlQuery(PGconn *conn, const char *query, ExecStatusType status)+{+ return exiting_handler(PQexec(conn, query),+ conn,+ status,+ ANY_NTUPS,+ query);
}
+/*+ * Like ExecuteSqlQuery, but requires PGRES_TUPLES_OK status and+ * requires that exactly one row be returned.+ */
PGresult *
-ExecuteSqlQuery(Archive *AHX, const char *query, ExecStatusType status)+ExecuteSqlQueryForSingleRow(PGconn *conn, const char *query)+{+ return exiting_handler(PQexec(conn, query),+ conn,+ PGRES_TUPLES_OK,+ 1,+ query);+}++void+ExecuteSqlStatementAH(Archive *AHX, const char *query)
{
ArchiveHandle *AH = (ArchiveHandle *) AHX;
- PGresult *res;- res = PQexec(AH->connection, query);- if (PQresultStatus(res) != status)- die_on_query_failure(AH, query);- return res;+ ExecuteSqlStatement(AH->connection, query);
}
-/*- * Execute an SQL query and verify that we got exactly one row back.- */
PGresult *
-ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)+ExecuteSqlQueryAH(Archive *AHX, const char *query, ExecStatusType status)
{
- PGresult *res;- int ntups;+ ArchiveHandle *AH = (ArchiveHandle *) AHX;- res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);+ return ExecuteSqlQuery(AH->connection, query, status);+}- /* Expecting a single result only */- ntups = PQntuples(res);- if (ntups != 1)- fatal(ngettext("query returned %d row instead of one: %s",- "query returned %d rows instead of one: %s",- ntups),- ntups, query);+PGresult *+ExecuteSqlQueryForSingleRowAH(Archive *AHX, const char *query)+{+ ArchiveHandle *AH = (ArchiveHandle *) AHX;- return res;+ return ExecuteSqlQueryForSingleRow(AH->connection, query);
}
/*
diff --git a/src/bin/pg_dump/pg_backup_db.h b/src/bin/pg_dump/pg_backup_db.hindex 8888dd34b9..1aac600ece 100644--- a/src/bin/pg_dump/pg_backup_db.h+++ b/src/bin/pg_dump/pg_backup_db.h@@ -13,10 +13,28 @@
extern int ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen);
-extern void ExecuteSqlStatement(Archive *AHX, const char *query);-extern PGresult *ExecuteSqlQuery(Archive *AHX, const char *query,- ExecStatusType status);-extern PGresult *ExecuteSqlQueryForSingleRow(Archive *fout, const char *query);+#define POSITIVE_NTUPS (-1)+#define ANY_NTUPS (-2)+typedef PGresult *(*PGresultHandler) (PGresult *res,+ PGconn *conn,+ ExecStatusType expected_status,+ int expected_ntups,+ const char *query);++extern PGresult *exiting_handler(PGresult *res, PGconn *conn,+ ExecStatusType expected_status,+ int expected_ntups, const char *query);++extern void ExecuteSqlStatement(PGconn *conn, const char *query);+extern PGresult *ExecuteSqlQuery(PGconn *conn, const char *query,+ ExecStatusType expected_status);+extern PGresult *ExecuteSqlQueryForSingleRow(PGconn *conn, const char *query);++extern void ExecuteSqlStatementAH(Archive *AHX, const char *query);+extern PGresult *ExecuteSqlQueryAH(Archive *AHX, const char *query,+ ExecStatusType status);+extern PGresult *ExecuteSqlQueryForSingleRowAH(Archive *fout,+ const char *query);
extern void EndDBCopyMode(Archive *AHX, const char *tocEntryTag);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.cindex 1f70653c02..e8985a834f 100644--- a/src/bin/pg_dump/pg_dump.c+++ b/src/bin/pg_dump/pg_dump.c@@ -1084,7 +1084,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
PGconn *conn = GetConnection(AH);
const char *std_strings;
- PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));+ PQclear(ExecuteSqlQueryForSingleRowAH(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
/*
* Set the client encoding if requested.
@@ -1119,7 +1119,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
PQExpBuffer query = createPQExpBuffer();
appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
- ExecuteSqlStatement(AH, query->data);+ ExecuteSqlStatementAH(AH, query->data);
destroyPQExpBuffer(query);
/* save it for possible later use by parallel workers */
@@ -1128,11 +1128,11 @@ setup_connection(Archive *AH, const char *dumpencoding,
}
/* Set the datestyle to ISO to ensure the dump's portability */
- ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");+ ExecuteSqlStatementAH(AH, "SET DATESTYLE = ISO");
/* Likewise, avoid using sql_standard intervalstyle */
if (AH->remoteVersion >= 80400)
- ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");+ ExecuteSqlStatementAH(AH, "SET INTERVALSTYLE = POSTGRES");
/*
* Use an explicitly specified extra_float_digits if it has been provided.
@@ -1145,35 +1145,35 @@ setup_connection(Archive *AH, const char *dumpencoding,
appendPQExpBuffer(q, "SET extra_float_digits TO %d",
extra_float_digits);
- ExecuteSqlStatement(AH, q->data);+ ExecuteSqlStatementAH(AH, q->data);
destroyPQExpBuffer(q);
}
else if (AH->remoteVersion >= 90000)
- ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");+ ExecuteSqlStatementAH(AH, "SET extra_float_digits TO 3");
else
- ExecuteSqlStatement(AH, "SET extra_float_digits TO 2");+ ExecuteSqlStatementAH(AH, "SET extra_float_digits TO 2");
/*
* If synchronized scanning is supported, disable it, to prevent
* unpredictable changes in row ordering across a dump and reload.
*/
if (AH->remoteVersion >= 80300)
- ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");+ ExecuteSqlStatementAH(AH, "SET synchronize_seqscans TO off");
/*
* Disable timeouts if supported.
*/
- ExecuteSqlStatement(AH, "SET statement_timeout = 0");+ ExecuteSqlStatementAH(AH, "SET statement_timeout = 0");
if (AH->remoteVersion >= 90300)
- ExecuteSqlStatement(AH, "SET lock_timeout = 0");+ ExecuteSqlStatementAH(AH, "SET lock_timeout = 0");
if (AH->remoteVersion >= 90600)
- ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");+ ExecuteSqlStatementAH(AH, "SET idle_in_transaction_session_timeout = 0");
/*
* Quote all identifiers, if requested.
*/
if (quote_all_identifiers && AH->remoteVersion >= 90100)
- ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");+ ExecuteSqlStatementAH(AH, "SET quote_all_identifiers = true");
/*
* Adjust row-security mode, if supported.
@@ -1181,15 +1181,15 @@ setup_connection(Archive *AH, const char *dumpencoding,
if (AH->remoteVersion >= 90500)
{
if (dopt->enable_row_security)
- ExecuteSqlStatement(AH, "SET row_security = on");+ ExecuteSqlStatementAH(AH, "SET row_security = on");
else
- ExecuteSqlStatement(AH, "SET row_security = off");+ ExecuteSqlStatementAH(AH, "SET row_security = off");
}
/*
* Start transaction-snapshot mode transaction to dump consistent data.
*/
- ExecuteSqlStatement(AH, "BEGIN");+ ExecuteSqlStatementAH(AH, "BEGIN");
if (AH->remoteVersion >= 90100)
{
/*
@@ -1201,17 +1201,17 @@ setup_connection(Archive *AH, const char *dumpencoding,
* guarantees. This is a kluge, but safe for back-patching.
*/
if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
- ExecuteSqlStatement(AH,+ ExecuteSqlStatementAH(AH,
"SET TRANSACTION ISOLATION LEVEL "
"SERIALIZABLE, READ ONLY, DEFERRABLE");
else
- ExecuteSqlStatement(AH,+ ExecuteSqlStatementAH(AH,
"SET TRANSACTION ISOLATION LEVEL "
"REPEATABLE READ, READ ONLY");
}
else
{
- ExecuteSqlStatement(AH,+ ExecuteSqlStatementAH(AH,
"SET TRANSACTION ISOLATION LEVEL "
"SERIALIZABLE, READ ONLY");
}
@@ -1230,7 +1230,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
- ExecuteSqlStatement(AH, query->data);+ ExecuteSqlStatementAH(AH, query->data);
destroyPQExpBuffer(query);
}
else if (AH->numWorkers > 1 &&
@@ -1270,7 +1270,7 @@ get_synchronized_snapshot(Archive *fout)
char *result;
PGresult *res;
- res = ExecuteSqlQueryForSingleRow(fout, query);+ res = ExecuteSqlQueryForSingleRowAH(fout, query);
result = pg_strdup(PQgetvalue(res, 0, 0));
PQclear(res);
@@ -1343,7 +1343,7 @@ expand_schema_name_patterns(Archive *fout,
processSQLNamePattern(GetConnection(fout), query, cell->val, false,
false, NULL, "n.nspname", NULL, NULL);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
if (strict_names && PQntuples(res) == 0)
fatal("no matching schemas were found for pattern \"%s\"", cell->val);
@@ -1390,7 +1390,7 @@ expand_foreign_server_name_patterns(Archive *fout,
processSQLNamePattern(GetConnection(fout), query, cell->val, false,
false, NULL, "s.srvname", NULL, NULL);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) == 0)
fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
@@ -1450,9 +1450,9 @@ expand_table_name_patterns(Archive *fout,
false, "n.nspname", "c.relname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
- ExecuteSqlStatement(fout, "RESET search_path");- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);- PQclear(ExecuteSqlQueryForSingleRow(fout,+ ExecuteSqlStatementAH(fout, "RESET search_path");+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);+ PQclear(ExecuteSqlQueryForSingleRowAH(fout,
ALWAYS_SECURE_SEARCH_PATH_SQL));
if (strict_names && PQntuples(res) == 0)
fatal("no matching tables were found for pattern \"%s\"", cell->val);
@@ -1907,7 +1907,7 @@ dumpTableData_copy(Archive *fout, void *dcontext)
fmtQualifiedDumpable(tbinfo),
column_list);
}
- res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);+ res = ExecuteSqlQueryAH(fout, q->data, PGRES_COPY_OUT);
PQclear(res);
destroyPQExpBuffer(clistBuf);
@@ -2028,11 +2028,11 @@ dumpTableData_insert(Archive *fout, void *dcontext)
if (tdinfo->filtercond)
appendPQExpBuffer(q, " %s", tdinfo->filtercond);
- ExecuteSqlStatement(fout, q->data);+ ExecuteSqlStatementAH(fout, q->data);
while (1)
{
- res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",+ res = ExecuteSqlQueryAH(fout, "FETCH 100 FROM _pg_dump_cursor",
PGRES_TUPLES_OK);
nfields = PQnfields(res);
@@ -2220,7 +2220,7 @@ dumpTableData_insert(Archive *fout, void *dcontext)
archputs("\n\n", fout);
- ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");+ ExecuteSqlStatementAH(fout, "CLOSE _pg_dump_cursor");
destroyPQExpBuffer(q);
if (insertStmt != NULL)
@@ -2520,7 +2520,7 @@ buildMatViewRefreshDependencies(Archive *fout)
"FROM w "
"WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -2847,7 +2847,7 @@ dumpDatabase(Archive *fout)
username_subquery);
}
- res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, dbQry->data);
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
@@ -2992,7 +2992,7 @@ dumpDatabase(Archive *fout)
seclabelQry = createPQExpBuffer();
buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
- shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);+ shres = ExecuteSqlQueryAH(fout, seclabelQry->data, PGRES_TUPLES_OK);
resetPQExpBuffer(seclabelQry);
emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
if (seclabelQry->len > 0)
@@ -3103,7 +3103,7 @@ dumpDatabase(Archive *fout)
"WHERE oid = %u;\n",
LargeObjectRelationId);
- lo_res = ExecuteSqlQueryForSingleRow(fout, loFrozenQry->data);+ lo_res = ExecuteSqlQueryForSingleRowAH(fout, loFrozenQry->data);
i_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
i_relminmxid = PQfnumber(lo_res, "relminmxid");
@@ -3162,7 +3162,7 @@ dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
else
printfPQExpBuffer(buf, "SELECT datconfig[%d] FROM pg_database WHERE oid = '%u'::oid", count, dboid);
- res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(AH, buf->data, PGRES_TUPLES_OK);
if (PQntuples(res) == 1 &&
!PQgetisnull(res, 0, 0))
@@ -3189,7 +3189,7 @@ dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
"WHERE setrole = r.oid AND setdatabase = '%u'::oid",
dboid);
- res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(AH, buf->data, PGRES_TUPLES_OK);
if (PQntuples(res) > 0)
{
@@ -3277,7 +3277,7 @@ dumpSearchPath(Archive *AH)
* listing schemas that may appear in search_path but not actually exist,
* which seems like a prudent exclusion.
*/
- res = ExecuteSqlQueryForSingleRow(AH,+ res = ExecuteSqlQueryForSingleRowAH(AH,
"SELECT pg_catalog.current_schemas(false)");
if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
@@ -3391,7 +3391,7 @@ getBlobs(Archive *fout)
"NULL::oid AS initrlomacl "
" FROM pg_largeobject");
- res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, blobQry->data, PGRES_TUPLES_OK);
i_oid = PQfnumber(res, "oid");
i_lomowner = PQfnumber(res, "rolname");
@@ -3537,7 +3537,7 @@ dumpBlobs(Archive *fout, void *arg)
"DECLARE bloboid CURSOR FOR "
"SELECT DISTINCT loid FROM pg_largeobject ORDER BY 1";
- ExecuteSqlStatement(fout, blobQry);+ ExecuteSqlStatementAH(fout, blobQry);
/* Command to fetch from cursor */
blobFetchQry = "FETCH 1000 IN bloboid";
@@ -3545,7 +3545,7 @@ dumpBlobs(Archive *fout, void *arg)
do
{
/* Do a fetch */
- res = ExecuteSqlQuery(fout, blobFetchQry, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, blobFetchQry, PGRES_TUPLES_OK);
/* Process the tuples, if any */
ntups = PQntuples(res);
@@ -3678,7 +3678,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
"FROM pg_catalog.pg_policy pol "
"WHERE polrelid = '%u'",
tbinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -3914,7 +3914,7 @@ getPublications(Archive *fout)
"FROM pg_publication p",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -4112,7 +4112,7 @@ getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
"WHERE pr.prrelid = '%u'"
" AND p.oid = pr.prpubid",
tbinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -4237,7 +4237,7 @@ getSubscriptions(Archive *fout)
{
int n;
- res = ExecuteSqlQuery(fout,+ res = ExecuteSqlQueryAH(fout,
"SELECT count(*) FROM pg_subscription "
"WHERE subdbid = (SELECT oid FROM pg_database"
" WHERE datname = current_database())",
@@ -4274,7 +4274,7 @@ getSubscriptions(Archive *fout)
"WHERE s.subdbid = (SELECT oid FROM pg_database\n"
" WHERE datname = current_database())");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -4446,7 +4446,7 @@ append_depends_on_extension(Archive *fout,
"AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
catalog,
dobj->catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_extname = PQfnumber(res, "extname");
for (i = 0; i < ntups; i++)
@@ -4485,7 +4485,7 @@ get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
"FROM pg_catalog.pg_type "
"WHERE oid = '%u'::pg_catalog.oid);",
next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, upgrade_query->data);
is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
PQclear(res);
} while (is_dup);
@@ -4518,7 +4518,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
"WHERE oid = '%u'::pg_catalog.oid;",
pg_type_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, upgrade_query->data);
pg_type_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
@@ -4551,7 +4551,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
"WHERE r.rngtypid = '%u'::pg_catalog.oid;",
pg_type_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, upgrade_query->data);
pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
@@ -4594,7 +4594,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
"WHERE c.oid = '%u'::pg_catalog.oid;",
pg_rel_oid);
- upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);+ upgrade_res = ExecuteSqlQueryForSingleRowAH(fout, upgrade_query->data);
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
@@ -4645,7 +4645,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout,
"WHERE c.oid = '%u'::pg_catalog.oid;",
pg_class_oid);
- upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);+ upgrade_res = ExecuteSqlQueryForSingleRowAH(fout, upgrade_query->data);
pg_class_reltoastrelid = atooid(PQgetvalue(upgrade_res, 0,
PQfnumber(upgrade_res, "reltoastrelid")));
@@ -4803,7 +4803,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
"FROM pg_namespace",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -4916,7 +4916,7 @@ getExtensions(Archive *fout, int *numExtensions)
"FROM pg_extension x "
"JOIN pg_namespace n ON n.oid = x.extnamespace");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -5093,7 +5093,7 @@ getTypes(Archive *fout, int *numTypes)
username_subquery);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -5249,7 +5249,7 @@ getOperators(Archive *fout, int *numOprs)
"FROM pg_operator",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numOprs = ntups;
@@ -5336,7 +5336,7 @@ getCollations(Archive *fout, int *numCollations)
"FROM pg_collation",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numCollations = ntups;
@@ -5408,7 +5408,7 @@ getConversions(Archive *fout, int *numConversions)
"FROM pg_conversion",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numConversions = ntups;
@@ -5481,7 +5481,7 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
"amhandler::pg_catalog.regproc AS amhandler "
"FROM pg_am");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numAccessMethods = ntups;
@@ -5552,7 +5552,7 @@ getOpclasses(Archive *fout, int *numOpclasses)
"FROM pg_opclass",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numOpclasses = ntups;
@@ -5635,7 +5635,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
"FROM pg_opfamily",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numOpfamilies = ntups;
@@ -5804,7 +5804,7 @@ getAggregates(Archive *fout, int *numAggs)
username_subquery);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numAggs = ntups;
@@ -6035,7 +6035,7 @@ getFuncs(Archive *fout, int *numFuncs)
appendPQExpBufferChar(query, ')');
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -6721,7 +6721,7 @@ getTables(Archive *fout, int *numTables)
RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -6791,7 +6791,7 @@ getTables(Archive *fout, int *numTables)
resetPQExpBuffer(query);
appendPQExpBufferStr(query, "SET statement_timeout = ");
appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
- ExecuteSqlStatement(fout, query->data);+ ExecuteSqlStatementAH(fout, query->data);
}
for (i = 0; i < ntups; i++)
@@ -6915,7 +6915,7 @@ getTables(Archive *fout, int *numTables)
appendPQExpBuffer(query,
"LOCK TABLE %s IN ACCESS SHARE MODE",
fmtQualifiedDumpable(&tblinfo[i]));
- ExecuteSqlStatement(fout, query->data);+ ExecuteSqlStatementAH(fout, query->data);
}
/* Emit notice if join for owner failed */
@@ -6926,7 +6926,7 @@ getTables(Archive *fout, int *numTables)
if (dopt->lockWaitTimeout)
{
- ExecuteSqlStatement(fout, "SET statement_timeout = 0");+ ExecuteSqlStatementAH(fout, "SET statement_timeout = 0");
}
PQclear(res);
@@ -7021,7 +7021,7 @@ getInherits(Archive *fout, int *numInherits)
/* find all the inheritance information */
appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -7365,7 +7365,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
tbinfo->dobj.catId.oid);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -7512,7 +7512,7 @@ getExtendedStatistics(Archive *fout)
"FROM pg_catalog.pg_statistic_ext",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -7610,7 +7610,7 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
"WHERE conrelid = '%u'::pg_catalog.oid "
"AND contype = 'f'",
tbinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -7746,7 +7746,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
"ORDER BY conname",
tyinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -7839,7 +7839,7 @@ getRules(Archive *fout, int *numRules)
"ORDER BY oid");
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -8013,7 +8013,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
tbinfo->dobj.catId.oid);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -8148,7 +8148,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
"ORDER BY e.oid",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -8318,7 +8318,7 @@ getProcLangs(Archive *fout, int *numProcLangs)
username_subquery);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -8427,7 +8427,7 @@ getCasts(Archive *fout, int *numCasts)
"FROM pg_cast ORDER BY 3,4");
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -8495,7 +8495,7 @@ get_language_name(Archive *fout, Oid langid)
query = createPQExpBuffer();
appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
destroyPQExpBuffer(query);
PQclear(res);
@@ -8538,7 +8538,7 @@ getTransforms(Archive *fout, int *numTransforms)
"FROM pg_transform "
"ORDER BY 3,4");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -8720,7 +8720,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"ORDER BY a.attnum",
tbinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, q->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -8797,7 +8797,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"WHERE adrelid = '%u'::pg_catalog.oid",
tbinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, q->data, PGRES_TUPLES_OK);
numDefaults = PQntuples(res);
attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
@@ -8919,7 +8919,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
tbinfo->dobj.catId.oid);
}
- res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, q->data, PGRES_TUPLES_OK);
numConstrs = PQntuples(res);
if (numConstrs != tbinfo->ncheck)
@@ -9062,7 +9062,7 @@ getTSParsers(Archive *fout, int *numTSParsers)
"prsend::oid, prsheadline::oid, prslextype::oid "
"FROM pg_ts_parser");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numTSParsers = ntups;
@@ -9146,7 +9146,7 @@ getTSDictionaries(Archive *fout, int *numTSDicts)
"FROM pg_ts_dict",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numTSDicts = ntups;
@@ -9226,7 +9226,7 @@ getTSTemplates(Archive *fout, int *numTSTemplates)
"tmplnamespace, tmplinit::oid, tmpllexize::oid "
"FROM pg_ts_template");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numTSTemplates = ntups;
@@ -9302,7 +9302,7 @@ getTSConfigurations(Archive *fout, int *numTSConfigs)
"FROM pg_ts_config",
username_subquery);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numTSConfigs = ntups;
@@ -9455,7 +9455,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
username_subquery);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numForeignDataWrappers = ntups;
@@ -9603,7 +9603,7 @@ getForeignServers(Archive *fout, int *numForeignServers)
username_subquery);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numForeignServers = ntups;
@@ -9742,7 +9742,7 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
username_subquery);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numDefaultACLs = ntups;
@@ -10098,7 +10098,7 @@ collectComments(Archive *fout, CommentItem **items)
"FROM pg_catalog.pg_description "
"ORDER BY classoid, objoid, objsubid");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
/* Construct lookup table containing OIDs in numeric form */
@@ -10546,7 +10546,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
"ORDER BY oid",
tyinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
num = PQntuples(res);
@@ -10684,7 +10684,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
"rngtypid = '%u'",
tyinfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -10942,7 +10942,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
"WHERE oid = '%u'::pg_catalog.oid",
tyinfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
@@ -11165,7 +11165,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.catId.oid);
}
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
@@ -11357,7 +11357,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.catId.oid);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -11536,7 +11536,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo)
tyinfo->typrelid);
/* Fetch column attnames */
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
if (ntups < 1)
@@ -12064,7 +12064,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"WHERE oid = '%u'::pg_catalog.oid",
finfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
@@ -12754,7 +12754,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo)
oprinfo->dobj.catId.oid);
}
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
i_oprkind = PQfnumber(res, "oprkind");
i_oprcode = PQfnumber(res, "oprcode");
@@ -12977,7 +12977,7 @@ convertTSFunction(Archive *fout, Oid funcOid)
snprintf(query, sizeof(query),
"SELECT '%u'::pg_catalog.regproc", funcOid);
- res = ExecuteSqlQueryForSingleRow(fout, query);+ res = ExecuteSqlQueryForSingleRowAH(fout, query);
result = pg_strdup(PQgetvalue(res, 0, 0));
@@ -13140,7 +13140,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
opcinfo->dobj.catId.oid);
}
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
i_opcintype = PQfnumber(res, "opcintype");
i_opckeytype = PQfnumber(res, "opckeytype");
@@ -13268,7 +13268,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
opcinfo->dobj.catId.oid);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -13347,7 +13347,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
opcinfo->dobj.catId.oid);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -13530,7 +13530,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
opfinfo->dobj.catId.oid);
}
- res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res_ops = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
resetPQExpBuffer(query);
@@ -13546,7 +13546,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
"ORDER BY amprocnum",
opfinfo->dobj.catId.oid);
- res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res_procs = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
/* Get additional fields from the pg_opfamily row */
resetPQExpBuffer(query);
@@ -13557,7 +13557,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
"WHERE oid = '%u'::pg_catalog.oid",
opfinfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
i_amname = PQfnumber(res, "amname");
@@ -13744,7 +13744,7 @@ dumpCollation(Archive *fout, CollInfo *collinfo)
"WHERE c.oid = '%u'::pg_catalog.oid",
collinfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
i_collprovider = PQfnumber(res, "collprovider");
i_collisdeterministic = PQfnumber(res, "collisdeterministic");
@@ -13861,7 +13861,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo)
"WHERE c.oid = '%u'::pg_catalog.oid",
convinfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
i_conforencoding = PQfnumber(res, "conforencoding");
i_contoencoding = PQfnumber(res, "contoencoding");
@@ -14078,7 +14078,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"AND p.oid = '%u'::pg_catalog.oid",
agginfo->aggfn.dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
i_agginitval = PQfnumber(res, "agginitval");
i_aggminitval = PQfnumber(res, "aggminitval");
@@ -14413,7 +14413,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo)
"FROM pg_ts_template p, pg_namespace n "
"WHERE p.oid = '%u' AND n.oid = tmplnamespace",
dictinfo->dicttemplate);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
nspname = PQgetvalue(res, 0, 0);
tmplname = PQgetvalue(res, 0, 1);
@@ -14555,7 +14555,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo)
"FROM pg_ts_parser p, pg_namespace n "
"WHERE p.oid = '%u' AND n.oid = prsnamespace",
cfginfo->cfgparser);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
nspname = PQgetvalue(res, 0, 0);
prsname = PQgetvalue(res, 0, 1);
@@ -14578,7 +14578,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo)
"ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
cfginfo->cfgparser, cfginfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_tokenname = PQfnumber(res, "tokenname");
@@ -14742,7 +14742,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo)
"FROM pg_foreign_data_wrapper w "
"WHERE w.oid = '%u'",
srvinfo->srvfdw);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
fdwname = PQgetvalue(res, 0, 0);
appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
@@ -14858,7 +14858,7 @@ dumpUserMappings(Archive *fout,
"ORDER BY usename",
catalogId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_usename = PQfnumber(res, "usename");
@@ -15379,7 +15379,7 @@ collectSecLabels(Archive *fout, SecLabelItem **items)
"FROM pg_catalog.pg_seclabel "
"ORDER BY classoid, objoid, objsubid");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
/* Construct lookup table containing OIDs in numeric form */
i_label = PQfnumber(res, "label");
@@ -15515,7 +15515,7 @@ dumpTable(Archive *fout, TableInfo *tbinfo)
tbinfo->dobj.catId.oid);
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
for (i = 0; i < PQntuples(res); i++)
{
@@ -15565,7 +15565,7 @@ createViewAsClause(Archive *fout, TableInfo *tbinfo)
"SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
tbinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) != 1)
{
@@ -15740,7 +15740,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
"ON (fs.oid = ft.ftserver) "
"WHERE ft.ftrelid = '%u'",
tbinfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
i_srvname = PQfnumber(res, "srvname");
i_ftoptions = PQfnumber(res, "ftoptions");
srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
@@ -16693,7 +16693,7 @@ dumpStatisticsExt(Archive *fout, StatsExtInfo *statsextinfo)
"pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
statsextinfo->dobj.catId.oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
stxdef = PQgetvalue(res, 0, 0);
@@ -17061,7 +17061,7 @@ findLastBuiltinOid_V71(Archive *fout)
PGresult *res;
Oid last_oid;
- res = ExecuteSqlQueryForSingleRow(fout,+ res = ExecuteSqlQueryForSingleRowAH(fout,
"SELECT datlastsysoid FROM pg_database WHERE datname = current_database()");
last_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "datlastsysoid")));
PQclear(res);
@@ -17130,7 +17130,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
fmtQualifiedDumpable(tbinfo));
}
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) != 1)
{
@@ -17353,7 +17353,7 @@ dumpSequenceData(Archive *fout, TableDataInfo *tdinfo)
"SELECT last_value, is_called FROM %s",
fmtQualifiedDumpable(tbinfo));
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) != 1)
{
@@ -17761,7 +17761,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
"SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
rinfo->dobj.catId.oid);
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) != 1)
{
@@ -17891,7 +17891,7 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
"AND deptype = 'e' "
"ORDER BY 3");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -18090,7 +18090,7 @@ processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
"AND refclassid = 'pg_extension'::regclass "
"AND classid = 'pg_class'::regclass;");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_conrelid = PQfnumber(res, "conrelid");
@@ -18196,7 +18196,7 @@ getDependencies(Archive *fout)
/* Sort the output for efficiency below */
appendPQExpBufferStr(query, "ORDER BY 1,2");
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@@ -18549,7 +18549,7 @@ getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
oid);
- res = ExecuteSqlQueryForSingleRow(fout, query->data);+ res = ExecuteSqlQueryForSingleRowAH(fout, query->data);
/* result of format_type is already quoted */
result = pg_strdup(PQgetvalue(res, 0, 0));
--
2.21.1 (Apple Git-122.3)
=
v32-0003-Creating-query_utils-frontend-utility.patch (application/octet-stream, 10.4K) download
From a5c25a47a6ef095b190a6049b82569b35e126e78 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 6 Jan 2021 13:01:02 -0800
Subject: [PATCH v32 3/9] Creating query_utils frontend utility
Moving the ExecuteSqlQuery, ExecuteSqlQueryForSingleRow, and
ExecuteSqlStatement out of the pg_dump project into a new shared
location.
---
src/bin/pg_dump/pg_backup_db.c | 102 +-------------------------
src/bin/pg_dump/pg_backup_db.h | 17 -----
src/fe_utils/Makefile | 1 +
src/fe_utils/query_utils.c | 114 +++++++++++++++++++++++++++++
src/include/fe_utils/query_utils.h | 34 +++++++++
5 files changed, 150 insertions(+), 118 deletions(-)
create mode 100644 src/fe_utils/query_utils.c
create mode 100644 src/include/fe_utils/query_utils.h
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.cindex b55a968da2..38402d0831 100644--- a/src/bin/pg_dump/pg_backup_db.c+++ b/src/bin/pg_dump/pg_backup_db.c@@ -20,6 +20,7 @@
#include "common/connect.h"
#include "common/string.h"
#include "dumputils.h"
+#include "fe_utils/query_utils.h"
#include "fe_utils/string_utils.h"
#include "parallel.h"
#include "pg_backup_archiver.h"
@@ -271,107 +272,6 @@ notice_processor(void *arg, const char *message)
pg_log_generic(PG_LOG_INFO, "%s", message);
}
-/*- * The exiting query result handler embeds the historical pg_dump behavior- * under query error conditions, including exiting nicely. The 'conn' object- * is unused here, but is included in the interface for alternate query result- * handler implementations.- *- * Whether the query was successful is determined by comparing the returned- * status code against the expected status code, and by comparing the number of- * tuples returned from the query against expected_ntups. Special negative- * values of expected_ntups can be used to require at least one row or to- * disables ntup checking.- *- * Exits on failure. On successful query completion, returns the 'res'- * argument as a notational convenience.- */-PGresult *-exiting_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,- int expected_ntups, const char *query)-{- if (PQresultStatus(res) != expected_status)- {- pg_log_error("query failed: %s", PQerrorMessage(conn));- pg_log_error("query was: %s", query);- PQfinish(conn);- exit_nicely(1);- }- if (expected_ntups == POSITIVE_NTUPS || expected_ntups >= 0)- {- int ntups = PQntuples(res);-- if (expected_ntups == POSITIVE_NTUPS)- {- if (ntups == 0)- fatal("query returned no rows: %s", query);- }- else if (ntups != expected_ntups)- {- /*- * Preserve historical message behavior of spelling "one" as the- * expected row count.- */- if (expected_ntups == 1)- fatal(ngettext("query returned %d row instead of one: %s",- "query returned %d rows instead of one: %s",- ntups),- ntups, query);- fatal(ngettext("query returned %d row instead of %d: %s",- "query returned %d rows instead of %d: %s",- ntups),- ntups, expected_ntups, query);- }- }- return res;-}--/*- * Executes the given SQL query statement.- *- * Invokes the exiting handler for any but PGRES_COMMAND_OK status.- */-void-ExecuteSqlStatement(PGconn *conn, const char *query)-{- PQclear(exiting_handler(PQexec(conn, query),- conn,- PGRES_COMMAND_OK,- ANY_NTUPS,- query));-}--/*- * Executes the given SQL query.- *- * Invokes the exiting handler unless the given 'status' results.- *- * If successful, returns the query result.- */-PGresult *-ExecuteSqlQuery(PGconn *conn, const char *query, ExecStatusType status)-{- return exiting_handler(PQexec(conn, query),- conn,- status,- ANY_NTUPS,- query);-}--/*- * Like ExecuteSqlQuery, but requires PGRES_TUPLES_OK status and- * requires that exactly one row be returned.- */-PGresult *-ExecuteSqlQueryForSingleRow(PGconn *conn, const char *query)-{- return exiting_handler(PQexec(conn, query),- conn,- PGRES_TUPLES_OK,- 1,- query);-}-
void
ExecuteSqlStatementAH(Archive *AHX, const char *query)
{
diff --git a/src/bin/pg_dump/pg_backup_db.h b/src/bin/pg_dump/pg_backup_db.hindex 1aac600ece..018a28908e 100644--- a/src/bin/pg_dump/pg_backup_db.h+++ b/src/bin/pg_dump/pg_backup_db.h@@ -13,23 +13,6 @@
extern int ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen);
-#define POSITIVE_NTUPS (-1)-#define ANY_NTUPS (-2)-typedef PGresult *(*PGresultHandler) (PGresult *res,- PGconn *conn,- ExecStatusType expected_status,- int expected_ntups,- const char *query);--extern PGresult *exiting_handler(PGresult *res, PGconn *conn,- ExecStatusType expected_status,- int expected_ntups, const char *query);--extern void ExecuteSqlStatement(PGconn *conn, const char *query);-extern PGresult *ExecuteSqlQuery(PGconn *conn, const char *query,- ExecStatusType expected_status);-extern PGresult *ExecuteSqlQueryForSingleRow(PGconn *conn, const char *query);-
extern void ExecuteSqlStatementAH(Archive *AHX, const char *query);
extern PGresult *ExecuteSqlQueryAH(Archive *AHX, const char *query,
ExecStatusType status);
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefileindex d6c328faf1..7fdbe08e11 100644--- a/src/fe_utils/Makefile+++ b/src/fe_utils/Makefile@@ -27,6 +27,7 @@ OBJS = \
mbprint.o \
print.o \
psqlscan.o \
+ query_utils.o \
recovery_gen.o \
simple_list.o \
string_utils.o
diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.cnew file mode 100644index 0000000000..b28750f4b2--- /dev/null+++ b/src/fe_utils/query_utils.c@@ -0,0 +1,114 @@+/*-------------------------------------------------------------------------+ *+ * Query executing routines with facilities for modular error handling.+ *+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/fe_utils/query_utils.c+ *+ *-------------------------------------------------------------------------+ */+#include "fe_utils/exit_utils.h"+#include "fe_utils/query_utils.h"++/*+ * The exiting query result handler embeds the historical pg_dump behavior+ * under query error conditions, including exiting nicely. The 'conn' object+ * is unused here, but is included in the interface for alternate query result+ * handler implementations.+ *+ * Whether the query was successful is determined by comparing the returned+ * status code against the expected status code, and by comparing the number of+ * tuples returned from the query against expected_ntups. Special negative+ * values of expected_ntups can be used to require at least one row or to+ * disables ntup checking.+ *+ * Exits on failure. On successful query completion, returns the 'res'+ * argument as a notational convenience.+ */+PGresult *+exiting_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,+ int expected_ntups, const char *query)+{+ if (PQresultStatus(res) != expected_status)+ {+ pg_log_error("query failed: %s", PQerrorMessage(conn));+ pg_log_error("query was: %s", query);+ PQfinish(conn);+ exit_nicely(1);+ }+ if (expected_ntups == POSITIVE_NTUPS || expected_ntups >= 0)+ {+ int ntups = PQntuples(res);++ if (expected_ntups == POSITIVE_NTUPS)+ {+ if (ntups == 0)+ fatal("query returned no rows: %s", query);+ }+ else if (ntups != expected_ntups)+ {+ /*+ * Preserve historical message behavior of spelling "one" as the+ * expected row count.+ */+ if (expected_ntups == 1)+ fatal(ngettext("query returned %d row instead of one: %s",+ "query returned %d rows instead of one: %s",+ ntups),+ ntups, query);+ fatal(ngettext("query returned %d row instead of %d: %s",+ "query returned %d rows instead of %d: %s",+ ntups),+ ntups, expected_ntups, query);+ }+ }+ return res;+}++/*+ * Executes the given SQL query statement.+ *+ * Invokes the exiting handler for any but PGRES_COMMAND_OK status.+ */+void+ExecuteSqlStatement(PGconn *conn, const char *query)+{+ PQclear(exiting_handler(PQexec(conn, query),+ conn,+ PGRES_COMMAND_OK,+ ANY_NTUPS,+ query));+}++/*+ * Executes the given SQL query.+ *+ * Invokes the exiting handler unless the given 'status' results.+ *+ * If successful, returns the query result.+ */+PGresult *+ExecuteSqlQuery(PGconn *conn, const char *query, ExecStatusType status)+{+ return exiting_handler(PQexec(conn, query),+ conn,+ status,+ ANY_NTUPS,+ query);+}++/*+ * Like ExecuteSqlQuery, but requires PGRES_TUPLES_OK status and+ * requires that exactly one row be returned.+ */+PGresult *+ExecuteSqlQueryForSingleRow(PGconn *conn, const char *query)+{+ return exiting_handler(PQexec(conn, query),+ conn,+ PGRES_TUPLES_OK,+ 1,+ query);+}diff --git a/src/include/fe_utils/query_utils.h b/src/include/fe_utils/query_utils.hnew file mode 100644index 0000000000..f03d17b1ed--- /dev/null+++ b/src/include/fe_utils/query_utils.h@@ -0,0 +1,34 @@+/*-------------------------------------------------------------------------+ *+ * Query executing routines with facilities for modular error handling.+ *+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/fe_utils/query_utils.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef QUERY_UTILS_H+#define QUERY_UTILS_H++#include "libpq-fe.h"++#define POSITIVE_NTUPS (-1)+#define ANY_NTUPS (-2)+typedef PGresult *(*PGresultHandler) (PGresult *res,+ PGconn *conn,+ ExecStatusType expected_status,+ int expected_ntups,+ const char *query);++extern PGresult *exiting_handler(PGresult *res, PGconn *conn,+ ExecStatusType expected_status,+ int expected_ntups, const char *query);++extern void ExecuteSqlStatement(PGconn *conn, const char *query);+extern PGresult *ExecuteSqlQuery(PGconn *conn, const char *query,+ ExecStatusType expected_status);+extern PGresult *ExecuteSqlQueryForSingleRow(PGconn *conn, const char *query);++#endif /* QUERY_UTILS_H */--
2.21.1 (Apple Git-122.3)
=
v32-0004-Adding-CurrentQueryHandler-logic.patch (application/octet-stream, 5.2K) download
From 86a8078d2750a9ee42db5a341838fad6072252f5 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 6 Jan 2021 13:25:29 -0800
Subject: [PATCH v32 4/9] Adding CurrentQueryHandler logic.
Extending the default set of PGresultHandlers and creating a
mechanism to switch between them using a new function
ResultHandlerSwitchTo, analogous to MemoryContextSwithTo. In
addition to the exiting_handler already created in a prior commit
(which embeds the historical behavior from pg_dump), adding a
quiet_handler which cleans up and exits without logging anything,
and a noop_handler which does nothing, leaving the responsibility
for cleanup handling to the caller.
---
src/fe_utils/query_utils.c | 81 ++++++++++++++++++++++--------
src/include/fe_utils/query_utils.h | 17 +++++++
2 files changed, 76 insertions(+), 22 deletions(-)
diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.cindex b28750f4b2..355da6edaf 100644--- a/src/fe_utils/query_utils.c+++ b/src/fe_utils/query_utils.c@@ -12,6 +12,12 @@
#include "fe_utils/exit_utils.h"
#include "fe_utils/query_utils.h"
+/*+ * Global memory.+ */++PGresultHandler CurrentResultHandler = exiting_handler;+
/*
* The exiting query result handler embeds the historical pg_dump behavior
* under query error conditions, including exiting nicely. The 'conn' object
@@ -29,7 +35,7 @@
*/
PGresult *
exiting_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,
- int expected_ntups, const char *query)+ int expected_ntups, const char *query)
{
if (PQresultStatus(res) != expected_status)
{
@@ -67,48 +73,79 @@ exiting_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,
return res;
}
+/*+ * Quietly cleans up and exits nicely unless the expected conditions were met.+ */+PGresult *+quiet_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,+ int expected_ntups, const char *query)+{+ int ntups = PQntuples(res);++ if ((PQresultStatus(res) != expected_status) ||+ (expected_ntups == POSITIVE_NTUPS && ntups == 0) ||+ (expected_ntups >= 0 && ntups != expected_ntups))+ {+ PQfinish(conn);+ exit_nicely(1);+ }++ return res;+}++/*+ * Does nothing other than returning the 'res' argument back to the caller.+ * This handler is intended for callers who prefer to perform the error+ * handling themselves.+ */+PGresult *+noop_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,+ int expected_ntups, const char *query)+{+ return res;+}+
/*
* Executes the given SQL query statement.
*
- * Invokes the exiting handler for any but PGRES_COMMAND_OK status.+ * Expects a PGRES_COMMAND_OK status.
*/
void
ExecuteSqlStatement(PGconn *conn, const char *query)
{
- PQclear(exiting_handler(PQexec(conn, query),- conn,- PGRES_COMMAND_OK,- ANY_NTUPS,- query));+ PQclear(CurrentResultHandler(PQexec(conn, query),+ conn,+ PGRES_COMMAND_OK,+ ANY_NTUPS,+ query));
}
/*
* Executes the given SQL query.
*
- * Invokes the exiting handler unless the given 'status' results.- *- * If successful, returns the query result.+ * Expects the given status.
*/
PGresult *
ExecuteSqlQuery(PGconn *conn, const char *query, ExecStatusType status)
{
- return exiting_handler(PQexec(conn, query),- conn,- status,- ANY_NTUPS,- query);+ return CurrentResultHandler(PQexec(conn, query),+ conn,+ status,+ ANY_NTUPS,+ query);
}
/*
- * Like ExecuteSqlQuery, but requires PGRES_TUPLES_OK status and- * requires that exactly one row be returned.+ * Executes the given SQL query.+ *+ * Expects a PGRES_TUPLES_OK status and precisely one row.
*/
PGresult *
ExecuteSqlQueryForSingleRow(PGconn *conn, const char *query)
{
- return exiting_handler(PQexec(conn, query),- conn,- PGRES_TUPLES_OK,- 1,- query);+ return CurrentResultHandler(PQexec(conn, query),+ conn,+ PGRES_TUPLES_OK,+ 1,+ query);
}
diff --git a/src/include/fe_utils/query_utils.h b/src/include/fe_utils/query_utils.hindex f03d17b1ed..80958e94fb 100644--- a/src/include/fe_utils/query_utils.h+++ b/src/include/fe_utils/query_utils.h@@ -22,9 +22,26 @@ typedef PGresult *(*PGresultHandler) (PGresult *res,
int expected_ntups,
const char *query);
+extern PGresultHandler CurrentResultHandler;++static inline PGresultHandler+ResultHandlerSwitchTo(PGresultHandler handler)+{+ PGresultHandler old = CurrentResultHandler;++ CurrentResultHandler = handler;+ return old;+}+
extern PGresult *exiting_handler(PGresult *res, PGconn *conn,
ExecStatusType expected_status,
int expected_ntups, const char *query);
+extern PGresult *quiet_handler(PGresult *res, PGconn *conn,+ ExecStatusType expected_status,+ int expected_ntups, const char *query);+extern PGresult *noop_handler(PGresult *res, PGconn *conn,+ ExecStatusType expected_status,+ int expected_ntups, const char *query);
extern void ExecuteSqlStatement(PGconn *conn, const char *query);
extern PGresult *ExecuteSqlQuery(PGconn *conn, const char *query,
--
2.21.1 (Apple Git-122.3)
=
v32-0005-Refactoring-pg_dumpall-functions.patch (application/octet-stream, 2.1K) download
From 34718483db460517416a84d505ce9a166abd5ebb Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 6 Jan 2021 13:37:42 -0800
Subject: [PATCH v32 5/9] Refactoring pg_dumpall functions.
The functions executeQuery and executeCommand in pg_dumpall.c were
not refactored in prior commits along with functions from
pg_backup_db.c because they were in a separate file, but now that
the infrastructure has been moved to fe_utils/query_utils,
refactoring these two functions to use it.
---
src/bin/pg_dump/pg_dumpall.c | 31 +++----------------------------
1 file changed, 3 insertions(+), 28 deletions(-)
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.cindex 85d08ad660..807226537a 100644--- a/src/bin/pg_dump/pg_dumpall.c+++ b/src/bin/pg_dump/pg_dumpall.c@@ -23,6 +23,7 @@
#include "common/logging.h"
#include "common/string.h"
#include "dumputils.h"
+#include "fe_utils/query_utils.h"
#include "fe_utils/string_utils.h"
#include "getopt_long.h"
#include "pg_backup.h"
@@ -1874,21 +1875,8 @@ constructConnStr(const char **keywords, const char **values)
static PGresult *
executeQuery(PGconn *conn, const char *query)
{
- PGresult *res;-
pg_log_info("executing %s", query);
-- res = PQexec(conn, query);- if (!res ||- PQresultStatus(res) != PGRES_TUPLES_OK)- {- pg_log_error("query failed: %s", PQerrorMessage(conn));- pg_log_error("query was: %s", query);- PQfinish(conn);- exit_nicely(1);- }-- return res;+ return ExecuteSqlQuery(conn, query, PGRES_TUPLES_OK);
}
/*
@@ -1897,21 +1885,8 @@ executeQuery(PGconn *conn, const char *query)
static void
executeCommand(PGconn *conn, const char *query)
{
- PGresult *res;-
pg_log_info("executing %s", query);
-- res = PQexec(conn, query);- if (!res ||- PQresultStatus(res) != PGRES_COMMAND_OK)- {- pg_log_error("query failed: %s", PQerrorMessage(conn));- pg_log_error("query was: %s", query);- PQfinish(conn);- exit_nicely(1);- }-- PQclear(res);+ PQclear(ExecuteSqlQuery(conn, query, PGRES_COMMAND_OK));
}
--
2.21.1 (Apple Git-122.3)
=
v32-0006-Refactoring-expand_schema_name_patterns-and-frie.patch (application/octet-stream, 6.6K) download
From 6d91a3031f28387fbc2a6d776278abfd2b04bb1e Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 6 Jan 2021 13:51:02 -0800
Subject: [PATCH v32 6/9] Refactoring expand_schema_name_patterns and friends.
Refactoring these functions to take a PGconn pointer rather than an
Archive pointer in preparation for moving these functions to
fe_utils. This is much like what was previously done for
ExecuteSqlQuery and friends, and for the same reasons.
---
src/bin/pg_dump/pg_dump.c | 47 ++++++++++++++++++++++-----------------
1 file changed, 27 insertions(+), 20 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.cindex e8985a834f..41ce4b7866 100644--- a/src/bin/pg_dump/pg_dump.c+++ b/src/bin/pg_dump/pg_dump.c@@ -55,6 +55,7 @@
#include "catalog/pg_type_d.h"
#include "common/connect.h"
#include "dumputils.h"
+#include "fe_utils/query_utils.h"
#include "fe_utils/string_utils.h"
#include "getopt_long.h"
#include "libpq/libpq-fs.h"
@@ -147,14 +148,14 @@ static void setup_connection(Archive *AH,
const char *dumpencoding, const char *dumpsnapshot,
char *use_role);
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
-static void expand_schema_name_patterns(Archive *fout,+static void expand_schema_name_patterns(PGconn *conn,
SimpleStringList *patterns,
SimpleOidList *oids,
bool strict_names);
-static void expand_foreign_server_name_patterns(Archive *fout,+static void expand_foreign_server_name_patterns(PGconn *conn,
SimpleStringList *patterns,
SimpleOidList *oids);
-static void expand_table_name_patterns(Archive *fout,+static void expand_table_name_patterns(PGconn *conn,
SimpleStringList *patterns,
SimpleOidList *oids,
bool strict_names);
@@ -798,13 +799,15 @@ main(int argc, char **argv)
/* Expand schema selection patterns into OID lists */
if (schema_include_patterns.head != NULL)
{
- expand_schema_name_patterns(fout, &schema_include_patterns,+ expand_schema_name_patterns(GetConnection(fout),+ &schema_include_patterns,
&schema_include_oids,
strict_names);
if (schema_include_oids.head == NULL)
fatal("no matching schemas were found");
}
- expand_schema_name_patterns(fout, &schema_exclude_patterns,+ expand_schema_name_patterns(GetConnection(fout),+ &schema_exclude_patterns,
&schema_exclude_oids,
false);
/* non-matching exclusion patterns aren't an error */
@@ -812,21 +815,25 @@ main(int argc, char **argv)
/* Expand table selection patterns into OID lists */
if (table_include_patterns.head != NULL)
{
- expand_table_name_patterns(fout, &table_include_patterns,+ expand_table_name_patterns(GetConnection(fout),+ &table_include_patterns,
&table_include_oids,
strict_names);
if (table_include_oids.head == NULL)
fatal("no matching tables were found");
}
- expand_table_name_patterns(fout, &table_exclude_patterns,+ expand_table_name_patterns(GetConnection(fout),+ &table_exclude_patterns,
&table_exclude_oids,
false);
- expand_table_name_patterns(fout, &tabledata_exclude_patterns,+ expand_table_name_patterns(GetConnection(fout),+ &tabledata_exclude_patterns,
&tabledata_exclude_oids,
false);
- expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,+ expand_foreign_server_name_patterns(GetConnection(fout),+ &foreign_servers_include_patterns,
&foreign_servers_include_oids);
/* non-matching exclusion patterns aren't an error */
@@ -1316,7 +1323,7 @@ parseArchiveFormat(const char *format, ArchiveMode *mode)
* and append them to the given OID list.
*/
static void
-expand_schema_name_patterns(Archive *fout,+expand_schema_name_patterns(PGconn *conn,
SimpleStringList *patterns,
SimpleOidList *oids,
bool strict_names)
@@ -1340,10 +1347,10 @@ expand_schema_name_patterns(Archive *fout,
{
appendPQExpBufferStr(query,
"SELECT oid FROM pg_catalog.pg_namespace n\n");
- processSQLNamePattern(GetConnection(fout), query, cell->val, false,+ processSQLNamePattern(conn, query, cell->val, false,
false, NULL, "n.nspname", NULL, NULL);
- res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);
if (strict_names && PQntuples(res) == 0)
fatal("no matching schemas were found for pattern \"%s\"", cell->val);
@@ -1364,7 +1371,7 @@ expand_schema_name_patterns(Archive *fout,
* and append them to the given OID list.
*/
static void
-expand_foreign_server_name_patterns(Archive *fout,+expand_foreign_server_name_patterns(PGconn *conn,
SimpleStringList *patterns,
SimpleOidList *oids)
{
@@ -1387,10 +1394,10 @@ expand_foreign_server_name_patterns(Archive *fout,
{
appendPQExpBufferStr(query,
"SELECT oid FROM pg_catalog.pg_foreign_server s\n");
- processSQLNamePattern(GetConnection(fout), query, cell->val, false,+ processSQLNamePattern(conn, query, cell->val, false,
false, NULL, "s.srvname", NULL, NULL);
- res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);+ res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);
if (PQntuples(res) == 0)
fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
@@ -1410,7 +1417,7 @@ expand_foreign_server_name_patterns(Archive *fout,
* in pg_dumpall.c
*/
static void
-expand_table_name_patterns(Archive *fout,+expand_table_name_patterns(PGconn *conn,
SimpleStringList *patterns, SimpleOidList *oids,
bool strict_names)
{
@@ -1446,13 +1453,13 @@ expand_table_name_patterns(Archive *fout,
RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
RELKIND_PARTITIONED_TABLE);
- processSQLNamePattern(GetConnection(fout), query, cell->val, true,+ processSQLNamePattern(conn, query, cell->val, true,
false, "n.nspname", "c.relname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
- ExecuteSqlStatementAH(fout, "RESET search_path");- res = ExecuteSqlQueryAH(fout, query->data, PGRES_TUPLES_OK);- PQclear(ExecuteSqlQueryForSingleRowAH(fout,+ ExecuteSqlStatement(conn, "RESET search_path");+ res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);+ PQclear(ExecuteSqlQueryForSingleRow(conn,
ALWAYS_SECURE_SEARCH_PATH_SQL));
if (strict_names && PQntuples(res) == 0)
fatal("no matching tables were found for pattern \"%s\"", cell->val);
--
2.21.1 (Apple Git-122.3)
=
v32-0007-Moving-pg_dump-functions-to-new-file-option_util.patch (application/octet-stream, 17.0K) download
From 0ceafd8608c542ca21ce8b8ea111a1f5115bbc0a Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 6 Jan 2021 14:07:58 -0800
Subject: [PATCH v32 7/9] Moving pg_dump functions to new file option_utils
Moving the recently refactored functions
expand_schema_name_patterns, expand_foreign_server_name_patterns,
and expand_table_name_patterns from pg_dump.c, along with the
function expand_dbname_patterns from pg_dumpall.c, into the new file
fe_utils/option_utils.c
---
src/bin/pg_dump/pg_dump.c | 170 +--------------------
src/bin/pg_dump/pg_dumpall.c | 46 +-----
src/fe_utils/Makefile | 1 +
src/fe_utils/option_utils.c | 225 ++++++++++++++++++++++++++++
src/include/fe_utils/option_utils.h | 35 +++++
5 files changed, 263 insertions(+), 214 deletions(-)
create mode 100644 src/fe_utils/option_utils.c
create mode 100644 src/include/fe_utils/option_utils.h
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.cindex 41ce4b7866..c334b9e829 100644--- a/src/bin/pg_dump/pg_dump.c+++ b/src/bin/pg_dump/pg_dump.c@@ -55,6 +55,7 @@
#include "catalog/pg_type_d.h"
#include "common/connect.h"
#include "dumputils.h"
+#include "fe_utils/option_utils.h"
#include "fe_utils/query_utils.h"
#include "fe_utils/string_utils.h"
#include "getopt_long.h"
@@ -148,17 +149,6 @@ static void setup_connection(Archive *AH,
const char *dumpencoding, const char *dumpsnapshot,
char *use_role);
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
-static void expand_schema_name_patterns(PGconn *conn,- SimpleStringList *patterns,- SimpleOidList *oids,- bool strict_names);-static void expand_foreign_server_name_patterns(PGconn *conn,- SimpleStringList *patterns,- SimpleOidList *oids);-static void expand_table_name_patterns(PGconn *conn,- SimpleStringList *patterns,- SimpleOidList *oids,- bool strict_names);
static NamespaceInfo *findNamespace(Oid nsoid);
static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo);
@@ -1318,164 +1308,6 @@ parseArchiveFormat(const char *format, ArchiveMode *mode)
return archiveFormat;
}
-/*- * Find the OIDs of all schemas matching the given list of patterns,- * and append them to the given OID list.- */-static void-expand_schema_name_patterns(PGconn *conn,- SimpleStringList *patterns,- SimpleOidList *oids,- bool strict_names)-{- PQExpBuffer query;- PGresult *res;- SimpleStringListCell *cell;- int i;-- if (patterns->head == NULL)- return; /* nothing to do */-- query = createPQExpBuffer();-- /*- * The loop below runs multiple SELECTs might sometimes result in- * duplicate entries in the OID list, but we don't care.- */-- for (cell = patterns->head; cell; cell = cell->next)- {- appendPQExpBufferStr(query,- "SELECT oid FROM pg_catalog.pg_namespace n\n");- processSQLNamePattern(conn, query, cell->val, false,- false, NULL, "n.nspname", NULL, NULL);-- res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);- if (strict_names && PQntuples(res) == 0)- fatal("no matching schemas were found for pattern \"%s\"", cell->val);-- for (i = 0; i < PQntuples(res); i++)- {- simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));- }-- PQclear(res);- resetPQExpBuffer(query);- }-- destroyPQExpBuffer(query);-}--/*- * Find the OIDs of all foreign servers matching the given list of patterns,- * and append them to the given OID list.- */-static void-expand_foreign_server_name_patterns(PGconn *conn,- SimpleStringList *patterns,- SimpleOidList *oids)-{- PQExpBuffer query;- PGresult *res;- SimpleStringListCell *cell;- int i;-- if (patterns->head == NULL)- return; /* nothing to do */-- query = createPQExpBuffer();-- /*- * The loop below runs multiple SELECTs might sometimes result in- * duplicate entries in the OID list, but we don't care.- */-- for (cell = patterns->head; cell; cell = cell->next)- {- appendPQExpBufferStr(query,- "SELECT oid FROM pg_catalog.pg_foreign_server s\n");- processSQLNamePattern(conn, query, cell->val, false,- false, NULL, "s.srvname", NULL, NULL);-- res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);- if (PQntuples(res) == 0)- fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);-- for (i = 0; i < PQntuples(res); i++)- simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));-- PQclear(res);- resetPQExpBuffer(query);- }-- destroyPQExpBuffer(query);-}--/*- * Find the OIDs of all tables matching the given list of patterns,- * and append them to the given OID list. See also expand_dbname_patterns()- * in pg_dumpall.c- */-static void-expand_table_name_patterns(PGconn *conn,- SimpleStringList *patterns, SimpleOidList *oids,- bool strict_names)-{- PQExpBuffer query;- PGresult *res;- SimpleStringListCell *cell;- int i;-- if (patterns->head == NULL)- return; /* nothing to do */-- query = createPQExpBuffer();-- /*- * this might sometimes result in duplicate entries in the OID list, but- * we don't care.- */-- for (cell = patterns->head; cell; cell = cell->next)- {- /*- * Query must remain ABSOLUTELY devoid of unqualified names. This- * would be unnecessary given a pg_table_is_visible() variant taking a- * search_path argument.- */- appendPQExpBuffer(query,- "SELECT c.oid"- "\nFROM pg_catalog.pg_class c"- "\n LEFT JOIN pg_catalog.pg_namespace n"- "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"- "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"- "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",- RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,- RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,- RELKIND_PARTITIONED_TABLE);- processSQLNamePattern(conn, query, cell->val, true,- false, "n.nspname", "c.relname", NULL,- "pg_catalog.pg_table_is_visible(c.oid)");-- ExecuteSqlStatement(conn, "RESET search_path");- res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);- PQclear(ExecuteSqlQueryForSingleRow(conn,- ALWAYS_SECURE_SEARCH_PATH_SQL));- if (strict_names && PQntuples(res) == 0)- fatal("no matching tables were found for pattern \"%s\"", cell->val);-- for (i = 0; i < PQntuples(res); i++)- {- simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));- }-- PQclear(res);- resetPQExpBuffer(query);- }-- destroyPQExpBuffer(query);-}-
/*
* checkExtensionMembership
* Determine whether object is an extension member, and if so,
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.cindex 807226537a..01db15dfda 100644--- a/src/bin/pg_dump/pg_dumpall.c+++ b/src/bin/pg_dump/pg_dumpall.c@@ -23,6 +23,7 @@
#include "common/logging.h"
#include "common/string.h"
#include "dumputils.h"
+#include "fe_utils/option_utils.h"
#include "fe_utils/query_utils.h"
#include "fe_utils/string_utils.h"
#include "getopt_long.h"
@@ -54,8 +55,6 @@ static PGconn *connectDatabase(const char *dbname, const char *connstr, const ch
static char *constructConnStr(const char **keywords, const char **values);
static PGresult *executeQuery(PGconn *conn, const char *query);
static void executeCommand(PGconn *conn, const char *query);
-static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,- SimpleStringList *names);
static char pg_dump_bin[MAXPGPATH];
static const char *progname;
@@ -1409,49 +1408,6 @@ dumpUserConfig(PGconn *conn, const char *username)
destroyPQExpBuffer(buf);
}
-/*- * Find a list of database names that match the given patterns.- * See also expand_table_name_patterns() in pg_dump.c- */-static void-expand_dbname_patterns(PGconn *conn,- SimpleStringList *patterns,- SimpleStringList *names)-{- PQExpBuffer query;- PGresult *res;-- if (patterns->head == NULL)- return; /* nothing to do */-- query = createPQExpBuffer();-- /*- * The loop below runs multiple SELECTs, which might sometimes result in- * duplicate entries in the name list, but we don't care, since all we're- * going to do is test membership of the list.- */-- for (SimpleStringListCell *cell = patterns->head; cell; cell = cell->next)- {- appendPQExpBufferStr(query,- "SELECT datname FROM pg_catalog.pg_database n\n");- processSQLNamePattern(conn, query, cell->val, false,- false, NULL, "datname", NULL, NULL);-- res = executeQuery(conn, query->data);- for (int i = 0; i < PQntuples(res); i++)- {- simple_string_list_append(names, PQgetvalue(res, i, 0));- }-- PQclear(res);- resetPQExpBuffer(query);- }-- destroyPQExpBuffer(query);-}-
/*
* Dump contents of databases.
*/
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefileindex 7fdbe08e11..eb937e4648 100644--- a/src/fe_utils/Makefile+++ b/src/fe_utils/Makefile@@ -25,6 +25,7 @@ OBJS = \
conditional.o \
exit_utils.o \
mbprint.o \
+ option_utils.o \
print.o \
psqlscan.o \
query_utils.o \
diff --git a/src/fe_utils/option_utils.c b/src/fe_utils/option_utils.cnew file mode 100644index 0000000000..7893df77aa--- /dev/null+++ b/src/fe_utils/option_utils.c@@ -0,0 +1,225 @@+/*-------------------------------------------------------------------------+ *+ * Command-line option facilities for frontend code+ *+ * Functions for converting shell-style patterns into simple lists of Oids for+ * database objects that match the patterns.+ *+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/fe_utils/option_utils.c+ *+ *-------------------------------------------------------------------------+ */+#include "postgres_fe.h"++#include "catalog/pg_class.h"+#include "common/connect.h"+#include "fe_utils/exit_utils.h"+#include "fe_utils/option_utils.h"+#include "fe_utils/query_utils.h"+#include "fe_utils/simple_list.h"+#include "fe_utils/string_utils.h"+#include "libpq-fe.h"+#include "pqexpbuffer.h"++/*+ * Find the OIDs of all schemas matching the given list of patterns,+ * and append them to the given OID list.+ */+void+expand_schema_name_patterns(PGconn *conn,+ SimpleStringList *patterns,+ SimpleOidList *oids,+ bool strict_names)+{+ PQExpBuffer query;+ PGresult *res;+ SimpleStringListCell *cell;+ int i;++ if (patterns->head == NULL)+ return; /* nothing to do */++ query = createPQExpBuffer();++ /*+ * The loop below runs multiple SELECTs might sometimes result in+ * duplicate entries in the OID list, but we don't care.+ */++ for (cell = patterns->head; cell; cell = cell->next)+ {+ appendPQExpBufferStr(query,+ "SELECT oid FROM pg_catalog.pg_namespace n\n");+ processSQLNamePattern(conn, query, cell->val, false,+ false, NULL, "n.nspname", NULL, NULL);++ res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);+ if (strict_names && PQntuples(res) == 0)+ fatal("no matching schemas were found for pattern \"%s\"", cell->val);++ for (i = 0; i < PQntuples(res); i++)+ {+ simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));+ }++ PQclear(res);+ resetPQExpBuffer(query);+ }++ destroyPQExpBuffer(query);+}++/*+ * Find the OIDs of all foreign servers matching the given list of patterns,+ * and append them to the given OID list.+ */+void+expand_foreign_server_name_patterns(PGconn *conn,+ SimpleStringList *patterns,+ SimpleOidList *oids)+{+ PQExpBuffer query;+ PGresult *res;+ SimpleStringListCell *cell;+ int i;++ if (patterns->head == NULL)+ return; /* nothing to do */++ query = createPQExpBuffer();++ /*+ * The loop below runs multiple SELECTs might sometimes result in+ * duplicate entries in the OID list, but we don't care.+ */++ for (cell = patterns->head; cell; cell = cell->next)+ {+ appendPQExpBufferStr(query,+ "SELECT oid FROM pg_catalog.pg_foreign_server s\n");+ processSQLNamePattern(conn, query, cell->val, false,+ false, NULL, "s.srvname", NULL, NULL);++ res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);+ if (PQntuples(res) == 0)+ fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);++ for (i = 0; i < PQntuples(res); i++)+ simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));++ PQclear(res);+ resetPQExpBuffer(query);+ }++ destroyPQExpBuffer(query);+}++/*+ * Find the OIDs of all tables matching the given list of patterns,+ * and append them to the given OID list.+ */+void+expand_table_name_patterns(PGconn *conn,+ SimpleStringList *patterns, SimpleOidList *oids,+ bool strict_names)+{+ PQExpBuffer query;+ PGresult *res;+ SimpleStringListCell *cell;+ int i;++ if (patterns->head == NULL)+ return; /* nothing to do */++ query = createPQExpBuffer();++ /*+ * this might sometimes result in duplicate entries in the OID list, but+ * we don't care.+ */++ for (cell = patterns->head; cell; cell = cell->next)+ {+ /*+ * Query must remain ABSOLUTELY devoid of unqualified names. This+ * would be unnecessary given a pg_table_is_visible() variant taking a+ * search_path argument.+ */+ appendPQExpBuffer(query,+ "SELECT c.oid"+ "\nFROM pg_catalog.pg_class c"+ "\n LEFT JOIN pg_catalog.pg_namespace n"+ "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"+ "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"+ "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",+ RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,+ RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,+ RELKIND_PARTITIONED_TABLE);+ processSQLNamePattern(conn, query, cell->val, true,+ false, "n.nspname", "c.relname", NULL,+ "pg_catalog.pg_table_is_visible(c.oid)");++ ExecuteSqlStatement(conn, "RESET search_path");+ res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);+ PQclear(ExecuteSqlQueryForSingleRow(conn,+ ALWAYS_SECURE_SEARCH_PATH_SQL));+ if (strict_names && PQntuples(res) == 0)+ fatal("no matching tables were found for pattern \"%s\"", cell->val);++ for (i = 0; i < PQntuples(res); i++)+ {+ simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));+ }++ PQclear(res);+ resetPQExpBuffer(query);+ }++ destroyPQExpBuffer(query);+}++/*+ * Find a list of database names that match the given patterns.+ */+void+expand_dbname_patterns(PGconn *conn,+ SimpleStringList *patterns,+ SimpleStringList *names)+{+ PQExpBuffer query;+ PGresult *res;++ if (patterns->head == NULL)+ return; /* nothing to do */++ query = createPQExpBuffer();++ /*+ * The loop below runs multiple SELECTs, which might sometimes result in+ * duplicate entries in the name list, but we don't care, since all we're+ * going to do is test membership of the list.+ */++ for (SimpleStringListCell *cell = patterns->head; cell; cell = cell->next)+ {+ appendPQExpBufferStr(query,+ "SELECT datname FROM pg_catalog.pg_database n\n");+ processSQLNamePattern(conn, query, cell->val, false,+ false, NULL, "datname", NULL, NULL);++ pg_log_info("executing %s", query->data);+ res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);+ for (int i = 0; i < PQntuples(res); i++)+ {+ simple_string_list_append(names, PQgetvalue(res, i, 0));+ }++ PQclear(res);+ resetPQExpBuffer(query);+ }++ destroyPQExpBuffer(query);+}diff --git a/src/include/fe_utils/option_utils.h b/src/include/fe_utils/option_utils.hnew file mode 100644index 0000000000..d626a0bbc9--- /dev/null+++ b/src/include/fe_utils/option_utils.h@@ -0,0 +1,35 @@+/*-------------------------------------------------------------------------+ *+ * Command-line option facilities for frontend code+ *+ * Functions for converting shell-style patterns into simple lists of Oids for+ * database objects that match the patterns.+ *+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group+ * Portions Copyright (c) 1994, Regents of the University of California+ *+ * src/include/fe_utils/option_utils.h+ *+ *-------------------------------------------------------------------------+ */+#ifndef OPTION_UTILS_H+#define OPTION_UTILS_H++#include "fe_utils/simple_list.h"+#include "libpq-fe.h"++extern void expand_schema_name_patterns(PGconn *conn,+ SimpleStringList *patterns,+ SimpleOidList *oids,+ bool strict_names);+extern void expand_foreign_server_name_patterns(PGconn *conn,+ SimpleStringList *patterns,+ SimpleOidList *oids);+extern void expand_table_name_patterns(PGconn *conn,+ SimpleStringList *patterns,+ SimpleOidList *oids,+ bool strict_names);+extern void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,+ SimpleStringList *names);++#endif /* OPTION_UTILS_H */--
2.21.1 (Apple Git-122.3)
=
v32-0008-Normalizing-option_utils-interface.patch (application/octet-stream, 14.5K) download
From 521ccf1daff979b9beb5bd08f395217175d5f948 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 6 Jan 2021 14:37:44 -0800
Subject: [PATCH v32 8/9] Normalizing option_utils interface.
The functions in option_utils were copied from pg_dump, mostly preserving
the function signatures. But the signatures and corresponding functionality
were originally written based solely on pg_dump's needs, not with the goal
of creating a consistent interface. Fixing that.
---
src/bin/pg_dump/pg_dump.c | 58 +++++++++++++++++++------
src/bin/pg_dump/pg_dumpall.c | 4 +-
src/fe_utils/option_utils.c | 66 ++++++++++++++++++++---------
src/fe_utils/string_utils.c | 50 ++++++++++++++++++++++
src/include/fe_utils/option_utils.h | 29 +++++++++----
src/include/fe_utils/string_utils.h | 6 +++
6 files changed, 169 insertions(+), 44 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.cindex c334b9e829..5c446c0f24 100644--- a/src/bin/pg_dump/pg_dump.c+++ b/src/bin/pg_dump/pg_dump.c@@ -125,6 +125,17 @@ static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
+/*+ * Cstring list of relkinds which qualify as tables for our purposes when+ * processing table inclusion or exclusion patterns.+ */+#define TABLE_RELKIND_LIST CppAsString2(RELKIND_RELATION) ", " \+ CppAsString2(RELKIND_SEQUENCE) ", " \+ CppAsString2(RELKIND_VIEW) ", " \+ CppAsString2(RELKIND_MATVIEW) ", " \+ CppAsString2(RELKIND_FOREIGN_TABLE) ", " \+ CppAsString2(RELKIND_PARTITIONED_TABLE)+
static const CatalogId nilCatalogId = {0, 0};
/* override for standard extra_float_digits setting */
@@ -791,6 +802,7 @@ main(int argc, char **argv)
{
expand_schema_name_patterns(GetConnection(fout),
&schema_include_patterns,
+ NULL,
&schema_include_oids,
strict_names);
if (schema_include_oids.head == NULL)
@@ -798,6 +810,7 @@ main(int argc, char **argv)
}
expand_schema_name_patterns(GetConnection(fout),
&schema_exclude_patterns,
+ NULL,
&schema_exclude_oids,
false);
/* non-matching exclusion patterns aren't an error */
@@ -805,26 +818,43 @@ main(int argc, char **argv)
/* Expand table selection patterns into OID lists */
if (table_include_patterns.head != NULL)
{
- expand_table_name_patterns(GetConnection(fout),- &table_include_patterns,- &table_include_oids,- strict_names);+ expand_rel_name_patterns(GetConnection(fout),+ &table_include_patterns,+ NULL,+ NULL,+ TABLE_RELKIND_LIST,+ AMTYPE_TABLE,+ &table_include_oids,+ strict_names,+ true);
if (table_include_oids.head == NULL)
fatal("no matching tables were found");
}
- expand_table_name_patterns(GetConnection(fout),- &table_exclude_patterns,- &table_exclude_oids,- false);-- expand_table_name_patterns(GetConnection(fout),- &tabledata_exclude_patterns,- &tabledata_exclude_oids,- false);+ expand_rel_name_patterns(GetConnection(fout),+ &table_exclude_patterns,+ NULL,+ NULL,+ TABLE_RELKIND_LIST,+ AMTYPE_TABLE,+ &table_exclude_oids,+ false,+ true);++ expand_rel_name_patterns(GetConnection(fout),+ &tabledata_exclude_patterns,+ NULL,+ NULL,+ TABLE_RELKIND_LIST,+ AMTYPE_TABLE,+ &tabledata_exclude_oids,+ false,+ true);
expand_foreign_server_name_patterns(GetConnection(fout),
&foreign_servers_include_patterns,
- &foreign_servers_include_oids);+ NULL,+ &foreign_servers_include_oids,+ true);
/* non-matching exclusion patterns aren't an error */
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.cindex 01db15dfda..2b3a4e3349 100644--- a/src/bin/pg_dump/pg_dumpall.c+++ b/src/bin/pg_dump/pg_dumpall.c@@ -471,8 +471,8 @@ main(int argc, char *argv[])
/*
* Get a list of database names that match the exclude patterns
*/
- expand_dbname_patterns(conn, &database_exclude_patterns,- &database_exclude_names);+ expand_dbname_patterns(conn, &database_exclude_patterns, NULL,+ &database_exclude_names, false);
/*
* Open the output file if required, otherwise use stdout
diff --git a/src/fe_utils/option_utils.c b/src/fe_utils/option_utils.cindex 7893df77aa..76ca456784 100644--- a/src/fe_utils/option_utils.c+++ b/src/fe_utils/option_utils.c@@ -14,6 +14,7 @@
*/
#include "postgres_fe.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_class.h"
#include "common/connect.h"
#include "fe_utils/exit_utils.h"
@@ -30,7 +31,8 @@
*/
void
expand_schema_name_patterns(PGconn *conn,
- SimpleStringList *patterns,+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_oids,
SimpleOidList *oids,
bool strict_names)
{
@@ -55,6 +57,7 @@ expand_schema_name_patterns(PGconn *conn,
"SELECT oid FROM pg_catalog.pg_namespace n\n");
processSQLNamePattern(conn, query, cell->val, false,
false, NULL, "n.nspname", NULL, NULL);
+ exclude_filter(query, "n.oid", exclude_oids);
res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);
if (strict_names && PQntuples(res) == 0)
@@ -78,8 +81,10 @@ expand_schema_name_patterns(PGconn *conn,
*/
void
expand_foreign_server_name_patterns(PGconn *conn,
- SimpleStringList *patterns,- SimpleOidList *oids)+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_oids,+ SimpleOidList *oids,+ bool strict_names)
{
PQExpBuffer query;
PGresult *res;
@@ -102,9 +107,10 @@ expand_foreign_server_name_patterns(PGconn *conn,
"SELECT oid FROM pg_catalog.pg_foreign_server s\n");
processSQLNamePattern(conn, query, cell->val, false,
false, NULL, "s.srvname", NULL, NULL);
+ exclude_filter(query, "s.oid", exclude_oids);
res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);
- if (PQntuples(res) == 0)+ if (strict_names && PQntuples(res) == 0)
fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
for (i = 0; i < PQntuples(res); i++)
@@ -118,18 +124,32 @@ expand_foreign_server_name_patterns(PGconn *conn,
}
/*
- * Find the OIDs of all tables matching the given list of patterns,- * and append them to the given OID list.+ * Find the OIDs of all relations matching the given list of patterns+ * and restrictions, and append them to the given OID list.
*/
void
-expand_table_name_patterns(PGconn *conn,- SimpleStringList *patterns, SimpleOidList *oids,- bool strict_names)+expand_rel_name_patterns(PGconn *conn,+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_nsp_oids,+ const SimpleOidList *exclude_oids,+ const char *relkinds,+ char amtype,+ SimpleOidList *oids,+ bool strict_names,+ bool restrict_visible)
{
PQExpBuffer query;
PGresult *res;
SimpleStringListCell *cell;
int i;
+ const char *visibility_rule;++ Assert(amtype == AMTYPE_TABLE || amtype == AMTYPE_INDEX);++ if (restrict_visible)+ visibility_rule = "pg_catalog.pg_table_is_visible(c.oid)";+ else+ visibility_rule = NULL;
if (patterns->head == NULL)
return; /* nothing to do */
@@ -154,20 +174,22 @@ expand_table_name_patterns(PGconn *conn,
"\n LEFT JOIN pg_catalog.pg_namespace n"
"\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
"\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
- "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",- RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,- RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,- RELKIND_PARTITIONED_TABLE);+ "\n (array[%s])\n", relkinds);
processSQLNamePattern(conn, query, cell->val, true,
- false, "n.nspname", "c.relname", NULL,- "pg_catalog.pg_table_is_visible(c.oid)");+ false, "n.nspname", "c.relname", NULL, visibility_rule);+ exclude_filter(query, "n.oid", exclude_nsp_oids);+ exclude_filter(query, "c.oid", exclude_oids);
ExecuteSqlStatement(conn, "RESET search_path");
res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);
PQclear(ExecuteSqlQueryForSingleRow(conn,
ALWAYS_SECURE_SEARCH_PATH_SQL));
if (strict_names && PQntuples(res) == 0)
- fatal("no matching tables were found for pattern \"%s\"", cell->val);+ {+ if (amtype == AMTYPE_TABLE)+ fatal("no matching tables were found for pattern \"%s\"", cell->val);+ fatal("no matching indexes were found for pattern \"%s\"", cell->val);+ }
for (i = 0; i < PQntuples(res); i++)
{
@@ -186,8 +208,10 @@ expand_table_name_patterns(PGconn *conn,
*/
void
expand_dbname_patterns(PGconn *conn,
- SimpleStringList *patterns,- SimpleStringList *names)+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_oids,+ SimpleStringList *names,+ bool strict_names)
{
PQExpBuffer query;
PGresult *res;
@@ -206,12 +230,16 @@ expand_dbname_patterns(PGconn *conn,
for (SimpleStringListCell *cell = patterns->head; cell; cell = cell->next)
{
appendPQExpBufferStr(query,
- "SELECT datname FROM pg_catalog.pg_database n\n");+ "SELECT datname FROM pg_catalog.pg_database d\n");
processSQLNamePattern(conn, query, cell->val, false,
false, NULL, "datname", NULL, NULL);
+ exclude_filter(query, "d.oid", exclude_oids);
pg_log_info("executing %s", query->data);
res = ExecuteSqlQuery(conn, query->data, PGRES_TUPLES_OK);
+ if (strict_names && PQntuples(res) == 0)+ fatal("no matching databases were found for pattern \"%s\"", cell->val);+
for (int i = 0; i < PQntuples(res); i++)
{
simple_string_list_append(names, PQgetvalue(res, i, 0));
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.cindex a1a9d691d5..4e57a6f940 100644--- a/src/fe_utils/string_utils.c+++ b/src/fe_utils/string_utils.c@@ -797,6 +797,56 @@ appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
return true;
}
+/*+ * Internal implementation of include_filter and exclude_filter.+ */+static void+apply_filter(PQExpBuffer querybuf, const char *lval, const SimpleOidList *oids,+ bool include)+{+ const SimpleOidListCell *cell;+ const char *comma;++ if (!oids || !oids->head)+ return;++ if (include)+ appendPQExpBuffer(querybuf, "\nAND %s OPERATOR(pg_catalog.=) ANY(array[", lval);+ else+ appendPQExpBuffer(querybuf, "\nAND %s OPERATOR(pg_catalog.!=) ALL(array[", lval);++ for (comma = "", cell = oids->head; cell; comma = ", ", cell = cell->next)+ appendPQExpBuffer(querybuf, "%s%u", comma, cell->val);+ appendPQExpBuffer(querybuf, "]::OID[])");+}++/*+ * Conditionally add a restriction to a query such that lval must be an Oid in+ * the given list of Oids, except that for a null or empty oids list argument,+ * no filtering is done and we return without having modified the query buffer.+ *+ * The query argument must already have begun the WHERE clause and must be in a+ * state where we can append an AND clause. No checking of this requirement is+ * done here.+ *+ * On return, the query buffer will be extended with an AND clause that filters+ * only those rows where the lval is an Oid present in the given list of oids.+ */+void+include_filter(PQExpBuffer querybuf, const char *lval, const SimpleOidList *oids)+{+ apply_filter(querybuf, lval, oids, true);+}++/*+ * Same as include_filter, above, except that for a non-null, non-empty oids+ * list, the lval is restricted to not be any of the values in the list.+ */+void+exclude_filter(PQExpBuffer querybuf, const char *lval, const SimpleOidList *oids)+{+ apply_filter(querybuf, lval, oids, false);+}
/*
* processSQLNamePattern
diff --git a/src/include/fe_utils/option_utils.h b/src/include/fe_utils/option_utils.hindex d626a0bbc9..53da30754f 100644--- a/src/include/fe_utils/option_utils.h+++ b/src/include/fe_utils/option_utils.h@@ -19,17 +19,28 @@
#include "libpq-fe.h"
extern void expand_schema_name_patterns(PGconn *conn,
- SimpleStringList *patterns,+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_oids,
SimpleOidList *oids,
bool strict_names);
extern void expand_foreign_server_name_patterns(PGconn *conn,
- SimpleStringList *patterns,- SimpleOidList *oids);-extern void expand_table_name_patterns(PGconn *conn,- SimpleStringList *patterns,- SimpleOidList *oids,- bool strict_names);-extern void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,- SimpleStringList *names);+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_oids,+ SimpleOidList *oids,+ bool strict_names);+extern void expand_rel_name_patterns(PGconn *conn,+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_nsp_oids,+ const SimpleOidList *exclude_oids,+ const char *relkinds,+ char amtype,+ SimpleOidList *oids,+ bool strict_names,+ bool restrict_visible);+extern void expand_dbname_patterns(PGconn *conn,+ const SimpleStringList *patterns,+ const SimpleOidList *exclude_oids,+ SimpleStringList *names,+ bool strict_names);
#endif /* OPTION_UTILS_H */
diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.hindex c290c302f5..301a8eef4d 100644--- a/src/include/fe_utils/string_utils.h+++ b/src/include/fe_utils/string_utils.h@@ -16,6 +16,7 @@
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
+#include "fe_utils/simple_list.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
@@ -50,6 +51,11 @@ extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
extern bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
const char *prefix, int encoding, bool std_strings);
+extern void include_filter(PQExpBuffer querybuf, const char *lval,+ const SimpleOidList *oids);+extern void exclude_filter(PQExpBuffer querybuf, const char *lval,+ const SimpleOidList *oids);+
extern bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf,
const char *pattern,
bool have_where, bool force_escape,
--
2.21.1 (Apple Git-122.3)
=
v32-0009-Adding-contrib-module-pg_amcheck.patch (application/octet-stream, 83.8K) download
From 01267b3ca3cf9651cd698342fe383fbe00028fde Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 6 Jan 2021 15:47:08 -0800
Subject: [PATCH v32 9/9] Adding contrib module pg_amcheck
Adding new contrib module pg_amcheck, which is a command line
interface for running amcheck's verifications against tables and
indexes.
---
contrib/Makefile | 1 +
contrib/pg_amcheck/.gitignore | 3 +
contrib/pg_amcheck/Makefile | 29 +
contrib/pg_amcheck/pg_amcheck.c | 886 +++++++++++++++++++++
contrib/pg_amcheck/pg_amcheck.control | 5 +
contrib/pg_amcheck/t/001_basic.pl | 9 +
contrib/pg_amcheck/t/002_nonesuch.pl | 60 ++
contrib/pg_amcheck/t/003_check.pl | 248 ++++++
contrib/pg_amcheck/t/004_verify_heapam.pl | 496 ++++++++++++
contrib/pg_amcheck/t/005_opclass_damage.pl | 52 ++
doc/src/sgml/contrib.sgml | 1 +
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/pgamcheck.sgml | 493 ++++++++++++
src/fe_utils/query_utils.c | 8 +-
src/tools/msvc/Mkvcbuild.pm | 10 +-
src/tools/pgindent/typedefs.list | 2 +
16 files changed, 2295 insertions(+), 9 deletions(-)
create mode 100644 contrib/pg_amcheck/.gitignore
create mode 100644 contrib/pg_amcheck/Makefile
create mode 100644 contrib/pg_amcheck/pg_amcheck.c
create mode 100644 contrib/pg_amcheck/pg_amcheck.control
create mode 100644 contrib/pg_amcheck/t/001_basic.pl
create mode 100644 contrib/pg_amcheck/t/002_nonesuch.pl
create mode 100644 contrib/pg_amcheck/t/003_check.pl
create mode 100644 contrib/pg_amcheck/t/004_verify_heapam.pl
create mode 100644 contrib/pg_amcheck/t/005_opclass_damage.pl
create mode 100644 doc/src/sgml/pgamcheck.sgml
diff --git a/contrib/Makefile b/contrib/Makefileindex 7a4866e338..0fd4125902 100644--- a/contrib/Makefile+++ b/contrib/Makefile@@ -30,6 +30,7 @@ SUBDIRS = \
old_snapshot \
pageinspect \
passwordcheck \
+ pg_amcheck \
pg_buffercache \
pg_freespacemap \
pg_prewarm \
diff --git a/contrib/pg_amcheck/.gitignore b/contrib/pg_amcheck/.gitignorenew file mode 100644index 0000000000..c21a14de31--- /dev/null+++ b/contrib/pg_amcheck/.gitignore@@ -0,0 +1,3 @@+pg_amcheck++/tmp_check/diff --git a/contrib/pg_amcheck/Makefile b/contrib/pg_amcheck/Makefilenew file mode 100644index 0000000000..bc61ee7970--- /dev/null+++ b/contrib/pg_amcheck/Makefile@@ -0,0 +1,29 @@+# contrib/pg_amcheck/Makefile++PGFILEDESC = "pg_amcheck - detects corruption within database relations"+PGAPPICON = win32++PROGRAM = pg_amcheck+OBJS = \+ $(WIN32RES) \+ pg_amcheck.o++REGRESS_OPTS += --load-extension=amcheck --load-extension=pageinspect+EXTRA_INSTALL += contrib/amcheck contrib/pageinspect++TAP_TESTS = 1++PG_CPPFLAGS = -I$(libpq_srcdir)+PG_LIBS_INTERNAL = -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)++ifdef USE_PGXS+PG_CONFIG = pg_config+PGXS := $(shell $(PG_CONFIG) --pgxs)+include $(PGXS)+else+SHLIB_PREREQS = submake-libpq+subdir = contrib/pg_amcheck+top_builddir = ../..+include $(top_builddir)/src/Makefile.global+include $(top_srcdir)/contrib/contrib-global.mk+endifdiff --git a/contrib/pg_amcheck/pg_amcheck.c b/contrib/pg_amcheck/pg_amcheck.cnew file mode 100644index 0000000000..97df1d1074--- /dev/null+++ b/contrib/pg_amcheck/pg_amcheck.c@@ -0,0 +1,886 @@+/*-------------------------------------------------------------------------+ *+ * pg_amcheck.c+ * Detects corruption within database relations.+ *+ * Copyright (c) 2017-2020, PostgreSQL Global Development Group+ *+ * IDENTIFICATION+ * contrib/pg_amcheck/pg_amcheck.c+ *+ *-------------------------------------------------------------------------+ */+#include "postgres_fe.h"++#include "catalog/pg_am.h"+#include "catalog/pg_class.h"+#include "common/connect.h"+#include "common/logging.h"+#include "common/string.h"+#include "common/username.h"+#include "fe_utils/exit_utils.h"+#include "fe_utils/option_utils.h"+#include "fe_utils/print.h"+#include "fe_utils/query_utils.h"+#include "fe_utils/simple_list.h"+#include "fe_utils/string_utils.h"+#include "getopt_long.h"+#include "pg_getopt.h"+#include "storage/block.h"++typedef struct ConnectOptions+{+ char *dbname;+ char *host;+ char *port;+ char *username;+} ConnectOptions;++typedef enum trivalue+{+ TRI_DEFAULT,+ TRI_NO,+ TRI_YES+} trivalue;++typedef struct+{+ bool notty; /* stdin or stdout is not a tty (as determined+ * on startup) */+ trivalue getPassword; /* prompt for a username and password */+ const char *progname; /* in case you renamed pg_amcheck */+ bool strict_names; /* The specified names/patterns should to+ * match at least one entity */+ bool on_error_stop; /* The checking of each table should stop+ * after the first corrupt page is found. */+ bool skip_frozen; /* Do not check pages marked all frozen */+ bool skip_visible; /* Do not check pages marked all visible */+ bool check_indexes; /* Check btree indexes */+ bool check_toast; /* Check associated toast tables and indexes */+ bool check_corrupt; /* Check indexes even if table is corrupt */+ bool heapallindexed; /* Perform index to table reconciling checks */+ bool rootdescend; /* Perform index rootdescend checks */+ bool verbose;+ long startblock; /* Block number where checking begins */+ long endblock; /* Block number where checking ends, inclusive */+} AmCheckSettings;++static AmCheckSettings settings;++/* Connection to backend */+static PGconn *conn;++/*+ * Object inclusion/exclusion lists+ *+ * The string lists record the patterns given by command-line switches,+ * which we then convert to lists of Oids of matching objects.+ */+static SimpleStringList schema_include_patterns = {NULL, NULL};+static SimpleOidList schema_include_oids = {NULL, NULL};+static SimpleStringList schema_exclude_patterns = {NULL, NULL};+static SimpleOidList schema_exclude_oids = {NULL, NULL};++static SimpleStringList table_include_patterns = {NULL, NULL};+static SimpleOidList table_include_oids = {NULL, NULL};+static SimpleStringList table_exclude_patterns = {NULL, NULL};+static SimpleOidList table_exclude_oids = {NULL, NULL};++static SimpleStringList index_include_patterns = {NULL, NULL};+static SimpleOidList index_include_oids = {NULL, NULL};+static SimpleStringList index_exclude_patterns = {NULL, NULL};+static SimpleOidList index_exclude_oids = {NULL, NULL};++/*+ * Cstring list of relkinds which qualify as tables for our purposes when+ * processing table inclusion or exclusion patterns.+ */+#define TABLE_RELKIND_LIST CppAsString2(RELKIND_RELATION) ", " \+ CppAsString2(RELKIND_MATVIEW) ", " \+ CppAsString2(RELKIND_PARTITIONED_TABLE)++#define INDEX_RELKIND_LIST CppAsString2(RELKIND_INDEX)++/*+ * List of main tables to be checked, compiled from above lists, and+ * corresponding list of toast tables. The lists should always be+ * the same length, with InvalidOid in the toastlist for main relations+ * without a corresponding toast relation.+ */+static SimpleOidList mainlist = {NULL, NULL};+static SimpleOidList toastlist = {NULL, NULL};+++/*+ * Functions for running the various corruption checks.+ */+static void check_tables(SimpleOidList *checklist);+static uint64 check_table(Oid tbloid, long startblock, long endblock,+ bool on_error_stop, bool check_toast);+static uint64 check_indexes(Oid tbloid, const SimpleOidList *include_oids,+ const SimpleOidList *exclude_oids);+static uint64 check_index(const char *idxoid, const char *idxname,+ const char *tblname);++/*+ * Functions implementing standard command line behaviors.+ */+static void parse_cli_options(int argc, char *argv[],+ ConnectOptions *connOpts);+static void usage(void);+static void showVersion(void);+static void NoticeProcessor(void *arg, const char *message);++static void get_table_check_list(const SimpleOidList *include_nsp,+ const SimpleOidList *exclude_nsp,+ const SimpleOidList *include_tbl,+ const SimpleOidList *exclude_tbl);++#define EXIT_BADCONN 2++int+main(int argc, char **argv)+{+ ConnectOptions connOpts;+ bool have_password = false;+ char *password = NULL;+ bool new_pass;++ pg_logging_init(argv[0]);+ set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_amcheck"));++ if (argc > 1)+ {+ if ((strcmp(argv[1], "-?") == 0) ||+ (argc == 2 && (strcmp(argv[1], "--help") == 0)))+ {+ usage();+ exit(EXIT_SUCCESS);+ }+ if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)+ {+ showVersion();+ exit(EXIT_SUCCESS);+ }+ }++ memset(&settings, 0, sizeof(settings));+ settings.progname = get_progname(argv[0]);++ conn = NULL;+ setDecimalLocale();++ settings.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));++ settings.getPassword = TRI_DEFAULT;++ settings.startblock = -1;+ settings.endblock = -1;++ /*+ * Default behaviors for user settable options. Note that these default+ * to doing all the safe checks and none of the unsafe ones, on the theory+ * that if a user says "pg_amcheck mydb" without specifying any additional+ * options, we should check everything we know how to check without+ * risking any backend aborts.+ */++ settings.on_error_stop = false;+ settings.skip_frozen = false;+ settings.skip_visible = false;++ /* Index checking options */+ settings.check_indexes = true;+ settings.check_corrupt = true;+ settings.heapallindexed = false;+ settings.rootdescend = false;++ /*+ * Reconciling toasted attributes from the main table against the toast+ * table can crash the backend if the toast table or index are corrupt. We+ * can optionally check the toast table and then the toast index prior to+ * checking the main table, but if the toast table or index are+ * concurrently corrupted after we conclude they are valid, the check of+ * the main table can crash the backend. The oneous is on any caller who+ * enables this option to make certain the environment is sufficiently+ * stable that concurrent corruptions of the toast is not possible.+ */+ settings.check_toast = false;++ parse_cli_options(argc, argv, &connOpts);++ if (settings.getPassword == TRI_YES)+ {+ /*+ * We can't be sure yet of the username that will be used, so don't+ * offer a potentially wrong one. Typical uses of this option are+ * noninteractive anyway.+ */+ password = simple_prompt("Password: ", false);+ have_password = true;+ }++ /* loop until we have a password if requested by backend */+ do+ {+#define ARRAY_SIZE 8+ const char **keywords = pg_malloc(ARRAY_SIZE * sizeof(*keywords));+ const char **values = pg_malloc(ARRAY_SIZE * sizeof(*values));++ keywords[0] = "host";+ values[0] = connOpts.host;+ keywords[1] = "port";+ values[1] = connOpts.port;+ keywords[2] = "user";+ values[2] = connOpts.username;+ keywords[3] = "password";+ values[3] = have_password ? password : NULL;+ keywords[4] = "dbname"; /* see do_connect() */+ if (connOpts.dbname == NULL)+ {+ if (getenv("PGDATABASE"))+ values[4] = getenv("PGDATABASE");+ else if (getenv("PGUSER"))+ values[4] = getenv("PGUSER");+ else+ values[4] = "postgres";+ }+ else+ values[4] = connOpts.dbname;+ keywords[5] = "fallback_application_name";+ values[5] = settings.progname;+ keywords[6] = "client_encoding";+ values[6] = (settings.notty ||+ getenv("PGCLIENTENCODING")) ? NULL : "auto";+ keywords[7] = NULL;+ values[7] = NULL;++ new_pass = false;+ conn = PQconnectdbParams(keywords, values, true);+ if (!conn)+ fatal("could not connect to database %s: out of memory", values[4]);++ free(keywords);+ free(values);++ if (PQstatus(conn) == CONNECTION_BAD &&+ PQconnectionNeedsPassword(conn) &&+ !have_password &&+ settings.getPassword != TRI_NO)+ {+ /*+ * Before closing the old PGconn, extract the user name that was+ * actually connected with.+ */+ const char *realusername = PQuser(conn);+ char *password_prompt;++ if (realusername && realusername[0])+ password_prompt = psprintf("Password for user %s: ",+ realusername);+ else+ password_prompt = pg_strdup("Password: ");+ PQfinish(conn);++ password = simple_prompt(password_prompt, false);+ free(password_prompt);+ have_password = true;+ new_pass = true;+ }++ if (!new_pass && PQstatus(conn) == CONNECTION_BAD)+ {+ pg_log_error("could not connect to database %s: %s",+ values[4], PQerrorMessage(conn));+ PQfinish(conn);+ exit(1);+ }+ } while (new_pass);++ if (settings.verbose)+ PQsetErrorVerbosity(conn, PQERRORS_VERBOSE);++ /*+ * Expand schema, table, and index exclusion patterns, if any. Note that+ * non-matching exclusion patterns are not an error, even when+ * --strict-names was specified.+ */+ expand_schema_name_patterns(conn,+ &schema_exclude_patterns,+ NULL,+ &schema_exclude_oids,+ false);+ expand_rel_name_patterns(conn,+ &table_exclude_patterns,+ NULL,+ NULL,+ TABLE_RELKIND_LIST,+ AMTYPE_TABLE,+ &table_exclude_oids,+ false,+ false);+ expand_rel_name_patterns(conn,+ &index_exclude_patterns,+ NULL,+ NULL,+ INDEX_RELKIND_LIST,+ AMTYPE_INDEX,+ &index_exclude_oids,+ false,+ false);++ /* Expand schema selection patterns into Oid lists */+ if (schema_include_patterns.head != NULL)+ {+ expand_schema_name_patterns(conn,+ &schema_include_patterns,+ &schema_exclude_oids,+ &schema_include_oids,+ settings.strict_names);+ if (schema_include_oids.head == NULL)+ fatal("no matching schemas were found");+ }++ /* Expand table selection patterns into Oid lists */+ if (table_include_patterns.head != NULL)+ {+ expand_rel_name_patterns(conn,+ &table_include_patterns,+ &schema_exclude_oids,+ &table_exclude_oids,+ TABLE_RELKIND_LIST,+ AMTYPE_TABLE,+ &table_include_oids,+ settings.strict_names,+ false);+ if (table_include_oids.head == NULL)+ fatal("no matching tables were found");+ }++ /* Expand index selection patterns into Oid lists */+ if (index_include_patterns.head != NULL)+ {+ expand_rel_name_patterns(conn,+ &index_include_patterns,+ &schema_exclude_oids,+ &index_exclude_oids,+ INDEX_RELKIND_LIST,+ AMTYPE_INDEX,+ &index_include_oids,+ settings.strict_names,+ false);+ if (index_include_oids.head == NULL)+ fatal("no matching indexes were found");+ }++ /*+ * Compile list of all tables to be checked based on namespace and table+ * includes and excludes.+ */+ get_table_check_list(&schema_include_oids, &schema_exclude_oids,+ &table_include_oids, &table_exclude_oids);++ PQsetNoticeProcessor(conn, NoticeProcessor, NULL);++ if (settings.check_toast)+ check_tables(&toastlist);+ check_tables(&mainlist);++ return 0;+}++/*+ * Check each table from the given checklist per the user specified options.+ */+static void+check_tables(SimpleOidList *checklist)+{+ const SimpleOidListCell *cell;++ for (cell = checklist->head; cell; cell = cell->next)+ {+ uint64 corruptions = 0;++ if (!OidIsValid(cell->val))+ continue;++ corruptions = check_table(cell->val,+ settings.startblock,+ settings.endblock,+ settings.on_error_stop,+ settings.check_toast);++ if (settings.check_indexes)+ {+ /* Optionally skip the index checks for a corrupt table. */+ if (corruptions && !settings.check_corrupt)+ continue;++ corruptions += check_indexes(cell->val,+ &index_include_oids,+ &index_exclude_oids);+ }+ }+}++/*+ * Checks the given table for corruption, returning the number of corruptions+ * detected and printed to the user.+ */+static uint64+check_table(Oid tbloid, long startblock, long endblock,+ bool on_error_stop, bool check_toast)+{+ PQExpBuffer querybuf;+ PGresult *res;+ int i;+ char *skip;+ char *toast;+ const char *stop;+ uint64 corruption_cnt = 0;++ if (settings.skip_frozen)+ skip = pg_strdup("'all frozen'");+ else if (settings.skip_visible)+ skip = pg_strdup("'all visible'");+ else+ skip = pg_strdup("'none'");+ stop = on_error_stop ? "true" : "false";+ toast = check_toast ? "true" : "false";++ querybuf = createPQExpBuffer();++ appendPQExpBuffer(querybuf,+ "SELECT c.relname, v.blkno, v.offnum, v.attnum, v.msg "+ "FROM public.verify_heapam("+ "relation := %u, "+ "on_error_stop := %s, "+ "skip := %s, "+ "check_toast := %s, ",+ tbloid, stop, skip, toast);+ if (startblock < 0)+ appendPQExpBuffer(querybuf, "startblock := NULL, ");+ else+ appendPQExpBuffer(querybuf, "startblock := %ld, ", startblock);++ if (endblock < 0)+ appendPQExpBuffer(querybuf, "endblock := NULL");+ else+ appendPQExpBuffer(querybuf, "endblock := %ld", endblock);++ appendPQExpBuffer(querybuf, ") v, pg_catalog.pg_class c "+ "WHERE c.oid = %u", tbloid);++ res = PQexec(conn, querybuf->data);+ if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) > 0)+ {+ corruption_cnt += PQntuples(res);+ for (i = 0; i < PQntuples(res); i++)+ {+ if (!PQgetisnull(res, i, 3))+ printf("relation %s, block %s, offset %s, attribute %s\n %s\n",+ PQgetvalue(res, i, 0), /* relname */+ PQgetvalue(res, i, 1), /* blkno */+ PQgetvalue(res, i, 2), /* offnum */+ PQgetvalue(res, i, 3), /* attnum */+ PQgetvalue(res, i, 4)); /* msg */+ else if (!PQgetisnull(res, i, 2))+ printf("relation %s, block %s, offset %s\n %s\n",+ PQgetvalue(res, i, 0), /* relname */+ PQgetvalue(res, i, 1), /* blkno */+ PQgetvalue(res, i, 2), /* offnum */+ PQgetvalue(res, i, 4)); /* msg */+ else if (!PQgetisnull(res, i, 1))+ printf("relation %s, block %s\n %s\n",+ PQgetvalue(res, i, 0), /* relname */+ PQgetvalue(res, i, 1), /* blkno */+ PQgetvalue(res, i, 4)); /* msg */+ else if (!PQgetisnull(res, i, 0))+ printf("relation %s\n %s\n",+ PQgetvalue(res, i, 0), /* relname */+ PQgetvalue(res, i, 4)); /* msg */+ else+ printf("%s\n", PQgetvalue(res, i, 4)); /* msg */+ }+ }+ else if (PQresultStatus(res) != PGRES_TUPLES_OK)+ {+ corruption_cnt++;+ printf("relation with OID %u\n %s\n", tbloid, PQerrorMessage(conn));+ }++ PQclear(res);+ destroyPQExpBuffer(querybuf);+ return corruption_cnt;+}++static uint64+check_indexes(Oid tbloid, const SimpleOidList *include_oids,+ const SimpleOidList *exclude_oids)+{+ PQExpBuffer querybuf;+ PGresult *res;+ int i;+ uint64 corruption_cnt = 0;++ querybuf = createPQExpBuffer();+ appendPQExpBuffer(querybuf,+ "SELECT i.indexrelid, ci.relname, ct.relname"+ "\nFROM pg_catalog.pg_index i, pg_catalog.pg_class ci, "+ "pg_catalog.pg_class ct"+ "\nWHERE i.indexrelid = ci.oid"+ "\n AND i.indrelid = ct.oid"+ "\n AND ci.relam = %u"+ "\n AND i.indrelid = %u",+ BTREE_AM_OID, tbloid);+ include_filter(querybuf, "i.indexrelid", include_oids);+ exclude_filter(querybuf, "i.indexrelid", exclude_oids);++ res = PQexec(conn, querybuf->data);+ if (PQresultStatus(res) == PGRES_TUPLES_OK)+ {+ for (i = 0; i < PQntuples(res); i++)+ corruption_cnt += check_index(PQgetvalue(res, i, 0),+ PQgetvalue(res, i, 1),+ PQgetvalue(res, i, 2));+ }+ else+ {+ corruption_cnt++;+ printf("%s\n", PQerrorMessage(conn));+ }++ PQclear(res);+ destroyPQExpBuffer(querybuf);++ return corruption_cnt;+}++static uint64+check_index(const char *idxoid, const char *idxname, const char *tblname)+{+ PQExpBuffer querybuf;+ PGresult *res;+ uint64 corruption_cnt = 0;++ querybuf = createPQExpBuffer();+ appendPQExpBuffer(querybuf,+ "SELECT public.bt_index_parent_check('%s'::regclass, %s, %s)",+ idxoid,+ settings.heapallindexed ? "true" : "false",+ settings.rootdescend ? "true" : "false");+ res = PQexec(conn, querybuf->data);+ if (PQresultStatus(res) != PGRES_TUPLES_OK)+ {+ corruption_cnt++;+ printf("index check failed for index %s of table %s:\n",+ idxname, tblname);+ printf("%s", PQerrorMessage(conn));+ }++ PQclear(res);+ destroyPQExpBuffer(querybuf);++ return corruption_cnt;+}++static void+parse_cli_options(int argc, char *argv[], ConnectOptions *connOpts)+{+ static struct option long_options[] =+ {+ {"check-toast", no_argument, NULL, 'z'},+ {"dbname", required_argument, NULL, 'd'},+ {"endblock", required_argument, NULL, 'e'},+ {"exclude-index", required_argument, NULL, 'I'},+ {"exclude-schema", required_argument, NULL, 'N'},+ {"exclude-table", required_argument, NULL, 'T'},+ {"heapallindexed", no_argument, NULL, 'a'},+ {"help", optional_argument, NULL, '?'},+ {"host", required_argument, NULL, 'h'},+ {"index", required_argument, NULL, 'i'},+ {"no-password", no_argument, NULL, 'w'},+ {"on-error-stop", no_argument, NULL, 'o'},+ {"password", no_argument, NULL, 'W'},+ {"port", required_argument, NULL, 'p'},+ {"rootdescend", no_argument, NULL, 'r'},+ {"schema", required_argument, NULL, 'n'},+ {"skip", required_argument, NULL, 'S'},+ {"skip-corrupt", no_argument, NULL, 'C'},+ {"skip-indexes", no_argument, NULL, 'X'},+ {"skip-toast", no_argument, NULL, 'Z'},+ {"startblock", required_argument, NULL, 'b'},+ {"strict-names", no_argument, NULL, 's'},+ {"table", required_argument, NULL, 't'},+ {"toast-endblock", required_argument, NULL, 'E'},+ {"toast-startblock", required_argument, NULL, 'B'},+ {"username", required_argument, NULL, 'U'},+ {"verbose", no_argument, NULL, 'v'},+ {"version", no_argument, NULL, 'V'},+ {NULL, 0, NULL, 0}+ };++ int optindex;+ int c;++ memset(connOpts, 0, sizeof *connOpts);++ while ((c = getopt_long(argc, argv, "ab:Cd:e:h:i:I:n:N:op:rsS:t:T:U:vVwWXzZ?1",+ long_options, &optindex)) != -1)+ {+ char *endptr;++ switch (c)+ {+ case 'a':+ settings.heapallindexed = true;+ break;+ case 'b':+ settings.startblock = strtol(optarg, &endptr, 10);+ if (*endptr != '\0')+ fatal("relation starting block argument contains garbage characters");+ if (settings.startblock > (long) MaxBlockNumber)+ fatal("relation starting block argument out of bounds");+ break;+ case 'C':+ settings.check_corrupt = false;+ break;+ case 'd':+ connOpts->dbname = pg_strdup(optarg);+ break;+ case 'e':+ settings.endblock = strtol(optarg, &endptr, 10);+ if (*endptr != '\0')+ fatal("relation ending block argument contains garbage characters");+ if (settings.endblock > (long) MaxBlockNumber)+ fatal("relation ending block argument out of bounds");+ break;+ case 'h':+ connOpts->host = pg_strdup(optarg);+ break;+ case 'i':+ simple_string_list_append(&index_include_patterns, optarg);+ break;+ case 'I':+ simple_string_list_append(&index_exclude_patterns, optarg);+ break;+ case 'n': /* include schema(s) */+ simple_string_list_append(&schema_include_patterns, optarg);+ break;+ case 'N': /* exclude schema(s) */+ simple_string_list_append(&schema_exclude_patterns, optarg);+ break;+ case 'o':+ settings.on_error_stop = true;+ break;+ case 'p':+ connOpts->port = pg_strdup(optarg);+ break;+ case 's':+ settings.strict_names = true;+ break;+ case 'S':+ if (pg_strcasecmp(optarg, "all-visible") == 0)+ {+ settings.skip_visible = true;+ settings.skip_frozen = false;+ }+ else if (pg_strcasecmp(optarg, "all-frozen") == 0)+ {+ settings.skip_frozen = true;+ settings.skip_visible = false;+ }+ else+ {+ exit(EXIT_FAILURE);+ pg_log_error("invalid skip option");+ }+ break;+ case 'r':+ settings.rootdescend = true;+ break;+ case 't': /* include table(s) */+ simple_string_list_append(&table_include_patterns, optarg);+ break;+ case 'T': /* exclude table(s) */+ simple_string_list_append(&table_exclude_patterns, optarg);+ break;+ case 'U':+ connOpts->username = pg_strdup(optarg);+ break;+ case 'V':+ showVersion();+ exit(EXIT_SUCCESS);+ case 'w':+ settings.getPassword = TRI_NO;+ break;+ case 'W':+ settings.getPassword = TRI_YES;+ break;+ case 'X':+ settings.check_indexes = false;+ break;+ case 'v':+ settings.verbose = true;+ break;+ case 'z':+ settings.check_toast = true;+ break;+ case 'Z':+ settings.check_toast = false;+ break;+ case '?':+ if (optind <= argc &&+ strcmp(argv[optind - 1], "-?") == 0)+ {+ /* actual help option given */+ usage();+ exit(EXIT_SUCCESS);+ }+ else+ {+ /* getopt error (unknown option or missing argument) */+ goto unknown_option;+ }+ break;+ case 1:+ {+ if (!optarg || strcmp(optarg, "options") == 0)+ usage();+ else+ goto unknown_option;++ exit(EXIT_SUCCESS);+ }+ break;+ default:+ unknown_option:+ fprintf(stderr, "Try \"%s --help\" for more information.\n",+ settings.progname);+ exit(EXIT_FAILURE);+ break;+ }+ }++ /*+ * if we still have arguments, use it as the database name and username+ */+ while (argc - optind >= 1)+ {+ if (!connOpts->dbname)+ connOpts->dbname = argv[optind];+ else if (!connOpts->username)+ connOpts->username = argv[optind];+ else+ pg_log_warning("extra command-line argument \"%s\" ignored",+ argv[optind]);++ optind++;+ }++ if (settings.endblock >= 0 && settings.endblock < settings.startblock)+ fatal("relation ending block argument precedes starting block argument");+}++/*+ * usage+ *+ * print out command line arguments+ */+static void+usage(void)+{+ printf("pg_amcheck is the PostgreSQL command line frontend for the amcheck database corruption checker.\n");+ printf("\n");+ printf("Usage:\n");+ printf(" pg_amcheck [OPTION]... [DBNAME [USERNAME]]\n");+ printf("\n");+ printf("General options:\n");+ printf(" -V, --version output version information, then exit\n");+ printf(" -?, --help show this help, then exit\n");+ printf(" -s, --strict-names require include patterns to match at least one entity each\n");+ printf(" -o, --on-error-stop stop checking at end of first corrupt page\n");+ printf(" -v, --verbose output verbose messages\n");+ printf("\n");+ printf("Schema checking options:\n");+ printf(" -n, --schema=PATTERN check relations in the specified schema(s) only\n");+ printf(" -N, --exclude-schema=PATTERN do NOT check relations in the specified schema(s)\n");+ printf("\n");+ printf("Table checking options:\n");+ printf(" -t, --table=PATTERN check the specified table(s) only\n");+ printf(" -T, --exclude-table=PATTERN do NOT check the specified table(s)\n");+ printf(" -b, --startblock begin checking table(s) at the given starting block number\n");+ printf(" -e, --endblock check table(s) only up to the given ending block number\n");+ printf(" -S, --skip=OPTION do NOT check \"all-frozen\" or \"all-visible\" blocks\n");+ printf("\n");+ printf("TOAST table checking options:\n");+ printf(" -z, --check-toast check associated toast tables and toast indexes\n");+ printf(" -Z, --skip-toast do NOT check associated toast tables and toast indexes\n");+ printf("\n");+ printf("Index checking options:\n");+ printf(" -X, --skip-indexes do NOT check any btree indexes\n");+ printf(" -i, --index=PATTERN check the specified index(es) only\n");+ printf(" -I, --exclude-index=PATTERN do NOT check the specified index(es)\n");+ printf(" -C, --skip-corrupt do NOT check indexes if their associated table is corrupt\n");+ printf(" -a, --heapallindexed check index tuples against the table tuples\n");+ printf(" -r, --rootdescend search from the root page for each index tuple\n");+ printf("\n");+ printf("Connection options:\n");+ printf(" -d, --dbname=DBNAME database name to connect to\n");+ printf(" -h, --host=HOSTNAME database server host or socket directory\n");+ printf(" -p, --port=PORT database server port\n");+ printf(" -U, --username=USERNAME database user name\n");+ printf(" -w, --no-password never prompt for password\n");+ printf(" -W, --password force password prompt (should happen automatically)\n");+ printf("\n");+ printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);+ printf("%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL);+}++static void+showVersion(void)+{+ puts("pg_amcheck (PostgreSQL) " PG_VERSION);+}++/*+ * for backend Notice messages (INFO, WARNING, etc)+ */+static void+NoticeProcessor(void *arg, const char *message)+{+ (void) arg; /* not used */+ pg_log_info("%s", message);+}++static void+get_table_check_list(const SimpleOidList *include_nsp, const SimpleOidList *exclude_nsp,+ const SimpleOidList *include_tbl, const SimpleOidList *exclude_tbl)+{+ PQExpBuffer querybuf;+ PGresult *res;+ int i;++ querybuf = createPQExpBuffer();++ appendPQExpBuffer(querybuf,+ "SELECT c.oid, c.reltoastrelid"+ "\nFROM pg_catalog.pg_class c, pg_catalog.pg_namespace n"+ "\nWHERE n.oid OPERATOR(pg_catalog.=) c.relnamespace"+ "\n AND c.relkind OPERATOR(pg_catalog.=) ANY(ARRAY[%s])\n",+ TABLE_RELKIND_LIST);+ include_filter(querybuf, "n.oid", include_nsp);+ exclude_filter(querybuf, "n.oid", exclude_nsp);+ include_filter(querybuf, "c.oid", include_tbl);+ exclude_filter(querybuf, "c.oid", exclude_tbl);++ res = ExecuteSqlQuery(conn, querybuf->data, PGRES_TUPLES_OK);+ for (i = 0; i < PQntuples(res); i++)+ {+ simple_oid_list_append(&mainlist, atooid(PQgetvalue(res, i, 0)));+ simple_oid_list_append(&toastlist, atooid(PQgetvalue(res, i, 1)));+ }++ PQclear(res);+ destroyPQExpBuffer(querybuf);+}diff --git a/contrib/pg_amcheck/pg_amcheck.control b/contrib/pg_amcheck/pg_amcheck.controlnew file mode 100644index 0000000000..395f368101--- /dev/null+++ b/contrib/pg_amcheck/pg_amcheck.control@@ -0,0 +1,5 @@+# pg_amcheck extension+comment = 'command-line tool for verifying relation integrity'+default_version = '1.3'+module_pathname = '$libdir/pg_amcheck'+relocatable = truediff --git a/contrib/pg_amcheck/t/001_basic.pl b/contrib/pg_amcheck/t/001_basic.plnew file mode 100644index 0000000000..dfa0ae9e06--- /dev/null+++ b/contrib/pg_amcheck/t/001_basic.pl@@ -0,0 +1,9 @@+use strict;+use warnings;++use TestLib;+use Test::More tests => 8;++program_help_ok('pg_amcheck');+program_version_ok('pg_amcheck');+program_options_handling_ok('pg_amcheck');diff --git a/contrib/pg_amcheck/t/002_nonesuch.pl b/contrib/pg_amcheck/t/002_nonesuch.plnew file mode 100644index 0000000000..189f05ef0a--- /dev/null+++ b/contrib/pg_amcheck/t/002_nonesuch.pl@@ -0,0 +1,60 @@+use strict;+use warnings;++use PostgresNode;+use TestLib;+use Test::More tests => 14;++# Test set-up+my ($node, $port);+$node = get_new_node('test');+$node->init;+$node->start;+$port = $node->port;++# Load the amcheck extension, upon which pg_amcheck depends+$node->safe_psql('postgres', q(CREATE EXTENSION amcheck));++#########################################+# Test connecting to a non-existent database++command_fails_like(+ [ 'pg_amcheck', '-p', "$port", 'qqq' ],+ qr/\Qpg_amcheck: error: could not connect to database qqq: FATAL: database "qqq" does not exist\E/,+ 'connecting to a non-existent database');++#########################################+# Test connecting with a non-existent user++command_fails_like(+ [ 'pg_amcheck', '-p', "$port", '-U=no_such_user', 'postgres' ],+ qr/\Qpg_amcheck: error: could not connect to database postgres: FATAL: role "=no_such_user" does not exist\E/,+ 'connecting with a non-existent user');++#########################################+# Test checking a non-existent schema, table, and patterns with --strict-names++command_fails_like(+ [ 'pg_amcheck', '-p', "$port", '-n', 'nonexistent' ],+ qr/\Qpg_amcheck: error: no matching schemas were found\E/,+ 'checking a non-existent schema');++command_fails_like(+ [ 'pg_amcheck', '-p', "$port", '-t', 'nonexistent' ],+ qr/\Qpg_amcheck: error: no matching tables were found\E/,+ 'checking a non-existent table');++command_fails_like(+ [ 'pg_amcheck', '-p', "$port", '--strict-names', '-n', 'nonexistent*' ],+ qr/\Qpg_amcheck: error: no matching schemas were found for pattern\E/,+ 'no matching schemas');++command_fails_like(+ [ 'pg_amcheck', '-p', "$port", '--strict-names', '-t', 'nonexistent*' ],+ qr/\Qpg_amcheck: error: no matching tables were found for pattern\E/,+ 'no matching tables');++command_fails_like(+ [ 'pg_amcheck', '-p', "$port", '--strict-names', '-i', 'nonexistent*' ],+ qr/\Qpg_amcheck: error: no matching indexes were found for pattern\E/,+ 'no matching indexes');diff --git a/contrib/pg_amcheck/t/003_check.pl b/contrib/pg_amcheck/t/003_check.plnew file mode 100644index 0000000000..30bbbdeddd--- /dev/null+++ b/contrib/pg_amcheck/t/003_check.pl@@ -0,0 +1,248 @@+use strict;+use warnings;++use PostgresNode;+use TestLib;+use Test::More tests => 45;++my ($node, $port);++# Returns the filesystem path for the named relation.+#+# Assumes the test node is running+sub relation_filepath($)+{+ my ($relname) = @_;++ my $pgdata = $node->data_dir;+ my $rel = $node->safe_psql('postgres',+ qq(SELECT pg_relation_filepath('$relname')));+ die "path not found for relation $relname" unless defined $rel;+ return "$pgdata/$rel";+}++# Stops the test node, corrupts the first page of the named relation, and+# restarts the node.+#+# Assumes the node is running.+sub corrupt_first_page($)+{+ my ($relname) = @_;+ my $relpath = relation_filepath($relname);++ $node->stop;+ my $fh;+ open($fh, '+<', $relpath);+ binmode $fh;+ seek($fh, 32, 0);+ syswrite($fh, '\x77\x77\x77\x77', 500);+ close($fh);+ $node->start;+}++# Stops the test node, unlinks the file from the filesystem that backs the+# relation, and restarts the node.+#+# Assumes the test node is running+sub remove_relation_file($)+{+ my ($relname) = @_;+ my $relpath = relation_filepath($relname);++ $node->stop();+ unlink($relpath);+ $node->start;+}++# Test set-up+$node = get_new_node('test');+$node->init;+$node->start;+$port = $node->port;++# Load the amcheck extension, upon which pg_amcheck depends+$node->safe_psql('postgres', q(CREATE EXTENSION amcheck));++# Create schemas and tables for checking pg_amcheck's include+# and exclude schema and table command line options+$node->safe_psql('postgres', q(+-- We'll corrupt all indexes in s1+CREATE SCHEMA s1;+CREATE TABLE s1.t1 (a TEXT);+CREATE TABLE s1.t2 (a TEXT);+CREATE INDEX i1 ON s1.t1(a);+CREATE INDEX i2 ON s1.t2(a);+INSERT INTO s1.t1 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);+INSERT INTO s1.t2 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);++-- We'll corrupt all tables in s2+CREATE SCHEMA s2;+CREATE TABLE s2.t1 (a TEXT);+CREATE TABLE s2.t2 (a TEXT);+CREATE INDEX i1 ON s2.t1(a);+CREATE INDEX i2 ON s2.t2(a);+INSERT INTO s2.t1 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);+INSERT INTO s2.t2 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);++-- We'll corrupt all tables and indexes in s3+CREATE SCHEMA s3;+CREATE TABLE s3.t1 (a TEXT);+CREATE TABLE s3.t2 (a TEXT);+CREATE INDEX i1 ON s3.t1(a);+CREATE INDEX i2 ON s3.t2(a);+INSERT INTO s3.t1 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);+INSERT INTO s3.t2 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);++-- We'll leave everything in s4 uncorrupted+CREATE SCHEMA s4;+CREATE TABLE s4.t1 (a TEXT);+CREATE TABLE s4.t2 (a TEXT);+CREATE INDEX i1 ON s4.t1(a);+CREATE INDEX i2 ON s4.t2(a);+INSERT INTO s4.t1 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);+INSERT INTO s4.t2 (a) (SELECT gs::TEXT FROM generate_series(1,10000) AS gs);+));++# Corrupt indexes in schema "s1"+remove_relation_file('s1.i1');+corrupt_first_page('s1.i2');++# Corrupt tables in schema "s2"+remove_relation_file('s2.t1');+corrupt_first_page('s2.t2');++# Corrupt tables and indexes in schema "s3"+remove_relation_file('s3.i1');+corrupt_first_page('s3.i2');+remove_relation_file('s3.t1');+corrupt_first_page('s3.t2');++# Leave schema "s4" alone+++# The pg_amcheck command itself should return a success exit status, even+# though tables and indexes are corrupt. An error code returned would mean the+# pg_amcheck command itself failed, for example because a connection to the+# database could not be established.+#+# For these checks, we're ignoring any corruption reported and focusing+# exclusively on the exit code from pg_amcheck.+#+$node->command_ok(+ [ 'pg_amcheck', '-p', $port, 'postgres' ],+ 'pg_amcheck all schemas and tables');++$node->command_ok(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres' ],+ 'pg_amcheck all schemas, tables and indexes');++$node->command_ok(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres', '-n', 's1' ],+ 'pg_amcheck all objects in schema s1');++$node->command_ok(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres', '-n', 's*', '-t', 't1' ],+ 'pg_amcheck all tables named t1 and their indexes');++$node->command_ok(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-T', 't1' ],+ 'pg_amcheck all tables not named t1');++$node->command_ok(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-N', 's1', '-T', 't1' ],+ 'pg_amcheck all tables not named t1 nor in schema s1');++# Scans of indexes in s1 should detect the specific corruption that we created+# above. For missing relation forks, we know what the error message looks+# like. For corrupted index pages, the error might vary depending on how the+# page was formatted on disk, including variations due to alignment differences+# between platforms, so we accept any non-empty error message.+#+$node->command_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's1', '-i', 'i1' ],+ qr/index "i1" lacks a main relation fork/,+ 'pg_amcheck index s1.i1 reports missing main relation fork');++$node->command_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's1', '-i', 'i2' ],+ qr/.+/, # Any non-empty error message is acceptable+ 'pg_amcheck index s1.s2 reports index corruption');+++# In schema s3, the tables and indexes are both corrupt. Ordinarily, checking+# of indexes will not be performed for corrupt tables, but the --check-corrupt+# option (-c) forces the indexes to also be checked.+#+$node->command_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's3', '-i', 'i1' ],+ qr/index "i1" lacks a main relation fork/,+ 'pg_amcheck index s3.i1 reports missing main relation fork');++$node->command_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's3', '-i', 'i2' ],+ qr/.+/, # Any non-empty error message is acceptable+ 'pg_amcheck index s3.s2 reports index corruption');+++# Check that '-x' and '-X' work as expected. Since only index corruption+# (and not table corruption) exists in s1, '-X' should give no errors, and+# '-x' should give errors about index corruption.+#+$node->command_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's1' ],+ qr/.+/, # Any non-empty error message is acceptable+ 'pg_amcheck over tables and indexes in schema s1 reports corruption');++$node->command_like(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres', '-n', 's1' ],+ qr/^$/, # Empty+ 'pg_amcheck over only tables in schema s1 reports no corruption');+++# Check that table corruption is reported as expected, with or without+# index checking+#+$node->command_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's2' ],+ qr/could not open file/,+ 'pg_amcheck over tables in schema s2 reports table corruption');++$node->command_like(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres', '-n', 's2' ],+ qr/could not open file/,+ 'pg_amcheck over tables and indexes in schema s2 reports table corruption');++# Check that no corruption is reported in schema s4+$node->command_like(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres', '-n', 's4' ],+ qr/^$/, # Empty+ 'pg_amcheck over schema s4 reports no corruption');++# Check that no corruption is reported if we exclude corrupt schemas+$node->command_like(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres', '-N', 's1', '-N', 's2', '-N', 's3' ],+ qr/^$/, # Empty+ 'pg_amcheck excluding corrupt schemas reports no corruption');++# Check that no corruption is reported if we exclude corrupt tables+$node->command_like(+ [ 'pg_amcheck', '-X', '-p', $port, 'postgres', '-T', 't1', '-T', 't2' ],+ qr/^$/, # Empty+ 'pg_amcheck excluding corrupt tables reports no corruption');++# Check errors about bad block range command line arguments. We use schema s4+# to avoid getting messages about corrupt tables or indexes.+command_fails_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's4', '-b', 'junk' ],+ qr/\Qpg_amcheck: error: relation starting block argument contains garbage characters\E/,+ 'pg_amcheck rejects garbage startblock');++command_fails_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's4', '-e', '1234junk' ],+ qr/\Qpg_amcheck: error: relation ending block argument contains garbage characters\E/,+ 'pg_amcheck rejects garbage endblock');++command_fails_like(+ [ 'pg_amcheck', '-p', $port, 'postgres', '-n', 's4', '-b', '5', '-e', '4' ],+ qr/\Qpg_amcheck: error: relation ending block argument precedes starting block argument\E/,+ 'pg_amcheck rejects invalid block range');diff --git a/contrib/pg_amcheck/t/004_verify_heapam.pl b/contrib/pg_amcheck/t/004_verify_heapam.plnew file mode 100644index 0000000000..271eca7da6--- /dev/null+++ b/contrib/pg_amcheck/t/004_verify_heapam.pl@@ -0,0 +1,496 @@+use strict;+use warnings;++use PostgresNode;+use TestLib;++use Test::More tests => 22;++# This regression test demonstrates that the pg_amcheck binary supplied with+# the pg_amcheck contrib module correctly identifies specific kinds of+# corruption within pages. To test this, we need a mechanism to create corrupt+# pages with predictable, repeatable corruption. The postgres backend cannot+# be expected to help us with this, as its design is not consistent with the+# goal of intentionally corrupting pages.+#+# Instead, we create a table to corrupt, and with careful consideration of how+# postgresql lays out heap pages, we seek to offsets within the page and+# overwrite deliberately chosen bytes with specific values calculated to+# corrupt the page in expected ways. We then verify that pg_amcheck reports+# the corruption, and that it runs without crashing. Note that the backend+# cannot simply be started to run queries against the corrupt table, as the+# backend will crash, at least for some of the corruption types we generate.+#+# Autovacuum potentially touching the table in the background makes the exact+# behavior of this test harder to reason about. We turn it off to keep things+# simpler. We use a "belt and suspenders" approach, turning it off for the+# system generally in postgresql.conf, and turning it off specifically for the+# test table.+#+# This test depends on the table being written to the heap file exactly as we+# expect it to be, so we take care to arrange the columns of the table, and+# insert rows of the table, that give predictable sizes and locations within+# the table page.+#+# The HeapTupleHeaderData has 23 bytes of fixed size fields before the variable+# length t_bits[] array. We have exactly 3 columns in the table, so natts = 3,+# t_bits is 1 byte long, and t_hoff = MAXALIGN(23 + 1) = 24.+#+# We're not too fussy about which datatypes we use for the test, but we do care+# about some specific properties. We'd like to test both fixed size and+# varlena types. We'd like some varlena data inline and some toasted. And+# we'd like the layout of the table such that the datums land at predictable+# offsets within the tuple. We choose a structure without padding on all+# supported architectures:+#+# a BIGINT+# b TEXT+# c TEXT+#+# We always insert a 7-ascii character string into field 'b', which with a+# 1-byte varlena header gives an 8 byte inline value. We always insert a long+# text string in field 'c', long enough to force toast storage.+#+# We choose to read and write binary copies of our table's tuples, using perl's+# pack() and unpack() functions. Perl uses a packing code system in which:+#+# L = "Unsigned 32-bit Long",+# S = "Unsigned 16-bit Short",+# C = "Unsigned 8-bit Octet",+# c = "signed 8-bit octet",+# q = "signed 64-bit quadword"+#+# Each tuple in our table has a layout as follows:+#+# xx xx xx xx t_xmin: xxxx offset = 0 L+# xx xx xx xx t_xmax: xxxx offset = 4 L+# xx xx xx xx t_field3: xxxx offset = 8 L+# xx xx bi_hi: xx offset = 12 S+# xx xx bi_lo: xx offset = 14 S+# xx xx ip_posid: xx offset = 16 S+# xx xx t_infomask2: xx offset = 18 S+# xx xx t_infomask: xx offset = 20 S+# xx t_hoff: x offset = 22 C+# xx t_bits: x offset = 23 C+# xx xx xx xx xx xx xx xx 'a': xxxxxxxx offset = 24 q+# xx xx xx xx xx xx xx xx 'b': xxxxxxxx offset = 32 Cccccccc+# xx xx xx xx xx xx xx xx 'c': xxxxxxxx offset = 40 SSSS+# xx xx xx xx xx xx xx xx : xxxxxxxx ...continued SSSS+# xx xx : xx ...continued S+#+# We could choose to read and write columns 'b' and 'c' in other ways, but+# it is convenient enough to do it this way. We define packing code+# constants here, where they can be compared easily against the layout.++use constant HEAPTUPLE_PACK_CODE => 'LLLSSSSSCCqCcccccccSSSSSSSSS';+use constant HEAPTUPLE_PACK_LENGTH => 58; # Total size++# Read a tuple of our table from a heap page.+#+# Takes an open filehandle to the heap file, and the offset of the tuple.+#+# Rather than returning the binary data from the file, unpacks the data into a+# perl hash with named fields. These fields exactly match the ones understood+# by write_tuple(), below. Returns a reference to this hash.+#+sub read_tuple ($$)+{+ my ($fh, $offset) = @_;+ my ($buffer, %tup);+ seek($fh, $offset, 0);+ sysread($fh, $buffer, HEAPTUPLE_PACK_LENGTH);++ @_ = unpack(HEAPTUPLE_PACK_CODE, $buffer);+ %tup = (t_xmin => shift,+ t_xmax => shift,+ t_field3 => shift,+ bi_hi => shift,+ bi_lo => shift,+ ip_posid => shift,+ t_infomask2 => shift,+ t_infomask => shift,+ t_hoff => shift,+ t_bits => shift,+ a => shift,+ b_header => shift,+ b_body1 => shift,+ b_body2 => shift,+ b_body3 => shift,+ b_body4 => shift,+ b_body5 => shift,+ b_body6 => shift,+ b_body7 => shift,+ c1 => shift,+ c2 => shift,+ c3 => shift,+ c4 => shift,+ c5 => shift,+ c6 => shift,+ c7 => shift,+ c8 => shift,+ c9 => shift);+ # Stitch together the text for column 'b'+ $tup{b} = join('', map { chr($tup{"b_body$_"}) } (1..7));+ return \%tup;+}++# Write a tuple of our table to a heap page.+#+# Takes an open filehandle to the heap file, the offset of the tuple, and a+# reference to a hash with the tuple values, as returned by read_tuple().+# Writes the tuple fields from the hash into the heap file.+#+# The purpose of this function is to write a tuple back to disk with some+# subset of fields modified. The function does no error checking. Use+# cautiously.+#+sub write_tuple($$$)+{+ my ($fh, $offset, $tup) = @_;+ my $buffer = pack(HEAPTUPLE_PACK_CODE,+ $tup->{t_xmin},+ $tup->{t_xmax},+ $tup->{t_field3},+ $tup->{bi_hi},+ $tup->{bi_lo},+ $tup->{ip_posid},+ $tup->{t_infomask2},+ $tup->{t_infomask},+ $tup->{t_hoff},+ $tup->{t_bits},+ $tup->{a},+ $tup->{b_header},+ $tup->{b_body1},+ $tup->{b_body2},+ $tup->{b_body3},+ $tup->{b_body4},+ $tup->{b_body5},+ $tup->{b_body6},+ $tup->{b_body7},+ $tup->{c1},+ $tup->{c2},+ $tup->{c3},+ $tup->{c4},+ $tup->{c5},+ $tup->{c6},+ $tup->{c7},+ $tup->{c8},+ $tup->{c9});+ seek($fh, $offset, 0);+ syswrite($fh, $buffer, HEAPTUPLE_PACK_LENGTH);+ return;+}++# Set umask so test directories and files are created with default permissions+umask(0077);++# Set up the node. Once we create and corrupt the table,+# autovacuum workers visiting the table could crash the backend.+# Disable autovacuum so that won't happen.+my $node = get_new_node('test');+$node->init;+$node->append_conf('postgresql.conf', 'autovacuum=off');++# Start the node and load the extensions. We depend on both+# amcheck and pageinspect for this test.+$node->start;+my $port = $node->port;+my $pgdata = $node->data_dir;+$node->safe_psql('postgres', "CREATE EXTENSION amcheck");+$node->safe_psql('postgres', "CREATE EXTENSION pageinspect");++# Get a non-zero datfrozenxid+$node->safe_psql('postgres', qq(VACUUM FREEZE));++# Create the test table with precisely the schema that our corruption function+# expects.+$node->safe_psql(+ 'postgres', qq(+ CREATE TABLE public.test (a BIGINT, b TEXT, c TEXT);+ ALTER TABLE public.test SET (autovacuum_enabled=false);+ ALTER TABLE public.test ALTER COLUMN c SET STORAGE EXTERNAL;+ CREATE INDEX test_idx ON public.test(a, b);+ ));++# We want (0 < datfrozenxid < test.relfrozenxid). To achieve this, we freeze+# an otherwise unused table, public.junk, prior to inserting data and freezing+# public.test+$node->safe_psql(+ 'postgres', qq(+ CREATE TABLE public.junk AS SELECT 'junk'::TEXT AS junk_column;+ ALTER TABLE public.junk SET (autovacuum_enabled=false);+ VACUUM FREEZE public.junk+ ));++my $rel = $node->safe_psql('postgres', qq(SELECT pg_relation_filepath('public.test')));+my $relpath = "$pgdata/$rel";++# Insert data and freeze public.test+use constant ROWCOUNT => 16;+$node->safe_psql('postgres', qq(+ INSERT INTO public.test (a, b, c)+ VALUES (+ 12345678,+ 'abcdefg',+ repeat('w', 10000)+ );+ VACUUM FREEZE public.test+ )) for (1..ROWCOUNT);++my $relfrozenxid = $node->safe_psql('postgres',+ q(select relfrozenxid from pg_class where relname = 'test'));+my $datfrozenxid = $node->safe_psql('postgres',+ q(select datfrozenxid from pg_database where datname = 'postgres'));++# Find where each of the tuples is located on the page.+my @lp_off;+for my $tup (0..ROWCOUNT-1)+{+ push (@lp_off, $node->safe_psql('postgres', qq(+select lp_off from heap_page_items(get_raw_page('test', 'main', 0))+ offset $tup limit 1)));+}++# Check that pg_amcheck runs against the uncorrupted table without error.+$node->command_ok(['pg_amcheck', '-p', $port, 'postgres'],+ 'pg_amcheck test table, prior to corruption');++# Check that pg_amcheck runs against the uncorrupted table and index without error.+$node->command_ok(['pg_amcheck', '-p', $port, 'postgres'],+ 'pg_amcheck test table and index, prior to corruption');++$node->stop;++# Sanity check that our 'test' table has a relfrozenxid newer than the+# datfrozenxid for the database, and that the datfrozenxid is greater than the+# first normal xid. We rely on these invariants in some of our tests.+if ($datfrozenxid <= 3 || $datfrozenxid >= $relfrozenxid)+{+ fail('Xid thresholds not as expected');+ $node->clean_node;+ exit;+}++# Some #define constants from access/htup_details.h for use while corrupting.+use constant HEAP_HASNULL => 0x0001;+use constant HEAP_XMAX_LOCK_ONLY => 0x0080;+use constant HEAP_XMIN_COMMITTED => 0x0100;+use constant HEAP_XMIN_INVALID => 0x0200;+use constant HEAP_XMAX_COMMITTED => 0x0400;+use constant HEAP_XMAX_INVALID => 0x0800;+use constant HEAP_NATTS_MASK => 0x07FF;+use constant HEAP_XMAX_IS_MULTI => 0x1000;+use constant HEAP_KEYS_UPDATED => 0x2000;++# Helper function to generate a regular expression matching the header we+# expect verify_heapam() to return given which fields we expect to be non-null.+sub header+{+ my ($blkno, $offnum, $attnum) = @_;+ return qr/relation test, block $blkno, offset $offnum, attribute $attnum\s+/ms+ if (defined $attnum);+ return qr/relation test, block $blkno, offset $offnum\s+/ms+ if (defined $offnum);+ return qr/relation test\s+/ms+ if (defined $blkno);+ return qr/relation test\s+/ms;+}++# Corrupt the tuples, one type of corruption per tuple. Some types of+# corruption cause verify_heapam to skip to the next tuple without+# performing any remaining checks, so we can't exercise the system properly if+# we focus all our corruption on a single tuple.+#+my @expected;+my $file;+open($file, '+<', $relpath);+binmode $file;++for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)+{+ my $offnum = $tupidx + 1; # offnum is 1-based, not zero-based+ my $offset = $lp_off[$tupidx];+ my $tup = read_tuple($file, $offset);++ # Sanity-check that the data appears on the page where we expect.+ if ($tup->{a} ne '12345678' || $tup->{b} ne 'abcdefg')+ {+ fail('Page layout differs from our expectations');+ $node->clean_node;+ exit;+ }++ my $header = header(0, $offnum, undef);+ if ($offnum == 1)+ {+ # Corruptly set xmin < relfrozenxid+ my $xmin = $relfrozenxid - 1;+ $tup->{t_xmin} = $xmin;+ $tup->{t_infomask} &= ~HEAP_XMIN_COMMITTED;+ $tup->{t_infomask} &= ~HEAP_XMIN_INVALID;++ # Expected corruption report+ push @expected,+ qr/${header}xmin $xmin precedes relation freeze threshold 0:\d+/;+ }+ if ($offnum == 2)+ {+ # Corruptly set xmin < datfrozenxid+ my $xmin = 3;+ $tup->{t_xmin} = $xmin;+ $tup->{t_infomask} &= ~HEAP_XMIN_COMMITTED;+ $tup->{t_infomask} &= ~HEAP_XMIN_INVALID;++ push @expected,+ qr/${$header}xmin $xmin precedes oldest valid transaction ID 0:\d+/;+ }+ elsif ($offnum == 3)+ {+ # Corruptly set xmin < datfrozenxid, further back, noting circularity+ # of xid comparison. For a new cluster with epoch = 0, the corrupt+ # xmin will be interpreted as in the future+ $tup->{t_xmin} = 4026531839;+ $tup->{t_infomask} &= ~HEAP_XMIN_COMMITTED;+ $tup->{t_infomask} &= ~HEAP_XMIN_INVALID;++ push @expected,+ qr/${$header}xmin 4026531839 equals or exceeds next valid transaction ID 0:\d+/;+ }+ elsif ($offnum == 4)+ {+ # Corruptly set xmax < relminmxid;+ $tup->{t_xmax} = 4026531839;+ $tup->{t_infomask} &= ~HEAP_XMAX_INVALID;++ push @expected,+ qr/${$header}xmax 4026531839 equals or exceeds next valid transaction ID 0:\d+/;+ }+ elsif ($offnum == 5)+ {+ # Corrupt the tuple t_hoff, but keep it aligned properly+ $tup->{t_hoff} += 128;++ push @expected,+ qr/${$header}data begins at offset 152 beyond the tuple length 58/,+ qr/${$header}tuple data should begin at byte 24, but actually begins at byte 152 \(3 attributes, no nulls\)/;+ }+ elsif ($offnum == 6)+ {+ # Corrupt the tuple t_hoff, wrong alignment+ $tup->{t_hoff} += 3;++ push @expected,+ qr/${$header}tuple data should begin at byte 24, but actually begins at byte 27 \(3 attributes, no nulls\)/;+ }+ elsif ($offnum == 7)+ {+ # Corrupt the tuple t_hoff, underflow but correct alignment+ $tup->{t_hoff} -= 8;++ push @expected,+ qr/${$header}tuple data should begin at byte 24, but actually begins at byte 16 \(3 attributes, no nulls\)/;+ }+ elsif ($offnum == 8)+ {+ # Corrupt the tuple t_hoff, underflow and wrong alignment+ $tup->{t_hoff} -= 3;++ push @expected,+ qr/${$header}tuple data should begin at byte 24, but actually begins at byte 21 \(3 attributes, no nulls\)/;+ }+ elsif ($offnum == 9)+ {+ # Corrupt the tuple to look like it has lots of attributes, not just 3+ $tup->{t_infomask2} |= HEAP_NATTS_MASK;++ push @expected,+ qr/${$header}number of attributes 2047 exceeds maximum expected for table 3/;+ }+ elsif ($offnum == 10)+ {+ # Corrupt the tuple to look like it has lots of attributes, some of+ # them null. This falsely creates the impression that the t_bits+ # array is longer than just one byte, but t_hoff still says otherwise.+ $tup->{t_infomask} |= HEAP_HASNULL;+ $tup->{t_infomask2} |= HEAP_NATTS_MASK;+ $tup->{t_bits} = 0xAA;++ push @expected,+ qr/${$header}tuple data should begin at byte 280, but actually begins at byte 24 \(2047 attributes, has nulls\)/;+ }+ elsif ($offnum == 11)+ {+ # Same as above, but this time t_hoff plays along+ $tup->{t_infomask} |= HEAP_HASNULL;+ $tup->{t_infomask2} |= (HEAP_NATTS_MASK & 0x40);+ $tup->{t_bits} = 0xAA;+ $tup->{t_hoff} = 32;++ push @expected,+ qr/${$header}number of attributes 67 exceeds maximum expected for table 3/;+ }+ elsif ($offnum == 12)+ {+ # Corrupt the bits in column 'b' 1-byte varlena header+ $tup->{b_header} = 0x80;++ $header = header(0, $offnum, 1);+ push @expected,+ qr/${header}attribute 1 with length 4294967295 ends at offset 416848000 beyond total tuple length 58/;+ }+ elsif ($offnum == 13)+ {+ # Corrupt the bits in column 'c' toast pointer+ $tup->{c6} = 41;+ $tup->{c7} = 41;++ $header = header(0, $offnum, 2);+ push @expected,+ qr/${header}final toast chunk number 0 differs from expected value 6/,+ qr/${header}toasted value for attribute 2 missing from toast table/;+ }+ elsif ($offnum == 14)+ {+ # Set both HEAP_XMAX_LOCK_ONLY and HEAP_KEYS_UPDATED+ $tup->{t_infomask} |= HEAP_XMAX_LOCK_ONLY;+ $tup->{t_infomask2} |= HEAP_KEYS_UPDATED;++ push @expected,+ qr/${header}tuple is marked as only locked, but also claims key columns were updated/;+ }+ elsif ($offnum == 15)+ {+ # Set both HEAP_XMAX_COMMITTED and HEAP_XMAX_IS_MULTI+ $tup->{t_infomask} |= HEAP_XMAX_COMMITTED;+ $tup->{t_infomask} |= HEAP_XMAX_IS_MULTI;+ $tup->{t_xmax} = 4;++ push @expected,+ qr/${header}multitransaction ID 4 equals or exceeds next valid multitransaction ID 1/;+ }+ elsif ($offnum == 16) # Last offnum must equal ROWCOUNT+ {+ # Set both HEAP_XMAX_COMMITTED and HEAP_XMAX_IS_MULTI+ $tup->{t_infomask} |= HEAP_XMAX_COMMITTED;+ $tup->{t_infomask} |= HEAP_XMAX_IS_MULTI;+ $tup->{t_xmax} = 4000000000;++ push @expected,+ qr/${header}multitransaction ID 4000000000 precedes relation minimum multitransaction ID threshold 1/;+ }+ write_tuple($file, $offset, $tup);+}+close($file);+$node->start;++# Run pg_amcheck against the corrupt table with epoch=0, comparing actual+# corruption messages against the expected messages+$node->command_checks_all(+ ['pg_amcheck', '--check-toast', '--skip-indexes', '-p', $port, 'postgres'],+ 0,+ [ @expected ],+ [ qr/^$/ ],+ 'Expected corruption message output');++$node->teardown_node;+$node->clean_node;diff --git a/contrib/pg_amcheck/t/005_opclass_damage.pl b/contrib/pg_amcheck/t/005_opclass_damage.plnew file mode 100644index 0000000000..c24f154883--- /dev/null+++ b/contrib/pg_amcheck/t/005_opclass_damage.pl@@ -0,0 +1,52 @@+# This regression test checks the behavior of the btree validation in the+# presence of breaking sort order changes.+#+use strict;+use warnings;+use PostgresNode;+use TestLib;+use Test::More tests => 6;++my $node = get_new_node('test');+$node->init;+$node->start;++# Create a custom operator class and an index which uses it.+$node->safe_psql('postgres', q(+ CREATE EXTENSION amcheck;++ CREATE FUNCTION int4_asc_cmp (a int4, b int4) RETURNS int LANGUAGE sql AS $$+ SELECT CASE WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN 1 ELSE -1 END; $$;++ CREATE OPERATOR CLASS int4_fickle_ops FOR TYPE int4 USING btree AS+ OPERATOR 1 < (int4, int4), OPERATOR 2 <= (int4, int4),+ OPERATOR 3 = (int4, int4), OPERATOR 4 >= (int4, int4),+ OPERATOR 5 > (int4, int4), FUNCTION 1 int4_asc_cmp(int4, int4);++ CREATE TABLE int4tbl (i int4);+ INSERT INTO int4tbl (SELECT * FROM generate_series(1,1000) gs);+ CREATE INDEX fickleidx ON int4tbl USING btree (i int4_fickle_ops);+));++# We have not yet broken the index, so we should get no corruption+$node->command_like(+ [ 'pg_amcheck', '-X', '-p', $node->port, 'postgres' ],+ qr/^$/,+ 'pg_amcheck all schemas, tables and indexes reports no corruption');++# Change the operator class to use a function which sorts in a different+# order to corrupt the btree index+$node->safe_psql('postgres', q(+ CREATE FUNCTION int4_desc_cmp (int4, int4) RETURNS int LANGUAGE sql AS $$+ SELECT CASE WHEN $1 = $2 THEN 0 WHEN $1 > $2 THEN -1 ELSE 1 END; $$;+ UPDATE pg_catalog.pg_amproc+ SET amproc = 'int4_desc_cmp'::regproc+ WHERE amproc = 'int4_asc_cmp'::regproc+));++# Index corruption should now be reported+$node->command_like(+ [ 'pg_amcheck', '-p', $node->port, 'postgres' ],+ qr/item order invariant violated for index "fickleidx"/,+ 'pg_amcheck all schemas, tables and indexes reports fickleidx corruption'+);diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgmlindex ae2759be55..797b4dc61e 100644--- a/doc/src/sgml/contrib.sgml+++ b/doc/src/sgml/contrib.sgml@@ -119,6 +119,7 @@ CREATE EXTENSION <replaceable>module_name</replaceable>;
&oldsnapshot;
&pageinspect;
&passwordcheck;
+ &pgamcheck;
&pgbuffercache;
&pgcrypto;
&pgfreespacemap;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgmlindex 38e8aa0bbf..a4e1b28b38 100644--- a/doc/src/sgml/filelist.sgml+++ b/doc/src/sgml/filelist.sgml@@ -133,6 +133,7 @@
<!ENTITY oldsnapshot SYSTEM "oldsnapshot.sgml">
<!ENTITY pageinspect SYSTEM "pageinspect.sgml">
<!ENTITY passwordcheck SYSTEM "passwordcheck.sgml">
+<!ENTITY pgamcheck SYSTEM "pgamcheck.sgml">
<!ENTITY pgbuffercache SYSTEM "pgbuffercache.sgml">
<!ENTITY pgcrypto SYSTEM "pgcrypto.sgml">
<!ENTITY pgfreespacemap SYSTEM "pgfreespacemap.sgml">
diff --git a/doc/src/sgml/pgamcheck.sgml b/doc/src/sgml/pgamcheck.sgmlnew file mode 100644index 0000000000..00643d2e58--- /dev/null+++ b/doc/src/sgml/pgamcheck.sgml@@ -0,0 +1,493 @@+<!-- doc/src/sgml/pgamcheck.sgml -->++<sect1 id="pgamcheck" xreflabel="pg_amcheck">+ <title>pg_amcheck</title>++ <indexterm zone="pgamcheck">+ <primary>pg_amcheck</primary>+ </indexterm>++ <para>+ The <filename>pg_amcheck</filename> module provides a command line interface+ to the <xref linkend="amcheck"/> corruption checking functionality.+ </para>++ <para>+ <application>pg_amcheck</application> is a regular+ <productname>PostgreSQL</productname> client application. You can perform+ corruption checks from any remote host that has access to the database+ connecting as a user with sufficient privileges to check tables and indexes.+ Currently, this requires execute privileges on <xref linkend="amcheck"/>'s+ <function>bt_index_parent_check</function> and <function>verify_heapam</function>+ functions.+ </para>++<synopsis>+pg_amcheck mydb+</synopsis>++ <sect2>+ <title>Options</title>++ <para>+ The following command-line options for controlling general program behavior+ are recognized.++ <variablelist>+ <varlistentry>+ <term><option>-V</option></term>+ <term><option>--version</option></term>+ <listitem>+ <para>+ Show <application>pg_amcheck</application> version number, and exit.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-?</option></term>+ <term><option>--help</option></term>+ <listitem>+ <para>+ Show help about <application>pg_amcheck</application> command line+ arguments, and exit.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-v</option></term>+ <term><option>--verbose</option></term>+ <listitem>+ <para>+ Specifies verbose mode. This will cause+ <application>pg_amcheck</application> to output more detailed information+ about its activities, mostly to do with its communication with the+ database.+ </para>+ <para>+ Note that this does not increase the number of corruptions reported nor+ the level of detail reported about each of them.+ </para>+ </listitem>+ </varlistentry>+ </variablelist>+ </para>++ <para>+ The following command-line options control which datatabase objects+ <application>pg_amcheck</application> checks and how such options+ are interpreted.+ + <variablelist>+ <varlistentry>+ <term><option>-i <replaceable class="parameter">pattern</replaceable></option></term>+ <term><option>--index=<replaceable class="parameter">pattern</replaceable></option></term>+ <listitem>+ <para>+ For indexes associated with tables being checked, check only those+ indexes with names matching <replaceable+ class="parameter">pattern</replaceable>. Multiple indexes can be+ selected by writing multiple <option>-i</option> switches. The+ <replaceable class="parameter">pattern</replaceable> parameter is+ interpreted as a pattern according to the same rules used by+ <application>psql</application>'s <literal>\d</literal> commands (see+ <xref linkend="app-psql-patterns"/>), so multiple indexes can also+ be selected by writing wildcard characters in the pattern. When using+ wildcards, be careful to quote the pattern if needed to prevent the+ shell from expanding the wildcards.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-I <replaceable class="parameter">pattern</replaceable></option></term>+ <term><option>--exclude-index=<replaceable class="parameter">pattern</replaceable></option></term>+ <listitem>+ <para>+ Do not check any indexes matching <replaceable+ class="parameter">pattern</replaceable>. The pattern is interpreted+ according to the same rules as for <option>-i</option>.+ <option>-I</option> can be given more than once to exclude indexes+ matching any of several patterns.+ </para>++ <para>+ When both <option>-i</option> and <option>-I</option> are given, the+ behavior is to check just the indexes that match at least one+ <option>-i</option> switch but no <option>-I</option> switches. If+ <option>-I</option> appears without <option>-i</option>, then indexes+ matching <option>-I</option> are excluded from what is otherwise a check+ of all indexes associated with tables that are checked.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-n <replaceable class="parameter">pattern</replaceable></option></term>+ <term><option>--schema=<replaceable class="parameter">pattern</replaceable></option></term>+ <listitem>+ <para>+ Dump only schemas matching <replaceable+ class="parameter">pattern</replaceable>; this selects both the+ schema itself, and all its contained objects. When this option is+ not specified, all non-system schemas in the target database will be+ checked. Multiple schemas can be+ selected by writing multiple <option>-n</option> switches. The+ <replaceable class="parameter">pattern</replaceable> parameter is+ interpreted as a pattern according to the same rules used by+ <application>psql</application>'s <literal>\d</literal> commands+ (see <xref linkend="app-psql-patterns"/>),+ so multiple schemas can also be selected by writing wildcard characters+ in the pattern. When using wildcards, be careful to quote the pattern+ if needed to prevent the shell from expanding the wildcards.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-N <replaceable class="parameter">pattern</replaceable></option></term>+ <term><option>--exclude-schema=<replaceable class="parameter">pattern</replaceable></option></term>+ <listitem>+ <para>+ Do not check any schemas matching <replaceable+ class="parameter">pattern</replaceable>. The pattern is+ interpreted according to the same rules as for <option>-n</option>.+ <option>-N</option> can be given more than once to exclude schemas+ matching any of several patterns.+ </para>++ <para>+ When both <option>-n</option> and <option>-N</option> are given, the behavior+ is to check just the schemas that match at least one <option>-n</option>+ switch but no <option>-N</option> switches. If <option>-N</option> appears+ without <option>-n</option>, then schemas matching <option>-N</option> are+ excluded from what is otherwise a check of all schemas.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-s</option></term>+ <term><option>--strict-names</option></term>+ <listitem>+ <para>+ Requires that each schema+ (<option>-n</option>/<option>--schema</option>), table+ (<option>-t</option>/<option>--table</option>) and index+ (<option>-i</option>/<option>--index</option>) qualifier match at least+ one schema/table/index in the database to be checked.+ </para>+ <para>+ This option has no effect on+ <option>-N</option>/<option>--exclude-schema</option>,+ <option>-T</option>/<option>--exclude-table</option>,+ or <option>-I</option><option>--exclude-index</option>. An exclude+ pattern failing to match any objects is not considered an error.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-t <replaceable class="parameter">pattern</replaceable></option></term>+ <term><option>--table=<replaceable class="parameter">pattern</replaceable></option></term>+ <listitem>+ <para>+ Check only tables with names matching+ <replaceable class="parameter">pattern</replaceable>. Multiple tables+ can be selected by writing multiple <option>-t</option> switches. The+ <replaceable class="parameter">pattern</replaceable> parameter is+ interpreted as a pattern according to the same rules used by+ <application>psql</application>'s <literal>\d</literal> commands+ (see <xref linkend="app-psql-patterns"/>),+ so multiple tables can also be selected by writing wildcard characters+ in the pattern. When using wildcards, be careful to quote the pattern+ if needed to prevent the shell from expanding the wildcards.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-T <replaceable class="parameter">pattern</replaceable></option></term>+ <term><option>--exclude-table=<replaceable class="parameter">pattern</replaceable></option></term>+ <listitem>+ <para>+ Do not check any tables matching <replaceable+ class="parameter">pattern</replaceable>. The pattern is interpreted+ according to the same rules as for <option>-t</option>.+ <option>-T</option> can be given more than once to exclude tables+ matching any of several patterns.+ </para>++ <para>+ When both <option>-t</option> and <option>-T</option> are given, the+ behavior is to check just the tables that match at least one+ <option>-t</option> switch but no <option>-T</option> switches. If+ <option>-T</option> appears without <option>-t</option>, then tables+ matching <option>-T</option> are excluded from what is otherwise a check+ of all tables.+ </para>+ </listitem>+ </varlistentry>+ </variablelist>+ </para>++ <para>+ The following command-line options control additional behaviors.++ <variablelist>+ <varlistentry>+ <term><option>-a</option></term>+ <term><option>--heapallindexed</option></term>+ <listitem>+ <para>+ When checking indexes, additionally verify the presence of all heap+ tuples as index tuples within the index.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-b <replaceable class="parameter">block</replaceable></option></term>+ <term><option>--startblock=<replaceable class="parameter">block</replaceable></option></term>+ <listitem>+ <para>+ Do not check blocks prior to <replaceable+ class="parameter">block</replaceable>, which should be a non-negative+ integer. (Negative values disable the option).+ </para>+ <para>+ When both <option>-b</option> <option>--startblock</option> and+ <option>-e</option> <option>--endblock</option> are specified, the end+ block must not be less than the start block.+ </para>+ <para>+ The <option>-b</option> <option>--startblock</option> option will be+ applied to all tables that are checked, including toast tables. The+ option is most useful when checking exactly one table, to focus the+ checking on just specific blocks of that one table.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-C</option></term>+ <term><option>--skip-corrupt</option></term>+ <listitem>+ <para>+ Skip checking indexes for a table if the table is found to be corrupt.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-e <replaceable class="parameter">block</replaceable></option></term>+ <term><option>--endblock=<replaceable class="parameter">block</replaceable></option></term>+ <listitem>+ <para>+ Do not check blocks after <replaceable+ class="parameter">block</replaceable>, which should be a non-negative+ integer. (Negative values disable the option).+ </para>+ <para>+ When both <option>-b</option> <option>--startblock</option> and+ <option>-e</option> <option>--endblock</option> are specified, the end+ block must not be less than the start block.+ </para>+ <para>+ The <option>-e</option> <option>--endblock</option> option will be+ applied to all tables that are checked, including toast tables. The+ option is most useful when checking exactly one table, to focus the+ checking on just specific blocks of that one table.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-o</option></term>+ <term><option>--on-error-stop</option></term>+ <listitem>+ <para>+ Stop checking database objects at the end of the first page on+ which the first corruption is found. Note that even with this option+ enabled, more than one corruption message may be reported.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-r</option></term>+ <term><option>--rootdescend</option></term>+ <listitem>+ <para>+ When checking indexes, for each tuple, perform addition verification by+ re-finding the tuple on the leaf level by performing a new search from+ the root page.+ </para>+ <para>+ This form of verification was originally written to help in the+ development of btree index features. It may be of limited or even of no+ use in helping detect the kinds of corruption that occur in practice.+ In any event, it is known to be a rather expensive check to perform.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-S</option></term>+ <term><option>--skip=<replaceable class="parameter">blocks</replaceable></option></term>+ <listitem>+ <para>+ When <option>-S</option><replaceable+ class="parameter">all-visible</replaceable> is given, skips corruption+ checking of blocks marked as all visible in the visibility map.+ </para>+ <para>+ When <option>-S</option><replaceable+ class="parameter">all-frozen</replaceable> is given, skips corruption+ checking of blocks marked as all frozen in the visibility map.+ </para>+ <para>+ The default is to check blocks without regard to their marking in the+ visibility map.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-X</option></term>+ <term><option>--skip-indexes</option></term>+ <listitem>+ <para>+ Check only tables but not indexes.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-Z</option></term>+ <term><option>--skip-toast</option></term>+ <listitem>+ <para>+ Do not check toast tables nor their indexes.+ </para>+ </listitem>+ </varlistentry>++ </variablelist>+ </para>++ <para>+ The following additional command-line options control the database+ connection parameters.++ <variablelist>+ <varlistentry>+ <term><option>-d <replaceable class="parameter">dbname</replaceable></option></term>+ <term><option>--dbname=<replaceable class="parameter">dbname</replaceable></option></term>+ <listitem>+ <para>+ Specifies the name of the database to connect to. This is+ equivalent to specifying <replaceable+ class="parameter">dbname</replaceable> as the first non-option+ argument on the command line. The <replaceable>dbname</replaceable>+ can be a <link linkend="libpq-connstring">connection string</link>.+ If so, connection string parameters will override any conflicting+ command line options.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-h <replaceable class="parameter">host</replaceable></option></term>+ <term><option>--host=<replaceable class="parameter">host</replaceable></option></term>+ <listitem>+ <para>+ Specifies the host name of the machine on which the server is+ running. If the value begins with a slash, it is used as the+ directory for the Unix domain socket. The default is taken+ from the <envar>PGHOST</envar> environment variable, if set,+ else a Unix domain socket connection is attempted.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-p <replaceable class="parameter">port</replaceable></option></term>+ <term><option>--port=<replaceable class="parameter">port</replaceable></option></term>+ <listitem>+ <para>+ Specifies the TCP port or local Unix domain socket file+ extension on which the server is listening for connections.+ Defaults to the <envar>PGPORT</envar> environment variable, if+ set, or a compiled-in default.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-U <replaceable>username</replaceable></option></term>+ <term><option>--username=<replaceable class="parameter">username</replaceable></option></term>+ <listitem>+ <para>+ User name to connect as.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-w</option></term>+ <term><option>--no-password</option></term>+ <listitem>+ <para>+ Never issue a password prompt. If the server requires+ password authentication and a password is not available by+ other means such as a <filename>.pgpass</filename> file, the+ connection attempt will fail. This option can be useful in+ batch jobs and scripts where no user is present to enter a+ password.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>-W</option></term>+ <term><option>--password</option></term>+ <listitem>+ <para>+ Force <application>pg_dump</application> to prompt for a+ password before connecting to a database.+ </para>++ <para>+ This option is never essential, since+ <application>pg_dump</application> will automatically prompt+ for a password if the server demands password authentication.+ However, <application>pg_dump</application> will waste a+ connection attempt finding out that the server wants a password.+ In some cases it is worth typing <option>-W</option> to avoid the extra+ connection attempt.+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><option>--role=<replaceable class="parameter">rolename</replaceable></option></term>+ <listitem>+ <para>+ Specifies a role name to be used to create the dump.+ This option causes <application>pg_dump</application> to issue a+ <command>SET ROLE</command> <replaceable class="parameter">rolename</replaceable>+ command after connecting to the database. It is useful when the+ authenticated user (specified by <option>-U</option>) lacks privileges+ needed by <application>pg_dump</application>, but can switch to a role with+ the required rights. Some installations have a policy against+ logging in directly as a superuser, and use of this option allows+ dumps to be made without violating the policy.+ </para>+ </listitem>+ </varlistentry>+ </variablelist>+ </para>+ </sect2>+</sect1>diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.cindex 355da6edaf..cbbc085698 100644--- a/src/fe_utils/query_utils.c+++ b/src/fe_utils/query_utils.c@@ -46,7 +46,7 @@ exiting_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,
}
if (expected_ntups == POSITIVE_NTUPS || expected_ntups >= 0)
{
- int ntups = PQntuples(res);+ int ntups = PQntuples(res);
if (expected_ntups == POSITIVE_NTUPS)
{
@@ -63,11 +63,11 @@ exiting_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,
fatal(ngettext("query returned %d row instead of one: %s",
"query returned %d rows instead of one: %s",
ntups),
- ntups, query);+ ntups, query);
fatal(ngettext("query returned %d row instead of %d: %s",
"query returned %d rows instead of %d: %s",
ntups),
- ntups, expected_ntups, query);+ ntups, expected_ntups, query);
}
}
return res;
@@ -80,7 +80,7 @@ PGresult *
quiet_handler(PGresult *res, PGconn *conn, ExecStatusType expected_status,
int expected_ntups, const char *query)
{
- int ntups = PQntuples(res);+ int ntups = PQntuples(res);
if ((PQresultStatus(res) != expected_status) ||
(expected_ntups == POSITIVE_NTUPS && ntups == 0) ||
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pmindex 7f014a12c9..eef00320e7 100644--- a/src/tools/msvc/Mkvcbuild.pm+++ b/src/tools/msvc/Mkvcbuild.pm@@ -33,9 +33,9 @@ my @unlink_on_exit;
# Set of variables for modules in contrib/ and src/test/modules/
my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' };
-my @contrib_uselibpq = ('dblink', 'oid2name', 'postgres_fdw', 'vacuumlo');-my @contrib_uselibpgport = ('oid2name', 'pg_standby', 'vacuumlo');-my @contrib_uselibpgcommon = ('oid2name', 'pg_standby', 'vacuumlo');+my @contrib_uselibpq = ('dblink', 'oid2name', 'pg_amcheck', 'postgres_fdw', 'vacuumlo');+my @contrib_uselibpgport = ('oid2name', 'pg_amcheck', 'pg_standby', 'vacuumlo');+my @contrib_uselibpgcommon = ('oid2name', 'pg_amcheck', 'pg_standby', 'vacuumlo');
my $contrib_extralibs = undef;
my $contrib_extraincludes = { 'dblink' => ['src/backend'] };
my $contrib_extrasource = {
@@ -146,8 +146,8 @@ sub mkvcbuild
our @pgcommonbkndfiles = @pgcommonallfiles;
our @pgfeutilsfiles = qw(
- archive.c cancel.c conditional.c mbprint.c print.c psqlscan.l- psqlscan.c simple_list.c string_utils.c recovery_gen.c);+ archive.c cancel.c conditional.c exit_utils.c mbprint.c option_utils.c print.c psqlscan.l+ psqlscan.c query_utils.c simple_list.c string_utils.c recovery_gen.c);
$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
$libpgport->AddDefine('FRONTEND');
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.listindex f3957bad6c..e4003b15a0 100644--- a/src/tools/pgindent/typedefs.list+++ b/src/tools/pgindent/typedefs.list@@ -101,6 +101,7 @@ AlterUserMappingStmt
AlteredTableInfo
AlternativeSubPlan
AlternativeSubPlanState
+AmCheckSettings
AnalyzeAttrComputeStatsFunc
AnalyzeAttrFetchFunc
AnalyzeForeignTable_function
@@ -404,6 +405,7 @@ ConnCacheEntry
ConnCacheKey
ConnStatusType
ConnType
+ConnectOptions
ConnectionStateEnum
ConsiderSplitContext
Const
--
2.21.1 (Apple Git-122.3)
=
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: mark.dilger@enterprisedb.com, thomas.munro@gmail.com, robertmhaas@gmail.com, tgl@sss.pgh.pa.us, pg@bowt.ie, x4mmm@yandex-team.ru, sfrost@snowman.net, michael@paquier.xyz, sulamul@gmail.com, dilipbalaut@gmail.com, andres@anarazel.de
Subject: Re: new heapcheck contrib module
In-Reply-To: <A4C2DFB8-436F-4802-BE8D-7A30290D52FA@enterprisedb.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox