From 9a8f2e9c3384527378c27a44da4d53aed76a33e4 Mon Sep 17 00:00:00 2001 From: Takahiro Itagaki Date: Thu, 14 May 2009 08:19:25 +0000 Subject: [PATCH] Fix ownership bug. New toast table, toast index, and toast type should not have been owned by the executor of pg_reorg, but by the original owner. --- bin/pg_reorg.c | 114 +++++++-------------- bin/pgut/pgut.c | 246 +++++++++++++++++++++++++++++---------------- bin/pgut/pgut.h | 97 ++++++++++++------ lib/Makefile | 2 +- lib/pgut/pgut-be.c | 61 +++++++++++ lib/pgut/pgut-be.h | 72 +++++++++++++ lib/reorg.c | 78 ++++++-------- 7 files changed, 424 insertions(+), 246 deletions(-) create mode 100755 lib/pgut/pgut-be.c create mode 100755 lib/pgut/pgut-be.h diff --git a/bin/pg_reorg.c b/bin/pg_reorg.c index 94407b8..36b23fd 100755 --- a/bin/pg_reorg.c +++ b/bin/pg_reorg.c @@ -8,18 +8,16 @@ * @brief Client Modules */ -#define PROGRAM_VERSION "1.0.4" -#define PROGRAM_URL "http://reorg.projects.postgresql.org/" -#define PROGRAM_EMAIL "reorg-general@lists.pgfoundry.org" +const char *PROGRAM_VERSION = "1.0.4"; +const char *PROGRAM_URL = "http://reorg.projects.postgresql.org/"; +const char *PROGRAM_EMAIL = "reorg-general@lists.pgfoundry.org"; #include "pgut/pgut.h" -#include "pqexpbuffer.h" #include #include #include -#define EXITCODE_HELP 2 #define APPLY_COUNT 1000 #define SQL_XID_SNAPSHOT_80300 \ @@ -37,12 +35,12 @@ " AND pid <> pg_backend_pid() AND transactionid = ANY($1) LIMIT 1" #define SQL_XID_SNAPSHOT \ - (PQserverVersion(current_conn) >= 80300 \ + (PQserverVersion(connection) >= 80300 \ ? SQL_XID_SNAPSHOT_80300 \ : SQL_XID_SNAPSHOT_80200) #define SQL_XID_ALIVE \ - (PQserverVersion(current_conn) >= 80300 \ + (PQserverVersion(connection) >= 80300 \ ? SQL_XID_ALIVE_80300 \ : SQL_XID_ALIVE_80200) @@ -80,7 +78,7 @@ typedef struct reorg_index } reorg_index; static void reorg_all_databases(const char *orderby); -static pqbool reorg_one_database(const char *orderby, const char *table); +static bool reorg_one_database(const char *orderby, const char *table); static void reorg_one_table(const reorg_table *table, const char *orderby); static char *getstr(PGresult *res, int row, int col); @@ -89,14 +87,14 @@ static Oid getoid(PGresult *res, int row, int col); #define SQLSTATE_INVALID_SCHEMA_NAME "3F000" #define SQLSTATE_LOCK_NOT_AVAILABLE "55P03" -static pqbool sqlstate_equals(PGresult *res, const char *state) +static bool sqlstate_equals(PGresult *res, const char *state) { return strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), state) == 0; } -static pqbool echo = false; -static pqbool verbose = false; -static pqbool quiet = false; +static bool echo = false; +static bool verbose = false; +static bool quiet = false; /* * The table begin re-organized. If not null, we need to cleanup temp @@ -125,11 +123,11 @@ const struct option pgut_longopts[] = { {NULL, 0, NULL, 0} }; -pqbool alldb = false; +bool alldb = false; const char *table = NULL; const char *orderby = NULL; -pqbool +bool pgut_argument(int c, const char *arg) { switch (c) @@ -147,13 +145,13 @@ pgut_argument(int c, const char *arg) alldb = true; break; case 't': - table = arg; + assign_option(&table, c, arg); break; case 'n': - orderby = ""; + assign_option(&orderby, c, ""); break; case 'o': - orderby = arg; + assign_option(&orderby, c, arg); break; default: return false; @@ -164,30 +162,18 @@ pgut_argument(int c, const char *arg) int main(int argc, char *argv[]) { - int exitcode; - - exitcode = pgut_getopt(argc, argv); - if (exitcode) - return exitcode; + parse_options(argc, argv); if (alldb) { if (table) - { - fprintf(stderr, "%s: cannot reorg a specific table in all databases\n", - progname); - exit(1); - } - + elog(ERROR, "cannot reorg a specific table in all databases"); reorg_all_databases(orderby); } else { if (!reorg_one_database(orderby, table)) - { - fprintf(stderr, "ERROR: %s is not installed\n", progname); - return 1; - } + elog(ERROR, "%s is not installed", PROGRAM_NAME); } return 0; @@ -209,13 +195,13 @@ reorg_all_databases(const char *orderby) for (i = 0; i < PQntuples(result); i++) { - pqbool ret; + bool ret; dbname = PQgetvalue(result, i, 0); if (!quiet) { - printf("%s: reorg database \"%s\"", progname, dbname); + printf("%s: reorg database \"%s\"", PROGRAM_NAME, dbname); fflush(stdout); } @@ -256,10 +242,10 @@ getoid(PGresult *res, int row, int col) /* * Call reorg_one_table for the target table or each table in a database. */ -static pqbool +static bool reorg_one_database(const char *orderby, const char *table) { - pqbool ret = true; + bool ret = true; PGresult *res; int i; int num; @@ -301,7 +287,7 @@ reorg_one_database(const char *orderby, const char *table) else { /* exit otherwise */ - printf("%s", PQerrorMessage(current_conn)); + printf("%s", PQerrorMessage(connection)); PQclear(res); exit(1); } @@ -324,10 +310,7 @@ reorg_one_database(const char *orderby, const char *table) table.ckid = getoid(res, i, c++); if (table.pkid == 0) - { - fprintf(stderr, "ERROR: relation \"%s\" has no primary key\n", table.target_name); - exit(1); - } + elog(ERROR, "relation \"%s\" has no primary key", table.target_name); table.create_pktype = getstr(res, i, c++); table.create_log = getstr(res, i, c++); @@ -343,10 +326,7 @@ reorg_one_database(const char *orderby, const char *table) { /* CLUSTER mode */ if (ckey == NULL) - { - fprintf(stderr, "ERROR: relation \"%s\" has no cluster key\n", table.target_name); - exit(1); - } + elog(ERROR, "relation \"%s\" has no cluster key", table.target_name); appendPQExpBuffer(&sql, "%s ORDER BY %s", create_table, ckey); table.create_table = sql.data; } @@ -455,11 +435,7 @@ reorg_one_table(const reorg_table *table, const char *orderby) " WHERE tgrelid = $1 AND tgname >= 'z_reorg_trigger' LIMIT 1", 1, params); if (PQntuples(res) > 0) - { - fprintf(stderr, "%s: trigger conflicted for %s\n", - progname, table->target_name); - exit(1); - } + elog(ERROR, "trigger conflicted for %s", table->target_name); command(table->create_pktype, 0, NULL); command(table->create_log, 0, NULL); @@ -482,7 +458,7 @@ reorg_one_table(const reorg_table *table, const char *orderby) command("BEGIN ISOLATION LEVEL SERIALIZABLE", 0, NULL); /* SET work_mem = maintenance_work_mem */ command("SELECT set_config('work_mem', current_setting('maintenance_work_mem'), true)", 0, NULL); - if (PQserverVersion(current_conn) >= 80300 && orderby && !orderby[0]) + if (PQserverVersion(connection) >= 80300 && orderby && !orderby[0]) command("SET LOCAL synchronize_seqscans = off", 0, NULL); res = execute(SQL_XID_SNAPSHOT, 0, NULL); vxid = strdup(PQgetvalue(res, 0, 0)); @@ -578,7 +554,7 @@ reorg_one_table(const reorg_table *table, const char *orderby) else { /* exit otherwise */ - printf("%s", PQerrorMessage(current_conn)); + printf("%s", PQerrorMessage(connection)); PQclear(res); exit(1); } @@ -606,7 +582,7 @@ reorg_one_table(const reorg_table *table, const char *orderby) } void -pgut_cleanup(pqbool fatal) +pgut_cleanup(bool fatal) { if (fatal) { @@ -622,11 +598,11 @@ pgut_cleanup(pqbool fatal) return; /* no needs to cleanup */ /* Rollback current transaction */ - if (current_conn) + if (connection) command("ROLLBACK", 0, NULL); /* Try reconnection if not available. */ - if (PQstatus(current_conn) != CONNECTION_OK) + if (PQstatus(connection) != CONNECTION_OK) reconnect(); /* do cleanup */ @@ -636,7 +612,7 @@ pgut_cleanup(pqbool fatal) } } -int +void pgut_help(void) { fprintf(stderr, @@ -645,33 +621,11 @@ pgut_help(void) " %s [OPTION]... [DBNAME]\n" "\nOptions:\n" " -a, --all reorg all databases\n" - " -d, --dbname=DBNAME database to reorg\n" " -t, --table=TABLE reorg specific table only\n" " -n, --no-order do vacuum full instead of cluster\n" " -o, --order-by=columns order by columns instead of cluster keys\n" " -e, --echo show the commands being sent to the server\n" " -q, --quiet don't write any messages\n" - " -v, --verbose display detailed information during processing\n" - " --help show this help, then exit\n" - " --version output version information, then exit\n" - "\nConnection options:\n" - " -h, --host=HOSTNAME database server host or socket directory\n" - " -p, --port=PORT database server port\n" - " -U, --username=USERNAME user name to connect as\n" - " -W, --password force password prompt\n", - progname, progname); -#ifdef PROGRAM_URL - fprintf(stderr,"\nRead the website for details. <" PROGRAM_URL ">\n"); -#endif -#ifdef PROGRAM_EMAIL - fprintf(stderr,"\nReport bugs to <" PROGRAM_EMAIL ">.\n"); -#endif - return EXITCODE_HELP; -} - -int -pgut_version(void) -{ - fprintf(stderr, "%s %s\n", progname, PROGRAM_VERSION); - return EXITCODE_HELP; + " -v, --verbose display detailed information during processing\n", + PROGRAM_NAME, PROGRAM_NAME); } diff --git a/bin/pgut/pgut.c b/bin/pgut/pgut.c index c36021a..7011680 100755 --- a/bin/pgut/pgut.c +++ b/bin/pgut/pgut.c @@ -1,29 +1,34 @@ -/* +/*------------------------------------------------------------------------- + * * pgut.c * * Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION + * + *------------------------------------------------------------------------- */ -#include "pgut.h" #include "postgres_fe.h" #include "libpq/pqsignal.h" #include -const char *progname = NULL; -const char *dbname = NULL; -char *host = NULL; -char *port = NULL; -char *username = NULL; -pqbool password = false; +#include "pgut.h" -/* Interrupted by SIGINT (Ctrl+C) ? */ -pqbool interrupted = false; +const char *PROGRAM_NAME = NULL; + +const char *dbname = NULL; +const char *host = NULL; +const char *port = NULL; +const char *username = NULL; +bool password = false; /* Database connections */ -PGconn *current_conn = NULL; +PGconn *connection = NULL; static PGcancel *volatile cancel_conn = NULL; +/* Interrupted by SIGINT (Ctrl+C) ? */ +static bool interrupted = false; + /* Connection routines */ static void init_cancel_handler(void); static void on_before_exec(PGconn *conn); @@ -31,7 +36,8 @@ static void on_after_exec(void); static void on_interrupt(void); static void on_cleanup(void); static void exit_or_abort(int exitcode); -const char *get_user_name(const char *progname); +static void help(void); +static const char *get_user_name(const char *PROGRAM_NAME); const char default_optstring[] = "d:h:p:U:W"; @@ -45,6 +51,9 @@ const struct option default_longopts[] = {NULL, 0, NULL, 0} }; +static const char *optstring = NULL; +static const struct option *longopts = NULL;; + static const char * merge_optstring(const char *opts) { @@ -83,29 +92,31 @@ merge_longopts(const struct option *opts) return result; } -int -pgut_getopt(int argc, char **argv) +void +parse_options(int argc, char **argv) { - const char *optstring; - const struct option *longopts; - int c; int optindex = 0; - progname = get_progname(argv[0]); + PROGRAM_NAME = get_progname(argv[0]); set_pglocale_pgservice(argv[0], "pgscripts"); - /* - * Help message and version are handled at first. - */ + /* Help message and version are handled at first. */ if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) - return pgut_help(); + { + help(); + exit_or_abort(HELP); + } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - return pgut_version(); + { + fprintf(stderr, "%s %s\n", PROGRAM_NAME, PROGRAM_VERSION); + exit_or_abort(HELP); + } } + /* Merge default and user options. */ optstring = merge_optstring(pgut_optstring); longopts = merge_longopts(pgut_longopts); @@ -113,26 +124,26 @@ pgut_getopt(int argc, char **argv) { switch (c) { + case 'd': + assign_option(&dbname, c, optarg); + break; case 'h': - host = optarg; + assign_option(&host, c, optarg); break; case 'p': - port = optarg; + assign_option(&port, c, optarg); break; case 'U': - username = optarg; + assign_option(&username, c, optarg); break; case 'W': password = true; break; - case 'd': - dbname = optarg; - break; default: if (!pgut_argument(c, optarg)) { - fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); - exit_or_abort(EXITCODE_ERROR); + fprintf(stderr, "Try \"%s --help\" for more information.\n", PROGRAM_NAME); + exit_or_abort(ERROR); } break; } @@ -142,10 +153,10 @@ pgut_getopt(int argc, char **argv) { if (!pgut_argument(0, argv[optind])) { - fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), - progname, argv[optind]); - fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); - exit_or_abort(EXITCODE_ERROR); + fprintf(stderr, "%s: too many command-line arguments (first is \"%s\")\n", + PROGRAM_NAME, argv[optind]); + fprintf(stderr, "Try \"%s --help\" for more information.\n", PROGRAM_NAME); + exit_or_abort(ERROR); } } @@ -155,9 +166,28 @@ pgut_getopt(int argc, char **argv) (void) (dbname || (dbname = getenv("PGDATABASE")) || (dbname = getenv("PGUSER")) || - (dbname = get_user_name(progname))); + (dbname = get_user_name(PROGRAM_NAME))); +} - return 0; +bool +assign_option(const char **value, int c, const char *arg) +{ + if (*value != NULL) + { + const struct option *opt; + for (opt = longopts; opt->name; opt++) + { + if (opt->val == c) + break; + } + if (opt->name) + elog(ERROR, "option -%c(--%s) should be specified only once", c, opt->name); + else + elog(ERROR, "option -%c should be specified only once", c); + return false; + } + *value = arg; + return true; } void @@ -182,11 +212,7 @@ reconnect(void) conn = PQsetdbLogin(host, port, NULL, NULL, dbname, username, pwd); if (!conn) - { - fprintf(stderr, _("%s: could not connect to database %s\n"), - progname, dbname); - exit_or_abort(EXITCODE_ERROR); - } + elog(ERROR, "could not connect to database %s", dbname); if (PQstatus(conn) == CONNECTION_BAD && #if PG_VERSION_NUM >= 80300 @@ -207,22 +233,19 @@ reconnect(void) /* check to see that the backend connection was successfully made */ if (PQstatus(conn) == CONNECTION_BAD) - { - fprintf(stderr, _("%s: could not connect to database %s: %s"), - progname, dbname, PQerrorMessage(conn)); - exit_or_abort(EXITCODE_ERROR); - } + elog(ERROR, "could not connect to database %s: %s", + dbname, PQerrorMessage(conn)); - current_conn = conn; + connection = conn; } void disconnect(void) { - if (current_conn) + if (connection) { - PQfinish(current_conn); - current_conn = NULL; + PQfinish(connection); + connection = NULL; } } @@ -231,11 +254,17 @@ execute_nothrow(const char *query, int nParams, const char **params) { PGresult *res; - on_before_exec(current_conn); + if (interrupted) + { + interrupted = false; + elog(ERROR, "%s: interrupted", PROGRAM_NAME); + } + + on_before_exec(connection); if (nParams == 0) - res = PQexec(current_conn, query); + res = PQexec(connection, query); else - res = PQexecParams(current_conn, query, nParams, NULL, params, NULL, NULL, 0); + res = PQexecParams(connection, query, nParams, NULL, params, NULL, NULL, 0); on_after_exec(); return res; @@ -247,27 +276,16 @@ execute_nothrow(const char *query, int nParams, const char **params) PGresult * execute(const char *query, int nParams, const char **params) { - if (interrupted) - { - interrupted = false; - fprintf(stderr, _("%s: interrupted\n"), progname); - } - else - { - PGresult *res = execute_nothrow(query, nParams, params); + PGresult *res = execute_nothrow(query, nParams, params); - if (PQresultStatus(res) == PGRES_TUPLES_OK || - PQresultStatus(res) == PGRES_COMMAND_OK) - return res; + if (PQresultStatus(res) == PGRES_TUPLES_OK || + PQresultStatus(res) == PGRES_COMMAND_OK) + return res; - fprintf(stderr, _("%s: query failed: %s"), - progname, PQerrorMessage(current_conn)); - fprintf(stderr, _("%s: query was: %s\n"), - progname, query); - PQclear(res); - } - - exit_or_abort(EXITCODE_ERROR); + fprintf(stderr, "%s: query failed: %s", PROGRAM_NAME, PQerrorMessage(connection)); + fprintf(stderr, "%s: query was: %s\n", PROGRAM_NAME, query); + PQclear(res); + exit_or_abort(ERROR); return NULL; /* keep compiler quiet */ } @@ -281,6 +299,48 @@ command(const char *query, int nParams, const char **params) PQclear(res); } +/* + * elog - log to stderr and exit if ERROR or FATAL + */ +void +elog(int elevel, const char *fmt, ...) +{ + va_list args; + + switch (elevel) + { + case LOG: + fputs("LOG: ", stderr); + break; + case INFO: + fputs("INFO: ", stderr); + break; + case NOTICE: + fputs("NOTICE: ", stderr); + break; + case WARNING: + fputs("WARNING: ", stderr); + break; + case ERROR: + fputs("ERROR: ", stderr); + break; + case FATAL: + fputs("FATAL: ", stderr); + break; + case PANIC: + fputs("PANIC: ", stderr); + break; + } + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fputc('\n', stderr); + fflush(stderr); + + if (elevel > 0) + exit_or_abort(elevel); +} #ifdef WIN32 static CRITICAL_SECTION cancelConnLock; @@ -357,12 +417,12 @@ on_interrupt(void) /* Send QueryCancel if we are processing a database query */ if (cancel_conn != NULL && PQcancel(cancel_conn, errbuf, sizeof(errbuf))) - fprintf(stderr, _("Cancel request sent\n")); + fprintf(stderr, "Cancel request sent\n"); errno = save_errno; /* just in case the write changed it */ } -static pqbool in_cleanup = false; +static bool in_cleanup = false; static void on_cleanup(void) @@ -388,33 +448,45 @@ exit_or_abort(int exitcode) } } +static void help(void) +{ + pgut_help(); + fprintf(stderr, "\nConnection options:\n"); + fprintf(stderr, " -d, --dbname=DBNAME database to connect\n"); + fprintf(stderr, " -h, --host=HOSTNAME database server host or socket directory\n"); + fprintf(stderr, " -p, --port=PORT database server port\n"); + fprintf(stderr, " -U, --username=USERNAME user name to connect as\n"); + fprintf(stderr, " -W, --password force password prompt\n"); + fprintf(stderr, "\nGeneric Options:\n"); + fprintf(stderr, " --help show this help, then exit\n"); + fprintf(stderr, " --version output version information, then exit\n\n"); + if (PROGRAM_URL) + fprintf(stderr, "Read the website for details. <%s>\n", PROGRAM_URL); + if (PROGRAM_EMAIL) + fprintf(stderr, "Report bugs to <%s>.\n", PROGRAM_EMAIL); +} + /* * Returns the current user name. */ -const char * -get_user_name(const char *progname) +static const char * +get_user_name(const char *PROGRAM_NAME) { #ifndef WIN32 struct passwd *pw; pw = getpwuid(geteuid()); if (!pw) - { - fprintf(stderr, _("%s: could not obtain information about current user: %s\n"), - progname, strerror(errno)); - exit_or_abort(EXITCODE_ERROR); - } + elog(ERROR, "%s: could not obtain information about current user: %s", + PROGRAM_NAME, strerror(errno)); return pw->pw_name; #else static char username[128]; /* remains after function exit */ DWORD len = sizeof(username) - 1; if (!GetUserName(username, &len)) - { - fprintf(stderr, _("%s: could not get current user name: %s\n"), - progname, strerror(errno)); - exit_or_abort(EXITCODE_ERROR); - } + elog(ERROR, "%s: could not get current user name: %s", + PROGRAM_NAME, strerror(errno)); return username; #endif } diff --git a/bin/pgut/pgut.h b/bin/pgut/pgut.h index 0dc759c..454d3ea 100755 --- a/bin/pgut/pgut.h +++ b/bin/pgut/pgut.h @@ -1,56 +1,61 @@ -/* +/*------------------------------------------------------------------------- + * * pgut.h * * Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION + * + *------------------------------------------------------------------------- */ #ifndef PGUT_H #define PGUT_H #include "libpq-fe.h" +#include "pqexpbuffer.h" + #include +#if !defined(C_H) && !defined(__cplusplus) +#ifndef bool +typedef char bool; +#endif +#ifndef true +#define true ((bool) 1) +#endif +#ifndef false +#define false ((bool) 0) +#endif +#endif + /* * pgut client variables and functions */ extern const char *pgut_optstring; extern const struct option pgut_longopts[]; -extern pqbool pgut_argument(int c, const char *arg); -extern int pgut_help(void); -extern int pgut_version(void); -extern void pgut_cleanup(pqbool fatal); - -/* - * exit codes - */ - -#define EXITCODE_OK 0 /**< normal exit */ -#define EXITCODE_ERROR 1 /**< normal error */ -#define EXITCODE_HELP 2 /**< help and version mode */ -#define EXITCODE_FATAL 3 /**< fatal error */ +extern bool pgut_argument(int c, const char *arg); +extern void pgut_help(void); +extern void pgut_cleanup(bool fatal); /* * pgut framework variables and functions */ -#ifndef true -#define true 1 -#endif -#ifndef false -#define false 0 -#endif +extern const char *PROGRAM_NAME; +extern const char *PROGRAM_VERSION; +extern const char *PROGRAM_URL; +extern const char *PROGRAM_EMAIL; -extern const char *progname; extern const char *dbname; -extern char *host; -extern char *port; -extern char *username; -extern pqbool password; -extern pqbool interrupted; -extern PGconn *current_conn; +extern const char *host; +extern const char *port; +extern const char *username; +extern bool password; -extern int pgut_getopt(int argc, char **argv); +extern PGconn *connection; + +extern void parse_options(int argc, char **argv); +extern bool assign_option(const char **value, int c, const char *arg); extern void reconnect(void); extern void disconnect(void); @@ -62,4 +67,40 @@ extern void command(const char *query, int nParams, const char **params); extern unsigned int sleep(unsigned int seconds); #endif +/* + * elog + */ +#define LOG (-4) +#define INFO (-3) +#define NOTICE (-2) +#define WARNING (-1) +#define ERROR 1 +#define HELP 2 +#define FATAL 3 +#define PANIC 4 + +#undef elog +extern void +elog(int elevel, const char *fmt, ...) +__attribute__((format(printf, 2, 3))); + +/* + * StringInfo + */ +#define StringInfoData PQExpBufferData +#define StringInfo PQExpBuffer +#define makeStringInfo createPQExpBuffer +#define initStringInfo initPQExpBuffer +#define freeStringInfo destroyPQExpBuffer +#define termStringInfo termPQExpBuffer +#define resetStringInfo resetPQExpBuffer +#define enlargeStringInfo enlargePQExpBuffer +/* +#define printfPQExpBuffer = resetStringInfo + appendStringInfo +*/ +#define appendStringInfo appendPQExpBuffer +#define appendStringInfoString appendPQExpBufferStr +#define appendStringInfoChar appendPQExpBufferChar +#define appendBinaryStringInfo appendBinaryPQExpBuffer + #endif /* PGUT_H */ diff --git a/lib/Makefile b/lib/Makefile index 721f8c3..bc5128a 100755 --- a/lib/Makefile +++ b/lib/Makefile @@ -3,7 +3,7 @@ # # Copyright (c) 2008-2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION # -SRCS = reorg.c +SRCS = reorg.c pgut/pgut-be.c OBJS = $(SRCS:.c=.o) MODULE_big = pg_reorg DATA_built = pg_reorg.sql diff --git a/lib/pgut/pgut-be.c b/lib/pgut/pgut-be.c new file mode 100755 index 0000000..2673622 --- /dev/null +++ b/lib/pgut/pgut-be.c @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- + * + * pgut-be.c + * + * Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "pgut-be.h" + +#if PG_VERSION_NUM < 80400 + +char * +text_to_cstring(const text *t) +{ + text *tunpacked = pg_detoast_datum_packed((struct varlena *) t); + int len = VARSIZE_ANY_EXHDR(tunpacked); + char *result; + + result = (char *) palloc(len + 1); + memcpy(result, VARDATA_ANY(tunpacked), len); + result[len] = '\0'; + + if (tunpacked != t) + pfree(tunpacked); + + return result; +} + +text * +cstring_to_text(const char *s) +{ + int len = strlen(s); + text *result = palloc(len + VARHDRSZ); + + SET_VARSIZE(result, len + VARHDRSZ); + memcpy(VARDATA(result), s, len); + + return result; +} + +int +SPI_execute_with_args(const char *src, + int nargs, Oid *argtypes, + Datum *values, const char *nulls, + bool read_only, long tcount) +{ + SPIPlanPtr plan; + int ret; + + plan = SPI_prepare(src, nargs, argtypes); + if (plan == NULL) + return SPI_result; + ret = SPI_execute_plan(plan, values, nulls, read_only, tcount); + SPI_freeplan(plan); + return ret; +} + +#endif diff --git a/lib/pgut/pgut-be.h b/lib/pgut/pgut-be.h new file mode 100755 index 0000000..af76c27 --- /dev/null +++ b/lib/pgut/pgut-be.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * pgut-be.h + * + * Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION + * + *------------------------------------------------------------------------- + */ + +#ifndef PGUT_BE_H +#define PGUT_BE_H + +#include "executor/spi.h" + +#if PG_VERSION_NUM < 80300 + +#define PGDLLIMPORT DLLIMPORT +#define SK_BT_DESC 0 /* Always ASC */ +#define SK_BT_NULLS_FIRST 0 /* Always NULLS LAST */ +#define MaxHeapTupleSize MaxTupleSize + +#define PG_GETARG_TEXT_PP(n) PG_GETARG_TEXT_P(n) +#define VARSIZE_ANY_EXHDR(v) (VARSIZE(v) - VARHDRSZ) +#define VARDATA_ANY(v) VARDATA(v) +#define SET_VARSIZE(v, sz) (VARATT_SIZEP(v) = (sz)) +#define pg_detoast_datum_packed(v) pg_detoast_datum(v) +#define DatumGetTextPP(v) DatumGetTextP(v) +#define ItemIdIsNormal(v) ItemIdIsUsed(v) +#define IndexBuildHeapScan(heap, index, info, sync, callback, state) \ + IndexBuildHeapScan((heap), (index), (info), (callback), (state)) +#define planner_rt_fetch(rti, root) \ + rt_fetch(rti, (root)->parse->rtable) +#define heap_sync(rel) ((void)0) +#define ItemIdIsDead(itemId) ItemIdDeleted(itemId) +#define GetCurrentCommandId(used) GetCurrentCommandId() +#define stringToQualifiedNameList(str) \ + stringToQualifiedNameList((str), "pg_bulkload") +#define setNewRelfilenode(rel, xid) \ + setNewRelfilenode((rel)) +#define PageAddItem(page, item, size, offnum, overwrite, is_heap) \ + PageAddItem((page), (item), (size), (offnum), LP_USED) + +typedef void *SPIPlanPtr; + +#endif + +#if PG_VERSION_NUM < 80400 + +#define MAIN_FORKNUM 0 +#define HEAP_INSERT_SKIP_WAL 0x0001 +#define HEAP_INSERT_SKIP_FSM 0x0002 + +#define relpath(rnode, forknum) relpath((rnode)) +#define smgrimmedsync(reln, forknum) smgrimmedsync((reln)) +#define smgrread(reln, forknum, blocknum, buffer) \ + smgrread((reln), (blocknum), (buffer)) +#define mdclose(reln, forknum) mdclose((reln)) +#define heap_insert(relation, tup, cid, options, bistate) \ + heap_insert((relation), (tup), (cid), true, true) +#define GetBulkInsertState() (NULL) +#define FreeBulkInsertState(bistate) ((void)0) + +typedef void *BulkInsertState; + +extern char *text_to_cstring(const text *t); +extern text *cstring_to_text(const char *s); +extern int SPI_execute_with_args(const char *src, int nargs, Oid *argtypes, + Datum *values, const char *nulls, bool read_only, long tcount); + +#endif + +#endif /* PGUT_BE_H */ diff --git a/lib/reorg.c b/lib/reorg.c index 81a4c4b..2e9ace5 100755 --- a/lib/reorg.c +++ b/lib/reorg.c @@ -21,20 +21,15 @@ #include "catalog/pg_type.h" #include "commands/tablecmds.h" #include "commands/trigger.h" -#include "executor/spi.h" #include "miscadmin.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/relcache.h" #include "utils/syscache.h" -PG_MODULE_MAGIC; +#include "pgut/pgut-be.h" -#if PG_VERSION_NUM < 80300 -#define SET_VARSIZE(PTR, len) (VARATT_SIZEP((PTR)) = (len)) -#define PGDLLIMPORT DLLIMPORT -typedef void *SPIPlanPtr; -#endif +PG_MODULE_MAGIC; Datum reorg_trigger(PG_FUNCTION_ARGS); Datum reorg_apply(PG_FUNCTION_ARGS); @@ -72,10 +67,6 @@ must_be_superuser(const char *func) } #if PG_VERSION_NUM < 80400 -static int SPI_execute_with_args(const char *src, int nargs, Oid *argtypes, - Datum *values, const char *nulls, - bool read_only, long tcount); -static text *cstring_to_text(const char * s); static void RenameRelationInternal(Oid myrelid, const char *newrelname, Oid namespaceId); #endif @@ -205,7 +196,7 @@ reorg_apply(PG_FUNCTION_ARGS) values_peek[0] = Int32GetDatum(NUMBER_OF_PROCESSING); else values_peek[0] = Int32GetDatum(Min(count - n, NUMBER_OF_PROCESSING)); - + reorg_execp(plan_peek, values_peek, nulls_peek, SPI_OK_SELECT); if (SPI_processed <= 0) break; @@ -222,7 +213,7 @@ reorg_apply(PG_FUNCTION_ARGS) { HeapTuple tuple; bool isnull; - + tuple = tuptable->vals[i]; values[0] = SPI_getbinval(tuple, desc, 1, &isnull); nulls[0] = ' '; @@ -230,7 +221,7 @@ reorg_apply(PG_FUNCTION_ARGS) nulls[1] = (isnull ? 'n' : ' '); values[2] = SPI_getbinval(tuple, desc, 3, &isnull); nulls[2] = (isnull ? 'n' : ' '); - + if (nulls[1] == 'n') { /* INSERT */ @@ -500,6 +491,8 @@ reorg_swap(PG_FUNCTION_ARGS) Oid oid2; Oid reltoastrelid2; Oid reltoastidxid2; + Oid owner1; + Oid owner2; /* authority check */ must_be_superuser("reorg_swap"); @@ -510,10 +503,12 @@ reorg_swap(PG_FUNCTION_ARGS) /* swap relfilenode and dependencies for tables. */ values[0] = ObjectIdGetDatum(oid); reorg_execd( - "SELECT X.oid, X.reltoastrelid, TX.reltoastidxid," - " Y.oid, Y.reltoastrelid, TY.reltoastidxid" - " FROM pg_class X LEFT JOIN pg_class TX ON X.reltoastrelid = TX.oid," - " pg_class Y LEFT JOIN pg_class TY ON Y.reltoastrelid = TY.oid" + "SELECT X.reltoastrelid, TX.reltoastidxid, X.relowner," + " Y.oid, Y.reltoastrelid, TY.reltoastidxid, Y.relowner" + " FROM pg_catalog.pg_class X LEFT JOIN pg_catalog.pg_class TX" + " ON X.reltoastrelid = TX.oid," + " pg_catalog.pg_class Y LEFT JOIN pg_catalog.pg_class TY" + " ON Y.reltoastrelid = TY.oid" " WHERE X.oid = $1" " AND Y.oid = ('reorg.table_' || X.oid)::regclass", 1, argtypes, values, nulls, SPI_OK_SELECT); @@ -527,11 +522,13 @@ reorg_swap(PG_FUNCTION_ARGS) tuple = tuptable->vals[0]; - reltoastrelid1 = getoid(tuple, desc, 2); - reltoastidxid1 = getoid(tuple, desc, 3); + reltoastrelid1 = getoid(tuple, desc, 1); + reltoastidxid1 = getoid(tuple, desc, 2); + owner1 = getoid(tuple, desc, 3); oid2 = getoid(tuple, desc, 4); reltoastrelid2 = getoid(tuple, desc, 5); reltoastidxid2 = getoid(tuple, desc, 6); + owner2 = getoid(tuple, desc, 7); /* should be all-or-nothing */ if ((reltoastrelid1 == InvalidOid || reltoastidxid1 == InvalidOid || @@ -543,6 +540,14 @@ reorg_swap(PG_FUNCTION_ARGS) reltoastrelid1, reltoastidxid1, reltoastrelid2, reltoastidxid2); } + /* change owner of new relation to original owner */ + if (owner1 != owner2) + { + ATExecChangeOwner(oid2, owner1, true); + CommandCounterIncrement(); + } + + /* swap heap and index files */ swap_heap_or_index_files(oid, oid2); CommandCounterIncrement(); @@ -550,7 +555,9 @@ reorg_swap(PG_FUNCTION_ARGS) values[0] = ObjectIdGetDatum(oid); reorg_execd( "SELECT X.oid, Y.oid" - " FROM pg_index I, pg_class X, pg_class Y" + " FROM pg_catalog.pg_index I," + " pg_catalog.pg_class X," + " pg_catalog.pg_class Y" " WHERE I.indrelid = $1" " AND I.indexrelid = X.oid" " AND Y.oid = ('reorg.index_' || X.oid)::regclass", @@ -897,35 +904,6 @@ swap_heap_or_index_files(Oid r1, Oid r2) extern PGDLLIMPORT bool allowSystemTableMods; -static int -SPI_execute_with_args(const char *src, - int nargs, Oid *argtypes, - Datum *values, const char *nulls, - bool read_only, long tcount) -{ - SPIPlanPtr plan; - int ret; - - plan = SPI_prepare(src, nargs, argtypes); - if (plan == NULL) - return SPI_result; - ret = SPI_execute_plan(plan, values, nulls, read_only, tcount); - SPI_freeplan(plan); - return ret; -} - -static text * -cstring_to_text(const char * s) -{ - int len = strlen(s); - text *result = palloc(len + VARHDRSZ); - - SET_VARSIZE(result, len + VARHDRSZ); - memcpy(VARDATA(result), s, len); - - return result; -} - static void RenameRelationInternal(Oid myrelid, const char *newrelname, Oid namespaceId) {