pg_reorg version 1.1.0.

- Add wait-timeout option and use SET statement_timeout instead of NOWAIT.
  This can avoid infinite NOWAIT loops to reorganize heavily accessed tables.
- Support native build with MSVC on Windows.
This commit is contained in:
Takahiro Itagaki
2010-03-25 07:13:16 +00:00
parent 8392b9462a
commit f3873ff55b
19 changed files with 2239 additions and 1228 deletions

View File

@ -3,7 +3,7 @@
#
# Copyright (c) 2008-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#
SRCS = pg_reorg.c pgut/pgut.c
SRCS = pg_reorg.c pgut/pgut.c pgut/pgut-fe.c
OBJS = $(SRCS:.c=.o)
PROGRAM = pg_reorg
REGRESS = init reorg

View File

@ -8,15 +8,16 @@
* @brief Client Modules
*/
const char *PROGRAM_VERSION = "1.0.8";
const char *PROGRAM_VERSION = "1.1.0";
const char *PROGRAM_URL = "http://reorg.projects.postgresql.org/";
const char *PROGRAM_EMAIL = "reorg-general@lists.pgfoundry.org";
#include "pgut/pgut.h"
#include "pgut/pgut-fe.h"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#define APPLY_COUNT 1000
@ -84,21 +85,22 @@ static void reorg_cleanup(bool fatal, void *userdata);
static char *getstr(PGresult *res, int row, int col);
static Oid getoid(PGresult *res, int row, int col);
static void lock_exclusive(const char *relid, const char *lock_query);
#define SQLSTATE_INVALID_SCHEMA_NAME "3F000"
#define SQLSTATE_LOCK_NOT_AVAILABLE "55P03"
#define SQLSTATE_QUERY_CANCELED "57014"
static bool sqlstate_equals(PGresult *res, const char *state)
{
return strcmp(PQresultErrorField(res, PG_DIAG_SQLSTATE), state) == 0;
}
static bool verbose = false;
static bool analyze = true;
static bool alldb = false;
static bool noorder = false;
static char *table = NULL;
static char *orderby = NULL;
static int wait_timeout = 60; /* in seconds */
/* buffer should have at least 11 bytes */
static char *
@ -110,11 +112,11 @@ utoa(unsigned int value, char *buffer)
static pgut_option options[] =
{
{ 'b', 'v', "verbose", &verbose },
{ 'b', 'a', "all", &alldb },
{ 's', 't', "table", &table },
{ 'b', 'n', "no-order", &noorder },
{ 's', 'o', "order-by", &orderby },
{ 'i', 'T', "wait-timeout", &wait_timeout },
{ 'B', 'Z', "no-analyze", &analyze },
{ 0 },
};
@ -129,7 +131,9 @@ main(int argc, char *argv[])
if (i == argc - 1)
dbname = argv[i];
else if (i < argc)
elog(ERROR_ARGS, "too many arguments");
ereport(ERROR,
(errcode(EINVAL),
errmsg("too many arguments")));
if (noorder)
orderby = "";
@ -137,13 +141,17 @@ main(int argc, char *argv[])
if (alldb)
{
if (table)
elog(ERROR, "cannot reorg a specific table in all databases");
ereport(ERROR,
(errcode(EINVAL),
errmsg("cannot reorg a specific table in all databases")));
reorg_all_databases(orderby);
}
else
{
if (!reorg_one_database(orderby, table))
elog(ERROR, "%s is not installed", PROGRAM_NAME);
ereport(ERROR,
(errcode(ENOENT),
errmsg("%s is not installed", PROGRAM_NAME)));
}
return 0;
@ -159,7 +167,7 @@ reorg_all_databases(const char *orderby)
int i;
dbname = "postgres";
reconnect();
reconnect(ERROR);
result = execute("SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", 0, NULL);
disconnect();
@ -169,7 +177,7 @@ reorg_all_databases(const char *orderby)
dbname = PQgetvalue(result, i, 0);
if (!quiet)
if (pgut_log_level >= INFO)
{
printf("%s: reorg database \"%s\"", PROGRAM_NAME, dbname);
fflush(stdout);
@ -177,7 +185,7 @@ reorg_all_databases(const char *orderby)
ret = reorg_one_database(orderby, NULL);
if (!quiet)
if (pgut_log_level >= INFO)
{
if (ret)
printf("\n");
@ -223,7 +231,10 @@ reorg_one_database(const char *orderby, const char *table)
initStringInfo(&sql);
reconnect();
reconnect(ERROR);
/* Disable statement timeout. */
command("SET statement_timeout = 0", 0, NULL);
/* Restrict search_path to system catalog. */
command("SET search_path = pg_catalog, pg_temp, public", 0, NULL);
@ -236,14 +247,14 @@ reorg_one_database(const char *orderby, const char *table)
if (table)
{
appendStringInfoString(&sql, "relid = $1::regclass");
res = execute_elevel(sql.data, 1, &table, LOG);
res = execute_elevel(sql.data, 1, &table, DEBUG2);
}
else
{
appendStringInfoString(&sql, "pkid IS NOT NULL");
if (!orderby)
appendStringInfoString(&sql, " AND ckid IS NOT NULL");
res = execute_elevel(sql.data, 0, NULL, LOG);
res = execute_elevel(sql.data, 0, NULL, DEBUG2);
}
if (PQresultStatus(res) != PGRES_TUPLES_OK)
@ -280,7 +291,9 @@ reorg_one_database(const char *orderby, const char *table)
table.ckid = getoid(res, i, c++);
if (table.pkid == 0)
elog(ERROR, "relation \"%s\" has no primary key", table.target_name);
ereport(ERROR,
(errcode(E_PG_COMMAND),
errmsg("relation \"%s\" has no primary key", table.target_name)));
table.create_pktype = getstr(res, i, c++);
table.create_log = getstr(res, i, c++);
@ -296,7 +309,9 @@ reorg_one_database(const char *orderby, const char *table)
{
/* CLUSTER mode */
if (ckey == NULL)
elog(ERROR, "relation \"%s\" has no cluster key", table.target_name);
ereport(ERROR,
(errcode(E_PG_COMMAND),
errmsg("relation \"%s\" has no cluster key", table.target_name)));
appendStringInfo(&sql, "%s ORDER BY %s", create_table, ckey);
table.create_table = sql.data;
}
@ -367,35 +382,30 @@ reorg_one_table(const reorg_table *table, const char *orderby)
initStringInfo(&sql);
if (verbose)
{
fprintf(stderr, "---- reorg_one_table ----\n");
fprintf(stderr, "target_name : %s\n", table->target_name);
fprintf(stderr, "target_oid : %u\n", table->target_oid);
fprintf(stderr, "target_toast : %u\n", table->target_toast);
fprintf(stderr, "target_tidx : %u\n", table->target_tidx);
fprintf(stderr, "pkid : %u\n", table->pkid);
fprintf(stderr, "ckid : %u\n", table->ckid);
fprintf(stderr, "create_pktype : %s\n", table->create_pktype);
fprintf(stderr, "create_log : %s\n", table->create_log);
fprintf(stderr, "create_trigger : %s\n", table->create_trigger);
fprintf(stderr, "create_table : %s\n", table->create_table);
fprintf(stderr, "delete_log : %s\n", table->delete_log);
fprintf(stderr, "lock_table : %s\n", table->lock_table);
fprintf(stderr, "sql_peek : %s\n", table->sql_peek);
fprintf(stderr, "sql_insert : %s\n", table->sql_insert);
fprintf(stderr, "sql_delete : %s\n", table->sql_delete);
fprintf(stderr, "sql_update : %s\n", table->sql_update);
fprintf(stderr, "sql_pop : %s\n", table->sql_pop);
}
elog(DEBUG2, "---- reorg_one_table ----");
elog(DEBUG2, "target_name : %s", table->target_name);
elog(DEBUG2, "target_oid : %u", table->target_oid);
elog(DEBUG2, "target_toast : %u", table->target_toast);
elog(DEBUG2, "target_tidx : %u", table->target_tidx);
elog(DEBUG2, "pkid : %u", table->pkid);
elog(DEBUG2, "ckid : %u", table->ckid);
elog(DEBUG2, "create_pktype : %s", table->create_pktype);
elog(DEBUG2, "create_log : %s", table->create_log);
elog(DEBUG2, "create_trigger : %s", table->create_trigger);
elog(DEBUG2, "create_table : %s", table->create_table);
elog(DEBUG2, "delete_log : %s", table->delete_log);
elog(DEBUG2, "lock_table : %s", table->lock_table);
elog(DEBUG2, "sql_peek : %s", table->sql_peek);
elog(DEBUG2, "sql_insert : %s", table->sql_insert);
elog(DEBUG2, "sql_delete : %s", table->sql_delete);
elog(DEBUG2, "sql_update : %s", table->sql_update);
elog(DEBUG2, "sql_pop : %s", table->sql_pop);
/*
* 1. Setup workspaces and a trigger.
*/
if (verbose)
fprintf(stderr, "---- setup ----\n");
command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL);
elog(DEBUG2, "---- setup ----");
lock_exclusive(utoa(table->target_oid, buffer), table->lock_table);
/*
* Check z_reorg_trigger is the trigger executed at last so that
@ -405,8 +415,10 @@ reorg_one_table(const reorg_table *table, const char *orderby)
res = execute("SELECT reorg.conflicted_triggers($1)", 1, params);
if (PQntuples(res) > 0)
elog(ERROR, "trigger %s conflicted for %s",
PQgetvalue(res, 0, 0), table->target_name);
ereport(ERROR,
(errcode(E_PG_COMMAND),
errmsg("trigger %s conflicted for %s",
PQgetvalue(res, 0, 0), table->target_name)));
command(table->create_pktype, 0, NULL);
command(table->create_log, 0, NULL);
@ -425,8 +437,7 @@ reorg_one_table(const reorg_table *table, const char *orderby)
/*
* 2. Copy tuples into temp table.
*/
if (verbose)
fprintf(stderr, "---- copy tuples ----\n");
elog(DEBUG2, "---- copy tuples ----");
command("BEGIN ISOLATION LEVEL SERIALIZABLE", 0, NULL);
/* SET work_mem = maintenance_work_mem */
@ -445,8 +456,7 @@ reorg_one_table(const reorg_table *table, const char *orderby)
/*
* 3. Create indexes on temp table.
*/
if (verbose)
fprintf(stderr, "---- create indexes ----\n");
elog(DEBUG2, "---- create indexes ----");
params[0] = utoa(table->target_oid, buffer);
res = execute("SELECT indexrelid,"
@ -462,12 +472,9 @@ reorg_one_table(const reorg_table *table, const char *orderby)
index.target_oid = getoid(res, i, c++);
index.create_index = getstr(res, i, c++);
if (verbose)
{
fprintf(stderr, "[%d]\n", i);
fprintf(stderr, "target_oid : %u\n", index.target_oid);
fprintf(stderr, "create_index : %s\n", index.create_index);
}
elog(DEBUG2, "[%d]", i);
elog(DEBUG2, "target_oid : %u", index.target_oid);
elog(DEBUG2, "create_index : %s", index.create_index);
/*
* NOTE: If we want to create multiple indexes in parallel,
@ -506,35 +513,8 @@ reorg_one_table(const reorg_table *table, const char *orderby)
/*
* 5. Swap.
*/
if (verbose)
fprintf(stderr, "---- swap ----\n");
for (;;)
{
command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL);
res = execute_elevel(table->lock_table, 0, NULL, NOTICE);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
break;
}
else if (sqlstate_equals(res, SQLSTATE_LOCK_NOT_AVAILABLE))
{
/* retry if lock conflicted */
PQclear(res);
command("ROLLBACK", 0, NULL);
sleep(1);
continue;
}
else
{
/* exit otherwise */
printf("%s", PQerrorMessage(connection));
PQclear(res);
exit(1);
}
}
elog(DEBUG2, "---- swap ----");
lock_exclusive(utoa(table->target_oid, buffer), table->lock_table);
apply_log(table, 0);
params[0] = utoa(table->target_oid, buffer);
command("SELECT reorg.reorg_swap($1)", 1, params);
@ -543,8 +523,7 @@ reorg_one_table(const reorg_table *table, const char *orderby)
/*
* 6. Drop.
*/
if (verbose)
fprintf(stderr, "---- drop ----\n");
elog(DEBUG2, "---- drop ----");
command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL);
params[0] = utoa(table->target_oid, buffer);
@ -561,12 +540,10 @@ reorg_one_table(const reorg_table *table, const char *orderby)
*/
if (analyze)
{
if (verbose)
fprintf(stderr, "---- analyze ----\n");
elog(DEBUG2, "---- analyze ----");
command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL);
printfStringInfo(&sql, "ANALYZE %s%s",
(verbose ? "VERBOSE " : ""), table->target_name);
printfStringInfo(&sql, "ANALYZE %s", table->target_name);
command(sql.data, 0, NULL);
command("COMMIT", 0, NULL);
}
@ -574,6 +551,79 @@ reorg_one_table(const reorg_table *table, const char *orderby)
termStringInfo(&sql);
}
/*
* Try acquire a table lock but avoid long time locks when conflict.
*/
static void
lock_exclusive(const char *relid, const char *lock_query)
{
time_t start = time(NULL);
int i;
for (i = 1; ; i++)
{
time_t duration;
char sql[1024];
PGresult *res;
int wait_msec;
command("BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL);
duration = time(NULL) - start;
if (duration > wait_timeout)
{
const char *cancel_query;
if (PQserverVersion(connection) >= 80400 &&
duration > wait_timeout * 2)
{
elog(WARNING, "terminating conflicted backends");
cancel_query =
"SELECT pg_terminate_backend(pid) FROM pg_locks"
" WHERE locktype = 'relation'"
" AND relation = $1 AND pid <> pg_backend_pid()";
}
else
{
elog(WARNING, "canceling conflicted backends");
cancel_query =
"SELECT pg_cancel_backend(pid) FROM pg_locks"
" WHERE locktype = 'relation'"
" AND relation = $1 AND pid <> pg_backend_pid()";
}
command(cancel_query, 1, &relid);
}
/* wait for a while to lock the table. */
wait_msec = Min(1000, i * 100);
snprintf(sql, lengthof(sql), "SET LOCAL statement_timeout = %d", wait_msec);
command(sql, 0, NULL);
res = execute_elevel(lock_query, 0, NULL, DEBUG2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
break;
}
else if (sqlstate_equals(res, SQLSTATE_QUERY_CANCELED))
{
/* retry if lock conflicted */
PQclear(res);
command("ROLLBACK", 0, NULL);
continue;
}
else
{
/* exit otherwise */
printf("%s", PQerrorMessage(connection));
PQclear(res);
exit(1);
}
}
command("RESET statement_timeout", 0, NULL);
}
/*
* The userdata pointing a table being re-organized. We need to cleanup temp
* objects before the program exits.
@ -598,7 +648,7 @@ reorg_cleanup(bool fatal, void *userdata)
/* Try reconnection if not available. */
if (PQstatus(connection) != CONNECTION_OK)
reconnect();
reconnect(ERROR);
/* do cleanup */
params[0] = utoa(table->target_oid, buffer);
@ -621,6 +671,6 @@ pgut_help(bool details)
printf(" -t, --table=TABLE reorg specific table only\n");
printf(" -n, --no-order do vacuum full instead of cluster\n");
printf(" -o, --order-by=columns order by columns instead of cluster keys\n");
printf(" -T, --wait-timeout=secs timeout to cancel other backends on conflict.\n");
printf(" -Z, --no-analyze don't analyze at end\n");
printf(" -v, --verbose display detailed information during processing\n");
}

