version 1.0.5.

- Disable autovacuum for working tables and update logs.
- Do ANALYZE automatically after reorg unless -Z, --no-analyze option
  is specified.
This commit is contained in:
Takahiro Itagaki
2009-05-25 07:06:38 +00:00
parent 9a8f2e9c33
commit 5fe3f037be
9 changed files with 232 additions and 101 deletions

View File

@ -8,7 +8,7 @@
* @brief Client Modules
*/
const char *PROGRAM_VERSION = "1.0.4";
const char *PROGRAM_VERSION = "1.0.5";
const char *PROGRAM_URL = "http://reorg.projects.postgresql.org/";
const char *PROGRAM_EMAIL = "reorg-general@lists.pgfoundry.org";
@ -92,9 +92,9 @@ static bool sqlstate_equals(PGresult *res, const char *state)
return strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), state) == 0;
}
static bool echo = false;
static bool verbose = false;
static bool quiet = false;
static bool analyze = true;
/*
* The table begin re-organized. If not null, we need to cleanup temp
@ -110,16 +110,14 @@ utoa(unsigned int value, char *buffer)
return buffer;
}
const char *pgut_optstring = "eqvat:no:";
const struct option pgut_longopts[] = {
{"echo", no_argument, NULL, 'e'},
const struct option pgut_options[] = {
{"quiet", no_argument, NULL, 'q'},
{"verbose", no_argument, NULL, 'v'},
{"all", no_argument, NULL, 'a'},
{"table", required_argument, NULL, 't'},
{"no-order", no_argument, NULL, 'n'},
{"order-by", required_argument, NULL, 'o'},
{"no-analyze", no_argument, NULL, 'Z'},
{NULL, 0, NULL, 0}
};
@ -132,9 +130,6 @@ pgut_argument(int c, const char *arg)
{
switch (c)
{
case 'e':
echo = true;
break;
case 'q':
quiet = true;
break;
@ -153,6 +148,9 @@ pgut_argument(int c, const char *arg)
case 'o':
assign_option(&orderby, c, arg);
break;
case 'Z':
analyze = false;
break;
default:
return false;
}
@ -249,9 +247,9 @@ reorg_one_database(const char *orderby, const char *table)
PGresult *res;
int i;
int num;
PQExpBufferData sql;
StringInfoData sql;
initPQExpBuffer(&sql);
initStringInfo(&sql);
reconnect();
@ -262,18 +260,18 @@ reorg_one_database(const char *orderby, const char *table)
command("SET client_min_messages = warning", 0, NULL);
/* acquire target tables */
appendPQExpBufferStr(&sql, "SELECT * FROM reorg.tables WHERE ");
appendStringInfoString(&sql, "SELECT * FROM reorg.tables WHERE ");
if (table)
{
appendPQExpBufferStr(&sql, "relid = $1::regclass");
res = execute_nothrow(sql.data, 1, &table);
appendStringInfoString(&sql, "relid = $1::regclass");
res = execute_elevel(sql.data, 1, &table, LOG);
}
else
{
appendPQExpBufferStr(&sql, "pkid IS NOT NULL");
appendStringInfoString(&sql, "pkid IS NOT NULL");
if (!orderby)
appendPQExpBufferStr(&sql, " AND ckid IS NOT NULL");
res = execute_nothrow(sql.data, 0, NULL);
appendStringInfoString(&sql, " AND ckid IS NOT NULL");
res = execute_elevel(sql.data, 0, NULL, LOG);
}
if (PQresultStatus(res) != PGRES_TUPLES_OK)
@ -321,13 +319,13 @@ reorg_one_database(const char *orderby, const char *table)
table.lock_table = getstr(res, i, c++);
ckey = getstr(res, i, c++);
resetPQExpBuffer(&sql);
resetStringInfo(&sql);
if (!orderby)
{
/* CLUSTER mode */
if (ckey == NULL)
elog(ERROR, "relation \"%s\" has no cluster key", table.target_name);
appendPQExpBuffer(&sql, "%s ORDER BY %s", create_table, ckey);
appendStringInfo(&sql, "%s ORDER BY %s", create_table, ckey);
table.create_table = sql.data;
}
else if (!orderby[0])
@ -338,7 +336,7 @@ reorg_one_database(const char *orderby, const char *table)
else
{
/* User specified ORDER BY */
appendPQExpBuffer(&sql, "%s ORDER BY %s", create_table, orderby);
appendStringInfo(&sql, "%s ORDER BY %s", create_table, orderby);
table.create_table = sql.data;
}
@ -354,7 +352,7 @@ reorg_one_database(const char *orderby, const char *table)
cleanup:
PQclear(res);
disconnect();
termPQExpBuffer(&sql);
termStringInfo(&sql);
return ret;
}
@ -387,12 +385,15 @@ apply_log(const reorg_table *table, int count)
static void
reorg_one_table(const reorg_table *table, const char *orderby)
{
PGresult *res;
const char *params[1];
int num;
int i;
char *vxid;
char buffer[12];
PGresult *res;
const char *params[1];
int num;
int i;
char *vxid;
char buffer[12];
StringInfoData sql;
initStringInfo(&sql);
if (verbose)
{
@ -430,16 +431,16 @@ reorg_one_table(const reorg_table *table, const char *orderby)
*/
params[0] = utoa(table->target_oid, buffer);
res = execute(
"SELECT 1 FROM pg_trigger"
" WHERE tgrelid = $1 AND tgname >= 'z_reorg_trigger' LIMIT 1",
1, params);
res = execute("SELECT reorg.conflicted_triggers($1)", 1, params);
if (PQntuples(res) > 0)
elog(ERROR, "trigger conflicted for %s", table->target_name);
elog(ERROR, "trigger %s conflicted for %s",
PQgetvalue(res, 0, 0), table->target_name);
command(table->create_pktype, 0, NULL);
command(table->create_log, 0, NULL);
command(table->create_trigger, 0, NULL);
printfStringInfo(&sql, "SELECT reorg.disable_autovacuum('reorg.log_%u')", table->target_oid);
command(sql.data, 0, NULL);
command("COMMIT", 0, NULL);
/*
@ -465,6 +466,8 @@ reorg_one_table(const reorg_table *table, const char *orderby)
PQclear(res);
command(table->delete_log, 0, NULL);
command(table->create_table, 0, NULL);
printfStringInfo(&sql, "SELECT reorg.disable_autovacuum('reorg.table_%u')", table->target_oid);
command(sql.data, 0, NULL);
command("COMMIT", 0, NULL);
/*
@ -537,7 +540,7 @@ reorg_one_table(const reorg_table *table, const char *orderby)
for (;;)
{
command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL);
res = execute_nothrow(table->lock_table, 0, NULL);
res = execute_elevel(table->lock_table, 0, NULL, NOTICE);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@ -577,8 +580,23 @@ reorg_one_table(const reorg_table *table, const char *orderby)
command("COMMIT", 0, NULL);
current_table = NULL;
free(vxid);
/*
* 7. Analyze.
* Note that current_table is already set to NULL here because analyze
* is an unimportant operation; No clean up even if failed.
*/
if (verbose)
fprintf(stderr, "---- analyze ----\n");
command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL);
printfStringInfo(&sql, "ANALYZE %s%s",
(verbose ? "VERBOSE " : ""), table->target_name);
command(sql.data, 0, NULL);
command("COMMIT", 0, NULL);
termStringInfo(&sql);
}
void
@ -624,7 +642,7 @@ pgut_help(void)
" -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"
" -Z, --no-analyze don't analyze at end\n"
" -q, --quiet don't write any messages\n"
" -v, --verbose display detailed information during processing\n",
PROGRAM_NAME, PROGRAM_NAME);