687
bin/pgut/pgut-fe.c Executable file
View File

@ -0,0 +1,687 @@
/*-------------------------------------------------------------------------
*
* pgut-fe.c
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#define FRONTEND
#include "pgut-fe.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include <getopt_long.h>
#endif
char *dbname = NULL;
char *host = NULL;
char *port = NULL;
char *username = NULL;
char *password = NULL;
YesNo prompt_password = DEFAULT;
PGconn *connection = NULL;
static bool parse_pair(const char buffer[], char key[], char value[]);
static char *get_username(void);
/*
* the result is also available with the global variable 'connection'.
*/
void
reconnect(int elevel)
{
StringInfoData buf;
char *new_password;
disconnect();
initStringInfo(&buf);
if (dbname && dbname[0])
appendStringInfo(&buf, "dbname=%s ", dbname);
if (host && host[0])
appendStringInfo(&buf, "host=%s ", host);
if (port && port[0])
appendStringInfo(&buf, "port=%s ", port);
if (username && username[0])
appendStringInfo(&buf, "username=%s ", username);
if (password && password[0])
appendStringInfo(&buf, "password=%s ", password);
connection = pgut_connect(buf.data, prompt_password, elevel);
/* update password */
if (connection)
{
new_password = PQpass(connection);
if (new_password && (!password || strcmp(new_password, password)))
{
free(password);
password = new_password;
}
}
termStringInfo(&buf);
}
void
disconnect(void)
{
if (connection)
{
pgut_disconnect(connection);
connection = NULL;
}
}
static void
option_from_env(pgut_option options[])
{
size_t i;
for (i = 0; options && options[i].type; i++)
{
pgut_option *opt = &options[i];
char name[256];
size_t j;
const char *s;
const char *value;
if (opt->source > SOURCE_ENV ||
opt->allowed == SOURCE_DEFAULT || opt->allowed > SOURCE_ENV)
continue;
for (s = opt->lname, j = 0; *s && j < lengthof(name) - 1; s++, j++)
{
if (strchr("-_ ", *s))
name[j] = '_'; /* - to _ */
else
name[j] = toupper(*s);
}
name[j] = '\0';
if ((value = getenv(name)) != NULL)
pgut_setopt(opt, value, SOURCE_ENV);
}
}
/* compare two strings ignore cases and ignore -_ */
bool
pgut_keyeq(const char *lhs, const char *rhs)
{
for (; *lhs && *rhs; lhs++, rhs++)
{
if (strchr("-_ ", *lhs))
{
if (!strchr("-_ ", *rhs))
return false;
}
else if (ToLower(*lhs) != ToLower(*rhs))
return false;
}
return *lhs == '\0' && *rhs == '\0';
}
void
pgut_setopt(pgut_option *opt, const char *optarg, pgut_optsrc src)
{
const char *message;
if (opt == NULL)
{
fprintf(stderr, "Try \"%s --help\" for more information.\n", PROGRAM_NAME);
exit(EINVAL);
}
if (opt->source > src)
{
/* high prior value has been set already. */
return;
}
else if (src >= SOURCE_CMDLINE && opt->source >= src)
{
/* duplicated option in command line */
message = "specified only once";
}
else
{
/* can be overwritten if non-command line source */
opt->source = src;
switch (opt->type)
{
case 'b':
case 'B':
if (optarg == NULL)
{
*((bool *) opt->var) = (opt->type == 'b');
return;
}
else if (parse_bool(optarg, (bool *) opt->var))
{
return;
}
message = "a boolean";
break;
case 'f':
((pgut_optfn) opt->var)(opt, optarg);
return;
case 'i':
if (parse_int32(optarg, opt->var))
return;
message = "a 32bit signed integer";
break;
case 'u':
if (parse_uint32(optarg, opt->var))
return;
message = "a 32bit unsigned integer";
break;
case 'I':
if (parse_int64(optarg, opt->var))
return;
message = "a 64bit signed integer";
break;
case 'U':
if (parse_uint64(optarg, opt->var))
return;
message = "a 64bit unsigned integer";
break;
case 's':
if (opt->source != SOURCE_DEFAULT)
free(*(char **) opt->var);
*(char **) opt->var = pgut_strdup(optarg);
return;
case 't':
if (parse_time(optarg, opt->var))
return;
message = "a time";
break;
case 'y':
case 'Y':
if (optarg == NULL)
{
*(YesNo *) opt->var = (opt->type == 'y' ? YES : NO);
return;
}
else
{
bool value;
if (parse_bool(optarg, &value))
{
*(YesNo *) opt->var = (value ? YES : NO);
return;
}
}
message = "a boolean";
break;
default:
ereport(ERROR,
(errcode(EINVAL),
errmsg("invalid option type: %c", opt->type)));
return; /* keep compiler quiet */
}
}
if (isprint(opt->sname))
ereport(ERROR,
(errcode(EINVAL),
errmsg("option -%c, --%s should be %s: '%s'",
opt->sname, opt->lname, message, optarg)));
else
ereport(ERROR,
(errcode(EINVAL),
errmsg("option --%s should be %s: '%s'",
opt->lname, message, optarg)));
}
/*
* Get configuration from configuration file.
*/
void
pgut_readopt(const char *path, pgut_option options[], int elevel)
{
FILE *fp;
char buf[1024];
char key[1024];
char value[1024];
if (!options)
return;
if ((fp = pgut_fopen(path, "Rt")) == NULL)
return;
while (fgets(buf, lengthof(buf), fp))
{
size_t i;
for (i = strlen(buf); i > 0 && IsSpace(buf[i - 1]); i--)
buf[i - 1] = '\0';
if (parse_pair(buf, key, value))
{
for (i = 0; options[i].type; i++)
{
pgut_option *opt = &options[i];
if (pgut_keyeq(key, opt->lname))
{
if (opt->allowed == SOURCE_DEFAULT ||
opt->allowed > SOURCE_FILE)
elog(elevel, "option %s cannot specified in file", opt->lname);
else if (opt->source <= SOURCE_FILE)
pgut_setopt(opt, value, SOURCE_FILE);
break;
}
}
if (!options[i].type)
elog(elevel, "invalid option \"%s\"", key);
}
}
fclose(fp);
}
static const char *
skip_space(const char *str, const char *line)
{
while (IsSpace(*str)) { str++; }
return str;
}
static const char *
get_next_token(const char *src, char *dst, const char *line)
{
const char *s;
size_t i;
size_t j;
if ((s = skip_space(src, line)) == NULL)
return NULL;
/* parse quoted string */
if (*s == '\'')
{
s++;
for (i = 0, j = 0; s[i] != '\0'; i++)
{
if (s[i] == '\\')
{
i++;
switch (s[i])
{
case 'b':
dst[j] = '\b';
break;
case 'f':
dst[j] = '\f';
break;
case 'n':
dst[j] = '\n';
break;
case 'r':
dst[j] = '\r';
break;
case 't':
dst[j] = '\t';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
int k;
long octVal = 0;
for (k = 0;
s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
k++)
octVal = (octVal << 3) + (s[i + k] - '0');
i += k - 1;
dst[j] = ((char) octVal);
}
break;
default:
dst[j] = s[i];
break;
}
}
else if (s[i] == '\'')
{
i++;
/* doubled quote becomes just one quote */
if (s[i] == '\'')
dst[j] = s[i];
else
break;
}
else
dst[j] = s[i];
j++;
}
}
else
{
i = j = strcspn(s, "# \n\r\t\v");
memcpy(dst, s, j);
}
dst[j] = '\0';
return s + i;
}
static bool
parse_pair(const char buffer[], char key[], char value[])
{
const char *start;
const char *end;
key[0] = value[0] = '\0';
/*
* parse key
*/
start = buffer;
if ((start = skip_space(start, buffer)) == NULL)
return false;
end = start + strcspn(start, "=# \n\r\t\v");
/* skip blank buffer */
if (end - start <= 0)
{
if (*start == '=')
elog(WARNING, "syntax error in \"%s\"", buffer);
return false;
}
/* key found */
strncpy(key, start, end - start);
key[end - start] = '\0';
/* find key and value split char */
if ((start = skip_space(end, buffer)) == NULL)
return false;
if (*start != '=')
{
elog(WARNING, "syntax error in \"%s\"", buffer);
return false;
}
start++;
/*
* parse value
*/
if ((end = get_next_token(start, value, buffer)) == NULL)
return false;
if ((start = skip_space(end, buffer)) == NULL)
return false;
if (*start != '\0' && *start != '#')
{
elog(WARNING, "syntax error in \"%s\"", buffer);
return false;
}
return true;
}
/*
* execute - Execute a SQL and return the result.
*/
PGresult *
execute(const char *query, int nParams, const char **params)
{
return pgut_execute(connection, query, nParams, params);
}
PGresult *
execute_elevel(const char *query, int nParams, const char **params, int elevel)
{
return pgut_execute_elevel(connection, query, nParams, params, elevel);
}
/*
* command - Execute a SQL and discard the result.
*/
ExecStatusType
command(const char *query, int nParams, const char **params)
{
return pgut_command(connection, query, nParams, params);
}
static void
set_elevel(pgut_option *opt, const char *arg)
{
pgut_log_level = parse_elevel(arg);
}
static pgut_option default_options[] =
{
{ 'b', 'e', "echo" , &pgut_echo },
{ 'f', 'E', "elevel" , set_elevel },
{ 's', 'd', "dbname" , &dbname },
{ 's', 'h', "host" , &host },
{ 's', 'p', "port" , &port },
{ 's', 'U', "username" , &username },
{ 'y', 'w', "no-password" , &prompt_password },
{ 'Y', 'W', "password" , &prompt_password },
{ 0 }
};
static size_t
option_length(const pgut_option opts[])
{
size_t len;
for (len = 0; opts && opts[len].type; len++) { }
return len;
}
static pgut_option *
option_find(int c, pgut_option opts1[], pgut_option opts2[])
{
size_t i;
for (i = 0; opts1 && opts1[i].type; i++)
if (opts1[i].sname == c)
return &opts1[i];
for (i = 0; opts2 && opts2[i].type; i++)
if (opts2[i].sname == c)
return &opts2[i];
return NULL; /* not found */
}
/*
* Returns the current user name.
*/
static char *
get_username(void)
{
char *ret;
#ifndef WIN32
struct passwd *pw;
pw = getpwuid(geteuid());
ret = (pw ? pw->pw_name : NULL);
#else
static char username[128]; /* remains after function execute */
DWORD len = sizeof(username) - 1;
if (GetUserNameA(username, &len))
ret = username;
else
{
_dosmaperr(GetLastError());
ret = NULL;
}
#endif
if (ret == NULL)
ereport(ERROR,
(errcode_errno(),
errmsg("could not get current user name: ")));
return ret;
}
static int
option_has_arg(char type)
{
switch (type)
{
case 'b':
case 'B':
case 'y':
case 'Y':
return no_argument;
default:
return required_argument;
}
}
static void
option_copy(struct option dst[], const pgut_option opts[], size_t len)
{
size_t i;
for (i = 0; i < len; i++)
{
dst[i].name = opts[i].lname;
dst[i].has_arg = option_has_arg(opts[i].type);
dst[i].flag = NULL;
dst[i].val = opts[i].sname;
}
}
static struct option *
option_merge(const pgut_option opts1[], const pgut_option opts2[])
{
struct option *result;
size_t len1 = option_length(opts1);
size_t len2 = option_length(opts2);
size_t n = len1 + len2;
result = pgut_newarray(struct option, n + 1);
option_copy(result, opts1, len1);
option_copy(result + len1, opts2, len2);
memset(&result[n], 0, sizeof(pgut_option));
return result;
}
static char *
longopts_to_optstring(const struct option opts[])
{
size_t len;
char *result;
char *s;
for (len = 0; opts[len].name; len++) { }
result = pgut_malloc(len * 2 + 1);
s = result;
for (len = 0; opts[len].name; len++)
{
if (!isprint(opts[len].val))
continue;
*s++ = opts[len].val;
if (opts[len].has_arg != no_argument)
*s++ = ':';
}
*s = '\0';
return result;
}
int
pgut_getopt(int argc, char **argv, pgut_option options[])
{
int c;
int optindex = 0;
char *optstring;
struct option *longopts;
pgut_option *opt;
pgut_init(argc, argv);
/* Help message and version are handled at first. */
if (argc > 1)
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
help(true);
exit(1);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
fprintf(stderr, "%s %s\n", PROGRAM_NAME, PROGRAM_VERSION);
exit(1);
}
}
/* Merge default and user options. */
longopts = option_merge(default_options, options);
optstring = longopts_to_optstring(longopts);
/* Assign named options */
while ((c = getopt_long(argc, argv, optstring, longopts, &optindex)) != -1)
{
opt = option_find(c, default_options, options);
pgut_setopt(opt, optarg, SOURCE_CMDLINE);
}
/* Read environment variables */
option_from_env(options);
(void) (dbname ||
(dbname = getenv("PGDATABASE")) ||
(dbname = getenv("PGUSER")) ||
(dbname = get_username()));
return optind;
}
void
help(bool details)
{
pgut_help(details);
if (details)
{
printf("\nConnection options:\n");
printf(" -d, --dbname=DBNAME database to connect\n");
printf(" -h, --host=HOSTNAME database server host or socket directory\n");
printf(" -p, --port=PORT database server port\n");
printf(" -U, --username=USERNAME user name to connect as\n");
printf(" -w, --no-password never prompt for password\n");
printf(" -W, --password force password prompt\n");
}
printf("\nGeneric options:\n");
if (details)
{
printf(" -e, --echo echo queries\n");
printf(" -E, --elevel=LEVEL set output message level\n");
}
printf(" --help show this help, then exit\n");
printf(" --version output version information, then exit\n");
if (details && (PROGRAM_URL || PROGRAM_EMAIL))
{
printf("\n");
if (PROGRAM_URL)
printf("Read the website for details. <%s>\n", PROGRAM_URL);
if (PROGRAM_EMAIL)
printf("Report bugs to <%s>.\n", PROGRAM_EMAIL);
}
}

74
bin/pgut/pgut-fe.h Executable file
View File

@ -0,0 +1,74 @@
/*-------------------------------------------------------------------------
*
* pgut-fe.h
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PGUT_FE_H
#define PGUT_FE_H
#include "pgut.h"
typedef enum pgut_optsrc
{
SOURCE_DEFAULT,
SOURCE_ENV,
SOURCE_FILE,
SOURCE_CMDLINE,
SOURCE_CONST
} pgut_optsrc;
/*
* type:
* b: bool (true)
* B: bool (false)
* f: pgut_optfn
* i: 32bit signed integer
* u: 32bit unsigned integer
* I: 64bit signed integer
* U: 64bit unsigned integer
* s: string
* t: time_t
* y: YesNo (YES)
* Y: YesNo (NO)
*/
typedef struct pgut_option
{
char type;
char sname; /* short name */
const char *lname; /* long name */
void *var; /* pointer to variable */
pgut_optsrc allowed; /* allowed source */
pgut_optsrc source; /* actual source */
} pgut_option;
typedef void (*pgut_optfn) (pgut_option *opt, const char *arg);
extern char *dbname;
extern char *host;
extern char *port;
extern char *username;
extern char *password;
extern YesNo prompt_password;
extern PGconn *connection;
extern void pgut_help(bool details);
extern void help(bool details);
extern void disconnect(void);
extern void reconnect(int elevel);
extern PGresult *execute(const char *query, int nParams, const char **params);
extern PGresult *execute_elevel(const char *query, int nParams, const char **params, int elevel);
extern ExecStatusType command(const char *query, int nParams, const char **params);
extern int pgut_getopt(int argc, char **argv, pgut_option options[]);
extern void pgut_readopt(const char *path, pgut_option options[], int elevel);
extern void pgut_setopt(pgut_option *opt, const char *optarg, pgut_optsrc src);
extern bool pgut_keyeq(const char *lhs, const char *rhs);
#endif /* PGUT_FE_H */