View File

@ -21,13 +21,14 @@ const char *host = NULL;
const char *port = NULL;
const char *username = NULL;
bool password = false;
bool debug = false;
/* Database connections */
PGconn *connection = NULL;
static PGcancel *volatile cancel_conn = NULL;
/* Interrupted by SIGINT (Ctrl+C) ? */
static bool interrupted = false;
bool interrupted = false;
/* Connection routines */
static void init_cancel_handler(void);
@ -39,40 +40,19 @@ static void exit_or_abort(int exitcode);
static void help(void);
static const char *get_user_name(const char *PROGRAM_NAME);
const char default_optstring[] = "d:h:p:U:W";
const struct option default_longopts[] =
const struct option default_options[] =
{
{"dbname", required_argument, NULL, 'd'},
{"host", required_argument, NULL, 'h'},
{"port", required_argument, NULL, 'p'},
{"username", required_argument, NULL, 'U'},
{"password", no_argument, NULL, 'W'},
{"debug", no_argument, NULL, '!'},
{NULL, 0, NULL, 0}
};
static const char *optstring = NULL;
static const struct option *longopts = NULL;;
static const char *
merge_optstring(const char *opts)
{
size_t len;
char *result;
if (opts == NULL)
return default_optstring;
len = strlen(opts);
if (len == 0)
return default_optstring;
result = malloc(len + lengthof(default_optstring));
memcpy(&result[0], opts, len);
memcpy(&result[len], default_optstring, lengthof(default_optstring));
return result;
}
static const struct option *
merge_longopts(const struct option *opts)
{
@ -80,23 +60,46 @@ merge_longopts(const struct option *opts)
struct option *result;
if (opts == NULL)
return default_longopts;
return default_options;
for (len = 0; opts[len].name; len++) { }
if (len == 0)
return default_longopts;
return default_options;
result = (struct option *) malloc((len + lengthof(default_longopts)) * sizeof(struct option));
result = (struct option *) malloc((len + lengthof(default_options)) * sizeof(struct option));
memcpy(&result[0], opts, len * sizeof(struct option));
memcpy(&result[len], default_longopts, lengthof(default_longopts) * sizeof(struct option));
memcpy(&result[len], default_options, lengthof(default_options) * sizeof(struct option));
return result;
}
static const char *
longopts_to_optstring(const struct option *opts)
{
size_t len;
char *result;
char *s;
for (len = 0; opts[len].name; len++) { }
result = malloc(len * 2 + 1);
s = result;
for (len = 0; opts[len].name; len++)
{
*s++ = opts[len].val;
if (opts[len].has_arg == required_argument)
*s++ = ':';
}
*s = '\0';
return result;
}
void
parse_options(int argc, char **argv)
{
int c;
int optindex = 0;
int c;
int optindex = 0;
const char *optstring;
PROGRAM_NAME = get_progname(argv[0]);
set_pglocale_pgservice(argv[0], "pgscripts");
@ -117,8 +120,8 @@ parse_options(int argc, char **argv)
}
/* Merge default and user options. */
optstring = merge_optstring(pgut_optstring);
longopts = merge_longopts(pgut_longopts);
longopts = merge_longopts(pgut_options);
optstring = longopts_to_optstring(longopts);
while ((c = getopt_long(argc, argv, optstring, longopts, &optindex)) != -1)
{
@ -139,6 +142,9 @@ parse_options(int argc, char **argv)
case 'W':
password = true;
break;
case '!':
debug = true;
break;
default:
if (!pgut_argument(c, optarg))
{
@ -250,7 +256,7 @@ disconnect(void)
}
PGresult *
execute_nothrow(const char *query, int nParams, const char **params)
execute_elevel(const char *query, int nParams, const char **params, int elevel)
{
PGresult *res;
@ -260,6 +266,19 @@ execute_nothrow(const char *query, int nParams, const char **params)
elog(ERROR, "%s: interrupted", PROGRAM_NAME);
}
/* write query to elog if debug */
if (debug)
{
int i;
if (strchr(query, '\n'))
elog(LOG, "(query)\n%s", query);
else
elog(LOG, "(query) %s", query);
for (i = 0; i < nParams; i++)
elog(LOG, "\t(param:%d) = %s", i, params[i] ? params[i] : "(null)");
}
on_before_exec(connection);
if (nParams == 0)
res = PQexec(connection, query);
@ -267,6 +286,17 @@ execute_nothrow(const char *query, int nParams, const char **params)
res = PQexecParams(connection, query, nParams, NULL, params, NULL, NULL, 0);
on_after_exec();
switch (PQresultStatus(res))
{
case PGRES_TUPLES_OK:
case PGRES_COMMAND_OK:
break;
default:
elog(elevel, "query failed: %squery was: %s",
PQerrorMessage(connection), query);
break;
}
return res;
}
@ -276,17 +306,7 @@ execute_nothrow(const char *query, int nParams, const char **params)
PGresult *
execute(const char *query, int nParams, const char **params)
{
PGresult *res = execute_nothrow(query, nParams, params);
if (PQresultStatus(res) == PGRES_TUPLES_OK ||
PQresultStatus(res) == PGRES_COMMAND_OK)
return res;
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 */
return execute_elevel(query, nParams, params, ERROR);
}
/*
@ -307,6 +327,9 @@ elog(int elevel, const char *fmt, ...)
{
va_list args;
if (!debug && elevel <= LOG)
return;
switch (elevel)
{
case LOG:
@ -457,7 +480,8 @@ static void help(void)
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, "\nGeneric options:\n");
fprintf(stderr, " --debug debug mode\n");
fprintf(stderr, " --help show this help, then exit\n");
fprintf(stderr, " --version output version information, then exit\n\n");
if (PROGRAM_URL)

View File

@ -30,8 +30,7 @@ typedef char bool;
/*
* pgut client variables and functions
*/
extern const char *pgut_optstring;
extern const struct option pgut_longopts[];
extern const struct option pgut_options[];
extern bool pgut_argument(int c, const char *arg);
extern void pgut_help(void);
@ -51,15 +50,17 @@ extern const char *host;
extern const char *port;
extern const char *username;
extern bool password;
extern bool debug;
extern PGconn *connection;
extern bool interrupted;
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);
extern PGresult *execute_nothrow(const char *query, int nParams, const char **params);
extern PGresult *execute_elevel(const char *query, int nParams, const char **params, int elevel);
extern PGresult *execute(const char *query, int nParams, const char **params);
extern void command(const char *query, int nParams, const char **params);
@ -87,6 +88,8 @@ __attribute__((format(printf, 2, 3)));
/*
* StringInfo
*/
#define STRINGINFO_H
#define StringInfoData PQExpBufferData
#define StringInfo PQExpBuffer
#define makeStringInfo createPQExpBuffer
@ -95,12 +98,29 @@ __attribute__((format(printf, 2, 3)));
#define termStringInfo termPQExpBuffer
#define resetStringInfo resetPQExpBuffer
#define enlargeStringInfo enlargePQExpBuffer
/*
#define printfPQExpBuffer = resetStringInfo + appendStringInfo
*/
#define printfStringInfo printfPQExpBuffer /* reset + append */
#define appendStringInfo appendPQExpBuffer
#define appendStringInfoString appendPQExpBufferStr
#define appendStringInfoChar appendPQExpBufferChar
#define appendBinaryStringInfo appendBinaryPQExpBuffer
/*
* import from postgres.h and catalog/genbki.h in 8.4
*/
#if PG_VERSION_NUM < 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#define CATALOG(name,oid) typedef struct CppConcat(FormData_,name)
#define BKI_BOOTSTRAP
#define BKI_SHARED_RELATION
#define BKI_WITHOUT_OIDS
#define DATA(x) extern int no_such_variable
#define DESCR(x) extern int no_such_variable
#define SHDESCR(x) extern int no_such_variable
typedef int aclitem;
#endif
#endif /* PGUT_H */