File diff suppressed because it is too large Load Diff

View File

@ -11,23 +11,16 @@
#define PGUT_H
#include "c.h"
#include <assert.h>
#ifndef WIN32
#include <sys/time.h>
#include <unistd.h>
#endif
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include <assert.h>
#include <sys/time.h>
#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
#include "utils/elog.h"
#define INFINITE_STR "INFINITE"
@ -38,40 +31,6 @@ typedef enum YesNo
YES
} YesNo;
typedef enum pgut_optsrc
{
SOURCE_DEFAULT,
SOURCE_ENV,
SOURCE_FILE,
SOURCE_CMDLINE,
SOURCE_CONST
} pgut_optsrc;
/*
* type:
* b: bool (true)
* B: bool (false)
* f: pgut_optfn
* i: 32bit signed integer
* u: 32bit unsigned integer
* I: 64bit signed integer
* U: 64bit unsigned integer
* s: string
* t: time_t
* y: YesNo (YES)
* Y: YesNo (NO)
*/
typedef struct pgut_option
{
char type;
char sname; /* short name */
const char *lname; /* long name */
void *var; /* pointer to variable */
pgut_optsrc allowed; /* allowed source */
pgut_optsrc source; /* actual source */
} pgut_option;
typedef void (*pgut_optfn) (pgut_option *opt, const char *arg);
typedef void (*pgut_atexit_callback)(bool fatal, void *userdata);
/*
@ -82,52 +41,33 @@ extern const char *PROGRAM_VERSION;
extern const char *PROGRAM_URL;
extern const char *PROGRAM_EMAIL;
extern void pgut_help(bool details);
/*
* pgut framework variables and functions
*/
extern const char *dbname;
extern const char *host;
extern const char *port;
extern const char *username;
extern char *password;
extern bool debug;
extern bool quiet;
extern bool interrupted;
extern int pgut_log_level;
extern int pgut_abort_level;
extern bool pgut_echo;
#ifndef PGUT_NO_PROMPT
extern YesNo prompt_password;
#endif
extern PGconn *connection;
extern bool interrupted;
extern void help(bool details);
extern int pgut_getopt(int argc, char **argv, pgut_option options[]);
extern void pgut_readopt(const char *path, pgut_option options[], int elevel);
extern void pgut_setopt(pgut_option *opt, const char *optarg, pgut_optsrc src);
extern bool pgut_keyeq(const char *lhs, const char *rhs);
extern void pgut_init(int argc, char **argv);
extern void pgut_atexit_push(pgut_atexit_callback callback, void *userdata);
extern void pgut_atexit_pop(pgut_atexit_callback callback, void *userdata);
extern void pgut_putenv(const char *key, const char *value);
/*
* Database connections
*/
extern PGconn *pgut_connect(int elevel);
extern PGconn *pgut_connectdb(const char *conninfo, int elevel);
extern PGconn *pgut_connect(const char *info, YesNo prompt, int elevel);
extern void pgut_disconnect(PGconn *conn);
extern PGresult *pgut_execute(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern ExecStatusType pgut_command(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern bool pgut_send(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern void pgut_disconnect_all(void);
extern PGresult *pgut_execute(PGconn* conn, const char *query, int nParams, const char **params);
PGresult *pgut_execute_elevel(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern ExecStatusType pgut_command(PGconn* conn, const char *query, int nParams, const char **params);
extern bool pgut_commit(PGconn *conn);
extern void pgut_rollback(PGconn *conn);
extern bool pgut_send(PGconn* conn, const char *query, int nParams, const char **params);
extern int pgut_wait(int num, PGconn *connections[], struct timeval *timeout);
extern PGconn *reconnect_elevel(int elevel);
extern void reconnect(void);
extern void disconnect(void);
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);
/*
* memory allocators
*/
@ -139,37 +79,38 @@ extern char *strdup_trim(const char *str);
#define pgut_new(type) ((type *) pgut_malloc(sizeof(type)))
#define pgut_newarray(type, n) ((type *) pgut_malloc(sizeof(type) * (n)))
#define pgut_newvar(type, m, n) ((type *) pgut_malloc(offsetof(type, m) + (n)))
/*
* file operations
*/
extern FILE *pgut_fopen(const char *path, const char *mode, bool missing_ok);
extern void pgut_mkdir(const char *path);
extern FILE *pgut_fopen(const char *path, const char *mode);
extern bool pgut_mkdir(const char *path);
/*
* elog
*/
#define LOG (-4)
#define INFO (-3)
#define NOTICE (-2)
#define WARNING (-1)
#define HELP 1
#define ERROR 2
#define FATAL 3
#define PANIC 4
#define ERROR_SYSTEM 10 /* I/O or system error */
#define ERROR_NOMEM 11 /* memory exhausted */
#define ERROR_ARGS 12 /* some configurations are invalid */
#define ERROR_INTERRUPTED 13 /* interrupted by signal */
#define ERROR_PG_COMMAND 14 /* PostgreSQL query or command error */
#define ERROR_PG_CONNECT 15 /* PostgreSQL connection error */
#define E_PG_CONNECT (-1) /* PostgreSQL connection error */
#define E_PG_COMMAND (-2) /* PostgreSQL query or command error */
#undef elog
extern void
elog(int elevel, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
#undef ereport
#define ereport(elevel, rest) \
(pgut_errstart(elevel) ? (pgut_errfinish rest) : (void) 0)
extern void elog(int elevel, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
extern const char *format_elevel(int elevel);
extern int parse_elevel(const char *value);
extern int errcode_errno(void);
extern bool log_required(int elevel, int log_min_level);
extern bool pgut_errstart(int elevel);
extern void pgut_errfinish(int dummy, ...);
extern void pgut_error(int elevel, int code, const char *msg, const char *detail);
/*
* CHECK_FOR_INTERRUPTS
*/
#undef CHECK_FOR_INTERRUPTS
extern void CHECK_FOR_INTERRUPTS(void);
@ -209,6 +150,7 @@ extern void CHECK_FOR_INTERRUPTS(void);
#define appendStringInfoChar appendPQExpBufferChar
#define appendBinaryStringInfo appendBinaryPQExpBuffer
extern bool appendStringInfoVA(StringInfo str, const char *fmt, va_list args);
extern int appendStringInfoFile(StringInfo str, FILE *fp);
extern int appendStringInfoFd(StringInfo str, int fd);