1305 lines
42 KiB
C++
1305 lines
42 KiB
C++
/*
|
|
* psql - the PostgreSQL interactive terminal
|
|
*
|
|
* Copyright (c) 2000-2012, PostgreSQL Global Development Group
|
|
*
|
|
* src/bin/psql/startup.c
|
|
*/
|
|
#include "settings.h"
|
|
#include "postgres_fe.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#else /* WIN32 */
|
|
#include <io.h>
|
|
#include <win32.h>
|
|
#endif /* WIN32 */
|
|
|
|
#include "getopt_long.h"
|
|
|
|
#include <locale.h>
|
|
|
|
#include "command.h"
|
|
#include "common.h"
|
|
#include "describe.h"
|
|
#include "help.h"
|
|
#include "input.h"
|
|
#include "mainloop.h"
|
|
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
#include <time.h>
|
|
#include "pgtime.h"
|
|
#include "mb/pg_wchar.h"
|
|
|
|
#ifndef WIN32
|
|
#include "libpq/libpq-int.h"
|
|
#endif
|
|
|
|
/*
|
|
* Global psql options
|
|
*/
|
|
PsqlSettings pset;
|
|
/* Used for change child process name in gsql parallel execute mode. */
|
|
char* argv_para;
|
|
int argv_num;
|
|
|
|
/* The version of libpq */
|
|
extern const char* libpqVersionString;
|
|
|
|
#ifndef WIN32
|
|
#define SYSPSQLRC "gsqlrc"
|
|
#define PSQLRC ".gsqlrc"
|
|
#else
|
|
#define SYSPSQLRC "gsqlrc"
|
|
#define PSQLRC "gsqlrc.conf"
|
|
#endif
|
|
|
|
/*
|
|
* Structures to pass information between the option parsing routine
|
|
* and the main function
|
|
*/
|
|
enum _actions { ACT_NOTHING = 0, ACT_SINGLE_SLASH, ACT_LIST_DB, ACT_SINGLE_QUERY, ACT_FILE };
|
|
|
|
struct adhoc_opts {
|
|
char* dbname;
|
|
char* host;
|
|
char* port;
|
|
char* username;
|
|
char* passwd;
|
|
char* logfilename;
|
|
enum _actions action;
|
|
char* action_string;
|
|
bool no_readline;
|
|
bool no_psqlrc;
|
|
bool single_txn;
|
|
};
|
|
|
|
static void parse_psql_options(int argc, char* const argv[], struct adhoc_opts* options);
|
|
static void process_psqlrc(const char* argv0);
|
|
static void process_psqlrc_file(char* filename);
|
|
static void showVersion(void);
|
|
static void EstablishVariableSpace(void);
|
|
static char* GetEnvStr(const char* env);
|
|
|
|
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
|
|
bool check_parseonly_parameter(adhoc_opts options)
|
|
{
|
|
if (pset.parseonly) {
|
|
if (options.action != ACT_FILE) {
|
|
fprintf(stderr, "%s: %s", pset.progname, "-f and -g argument must be set together\n");
|
|
exit(EXIT_USER);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
static void set_aes_key(const char* dencrypt_key);
|
|
|
|
#ifdef HAVE_CE
|
|
#define PARAMS_ARRAY_SIZE 11
|
|
#else
|
|
#define PARAMS_ARRAY_SIZE 10
|
|
#endif
|
|
/*
|
|
*
|
|
* main
|
|
*
|
|
*/
|
|
int main(int argc, char* argv[])
|
|
{
|
|
struct adhoc_opts options;
|
|
int successResult;
|
|
char* password = NULL;
|
|
char* password_prompt = NULL;
|
|
bool new_pass = false;
|
|
errno_t rc;
|
|
bool isparseonly = false;
|
|
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
struct timeval aes_start_time;
|
|
struct timeval aes_end_time;
|
|
pg_time_t total_time = 0;
|
|
|
|
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("gsql"));
|
|
|
|
if (strcmp(libpqVersionString, DEF_GS_VERSION) != 0) {
|
|
fprintf(stderr,
|
|
"[Warning]: The \"libpq.so\" loaded mismatch the version of gsql, "
|
|
"please check it.\n"
|
|
"expected: %s\nresult: %s\n",
|
|
DEF_GS_VERSION,
|
|
libpqVersionString);
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
exit(EXIT_FAILURE);
|
|
#endif
|
|
}
|
|
|
|
if (argc > 1) {
|
|
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) {
|
|
usage();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) {
|
|
showVersion();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
setvbuf(stderr, NULL, _IONBF, 0);
|
|
#endif
|
|
|
|
ignore_quit_signal();
|
|
setup_cancel_handler();
|
|
|
|
/* Server will strictly check the name message of client tool, it can't be changed. */
|
|
pset.progname = "gsql";
|
|
|
|
pset.db = NULL;
|
|
setDecimalLocale();
|
|
pset.encoding = PQenv2encoding();
|
|
pset.queryFout = stdout;
|
|
pset.queryFoutPipe = false;
|
|
pset.cur_cmd_source = stdin;
|
|
pset.cur_cmd_interactive = false;
|
|
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
|
|
pset.parseonly = false;
|
|
#endif
|
|
/* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */
|
|
pset.popt.topt.format = PRINT_ALIGNED;
|
|
pset.popt.topt.border = 1;
|
|
pset.popt.topt.pager = 1;
|
|
pset.popt.topt.start_table = true;
|
|
pset.popt.topt.stop_table = true;
|
|
pset.popt.topt.default_footer = true;
|
|
pset.popt.topt.fieldSep.separator = NULL;
|
|
pset.popt.topt.tableAttr = NULL;
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
initDecryptInfo(&pset.decryptInfo);
|
|
|
|
/* maintance mode is off on default */
|
|
pset.maintance = false;
|
|
/* client encryption is off on default */
|
|
pset.enable_client_encryption = false;
|
|
/* We must get COLUMNS here before readline() sets it */
|
|
char* columnsEnvStr = GetEnvStr("COLUMNS");
|
|
pset.popt.topt.env_columns = columnsEnvStr != NULL ? atoi(columnsEnvStr) : 0;
|
|
if (columnsEnvStr != NULL) {
|
|
free(columnsEnvStr);
|
|
columnsEnvStr = NULL;
|
|
}
|
|
|
|
pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
|
|
|
|
pset.getPassword = TRI_DEFAULT;
|
|
|
|
// Init retry variables.
|
|
//
|
|
pset.retry_times = 0;
|
|
rc = memset_s(pset.retry_sqlstate, sizeof(pset.retry_sqlstate), 0, sizeof(pset.retry_sqlstate));
|
|
securec_check_c(rc, "\0", "\0");
|
|
pset.retry_on = false;
|
|
pset.retry_sleep = false;
|
|
pset.max_retry_times = 0;
|
|
|
|
EstablishVariableSpace();
|
|
|
|
if (!SetVariable(pset.vars, "VERSION", PG_VERSION_STR)) {
|
|
psql_error("set variable %s failed.\n", "VERSION");
|
|
}
|
|
|
|
/* Default values for variables */
|
|
SetVariableBool(pset.vars, "AUTOCOMMIT");
|
|
if (!SetVariable(pset.vars, "VERBOSITY", "default")) {
|
|
psql_error("set variable %s failed.\n", "VERBOSITY");
|
|
}
|
|
if (!SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1)) {
|
|
psql_error("set variable %s failed.\n", "PROMPT1");
|
|
}
|
|
if (!SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2)) {
|
|
psql_error("set variable %s failed.\n", "PROMPT2");
|
|
}
|
|
if (!SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3)) {
|
|
psql_error("set variable %s failed.\n", "PROMPT3");
|
|
}
|
|
|
|
/* init options.action_string */
|
|
options.action_string = NULL;
|
|
parse_psql_options(argc, argv, &options);
|
|
|
|
/* Save the argv and argc for change process name. */
|
|
argv_para = argv[0];
|
|
argv_num = argc;
|
|
|
|
if ((pset.popt.topt.fieldSep.separator == NULL) && !pset.popt.topt.fieldSep.separator_zero) {
|
|
pset.popt.topt.fieldSep.separator = pg_strdup(DEFAULT_FIELD_SEP);
|
|
pset.popt.topt.fieldSep.separator_zero = false;
|
|
}
|
|
if ((pset.popt.topt.recordSep.separator == NULL) && !pset.popt.topt.recordSep.separator_zero) {
|
|
pset.popt.topt.recordSep.separator = pg_strdup(DEFAULT_RECORD_SEP);
|
|
pset.popt.topt.recordSep.separator_zero = false;
|
|
}
|
|
|
|
if (options.username == NULL)
|
|
password_prompt = pg_strdup(_("Password: "));
|
|
else {
|
|
errno_t err = EOK;
|
|
size_t len = strlen(_("Password for user %s: ")) - 2 + strlen(options.username) + 1;
|
|
password_prompt = (char*)malloc(len);
|
|
if (password_prompt == NULL)
|
|
exit(EXIT_FAILURE);
|
|
err = sprintf_s(password_prompt, len, _("Password for user %s: "), options.username);
|
|
check_sprintf_s(err);
|
|
}
|
|
|
|
password = options.passwd;
|
|
if (pset.getPassword == TRI_YES && password == NULL)
|
|
password = simple_prompt(password_prompt, MAX_PASSWORD_LENGTH, false);
|
|
|
|
/* loop until we have a password if requested by backend */
|
|
do {
|
|
const char** keywords = (const char**)pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
|
|
char** values = (char**)pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
|
|
bool* values_free = (bool*)pg_malloc(PARAMS_ARRAY_SIZE * sizeof(bool));
|
|
char* tmpenv = GetEnvStr("PGCLIENTENCODING");
|
|
|
|
rc = memset_s(values_free, PARAMS_ARRAY_SIZE * sizeof(bool), 0, PARAMS_ARRAY_SIZE * sizeof(bool));
|
|
securec_check_c(rc, "\0", "\0");
|
|
char* encodetext = NULL;
|
|
|
|
keywords[0] = "host";
|
|
values[0] = options.host;
|
|
keywords[1] = "port";
|
|
values[1] = options.port;
|
|
keywords[2] = "user";
|
|
values[2] = options.username;
|
|
keywords[3] = "password";
|
|
values[3] = password;
|
|
keywords[4] = "dbname";
|
|
values[4] = (char*)((options.action == ACT_LIST_DB && options.dbname == NULL) ? "postgres" : options.dbname);
|
|
keywords[5] = "fallback_application_name";
|
|
values[5] = (char*)(pset.progname);
|
|
keywords[6] = "client_encoding";
|
|
values[6] = (char*)((pset.notty || (tmpenv != NULL)) ? NULL : "auto");
|
|
keywords[7] = "connect_timeout";
|
|
values[7] = CONNECT_TIMEOUT;
|
|
#ifdef HAVE_CE
|
|
keywords[8] = "enable_ce";
|
|
values[8] = (pset.enable_client_encryption) ? (char*)"1" : NULL;
|
|
#endif
|
|
if (pset.maintance) {
|
|
keywords[PARAMS_ARRAY_SIZE - 2] = "options";
|
|
values[PARAMS_ARRAY_SIZE - 2] = (char *)("-c xc_maintenance_mode=on");
|
|
} else {
|
|
keywords[PARAMS_ARRAY_SIZE - 2] = NULL;
|
|
values[PARAMS_ARRAY_SIZE - 2] = NULL;
|
|
}
|
|
|
|
if (tmpenv != NULL)
|
|
free(tmpenv);
|
|
tmpenv = NULL;
|
|
keywords[PARAMS_ARRAY_SIZE - 1] = NULL;
|
|
values[PARAMS_ARRAY_SIZE - 1] = NULL;
|
|
new_pass = false;
|
|
pset.db = PQconnectdbParams(keywords, values, (int)true);
|
|
|
|
if (pset.db->sock < 0) {
|
|
fprintf(stderr,
|
|
"failed to connect %s:%s.\n",
|
|
pset.db->pghost == NULL ? "Unknown" : pset.db->pghost,
|
|
pset.db->pgport == NULL ? "Unknown" : pset.db->pgport);
|
|
PQfinish(pset.db);
|
|
exit(EXIT_BADCONN);
|
|
}
|
|
|
|
/* Encode password and stored in memory here for gsql parallel execute function. */
|
|
if ((password != NULL) && strlen(password) != 0) {
|
|
encodetext = SEC_encodeBase64((char*)password, strlen(password));
|
|
if (encodetext == NULL) {
|
|
fprintf(stderr, "%s: encode the parallel connect value failed.", pset.progname);
|
|
PQfinish(pset.db);
|
|
exit(EXIT_BADCONN);
|
|
}
|
|
values[3] = encodetext;
|
|
}
|
|
|
|
/* Stored connection and guc info for new connections in gsql parallel mode. */
|
|
values_free[3] = true;
|
|
|
|
pset.connInfo.keywords = keywords;
|
|
pset.connInfo.values = values;
|
|
pset.connInfo.values_free = values_free;
|
|
pset.num_guc_stmt = 0;
|
|
pset.guc_stmt = NULL;
|
|
|
|
/* Clear password related memory to avoid leaks when core. */
|
|
if (password != NULL) {
|
|
rc = memset_s(password, strlen(password), 0, strlen(password));
|
|
securec_check_c(rc, "\0", "\0");
|
|
}
|
|
|
|
/* Whenever occur connect error of password, we will try ask for user input again. */
|
|
if (PQstatus(pset.db) == CONNECTION_BAD && (strstr(PQerrorMessage(pset.db), "password") != NULL) &&
|
|
password == NULL && pset.getPassword != TRI_NO) {
|
|
PQfinish(pset.db);
|
|
password = simple_prompt(password_prompt, MAX_PASSWORD_LENGTH, false);
|
|
new_pass = true;
|
|
free(keywords);
|
|
keywords = NULL;
|
|
free(values);
|
|
values = NULL;
|
|
free(values_free);
|
|
values_free = NULL;
|
|
}
|
|
} while (new_pass);
|
|
if (options.passwd == NULL) {
|
|
if (password == pset.connInfo.values[3]) {
|
|
pset.connInfo.values_free[3] = false;
|
|
pset.connInfo.values[3] = NULL;
|
|
}
|
|
free(password);
|
|
password = NULL;
|
|
}
|
|
free(password_prompt);
|
|
|
|
/* Clear password related memory to avoid leaks when core. */
|
|
if (options.passwd != NULL) {
|
|
rc = memset_s(options.passwd, strlen(options.passwd), 0, strlen(options.passwd));
|
|
securec_check_c(rc, "\0", "\0");
|
|
free(options.passwd);
|
|
options.passwd = NULL;
|
|
}
|
|
#ifndef WIN32
|
|
if (pset.db->pgpass != NULL) {
|
|
rc = memset_s(pset.db->pgpass, strlen(pset.db->pgpass), 0, strlen(pset.db->pgpass));
|
|
securec_check_c(rc, "\0", "\0");
|
|
}
|
|
#endif
|
|
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
|
|
isparseonly = check_parseonly_parameter(options);
|
|
#endif
|
|
if (PQstatus(pset.db) == CONNECTION_BAD && !isparseonly) {
|
|
fprintf(stderr, "%s: %s", pset.progname, PQerrorMessage(pset.db));
|
|
PQfinish(pset.db);
|
|
exit(EXIT_BADCONN);
|
|
}
|
|
|
|
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
|
|
|
|
SyncVariables();
|
|
|
|
if (options.action == ACT_LIST_DB && !isparseonly) {
|
|
int success;
|
|
|
|
if (!options.no_psqlrc) {
|
|
process_psqlrc(argv[0]);
|
|
}
|
|
success = listAllDbs((int)false);
|
|
PQfinish(pset.db);
|
|
exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
}
|
|
|
|
if (options.logfilename != NULL) {
|
|
canonicalize_path(options.logfilename);
|
|
pset.logfile = fopen(options.logfilename, "a");
|
|
if (pset.logfile == NULL)
|
|
fprintf(stderr,
|
|
_("%s: could not open log file \"%s\": %s\n"),
|
|
pset.progname,
|
|
options.logfilename,
|
|
strerror(errno));
|
|
else {
|
|
int logfd = fileno(pset.logfile);
|
|
/* change the privilege of log file for security. */
|
|
if ((logfd >= 0) && (-1 == fchmod(logfd, S_IRUSR | S_IWUSR)))
|
|
fprintf(stderr, _("could not set permissions of file \"%s\"\n"), options.logfilename);
|
|
logfd = -1;
|
|
}
|
|
}
|
|
|
|
/* show warning message when the client and server have diffrent version numbers */
|
|
if (!isparseonly) {
|
|
/* show warning message when the client and server have diffrent version numbers */
|
|
(void)client_server_version_check(pset.db);
|
|
} else {
|
|
/*
|
|
we could not get the encoding from the server so let's get the encoding from the environment variable
|
|
*/
|
|
if (pset.encoding == -1) {
|
|
char *encodingStr = GetEnvStr("PGCLIENTENCODING");
|
|
if (encodingStr) {
|
|
check_env_value(encodingStr);
|
|
pset.encoding = pg_char_to_encoding(encodingStr);
|
|
free(encodingStr);
|
|
encodingStr = NULL;
|
|
}
|
|
}
|
|
/*
|
|
again we could not get the encoding so let's try the default encoding of ASCII
|
|
All we want is to print to the screen for the debugging so why not.
|
|
*/
|
|
if (pset.encoding == -1) {
|
|
pset.encoding = PG_SQL_ASCII;
|
|
}
|
|
}
|
|
/* Now find something to do */
|
|
/* process file given by -f */
|
|
if (options.action == ACT_FILE) {
|
|
if (!options.no_psqlrc) {
|
|
process_psqlrc(argv[0]);
|
|
}
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
gettimeofday(&aes_start_time, NULL);
|
|
successResult = process_file(options.action_string, options.single_txn, false);
|
|
gettimeofday(&aes_end_time, NULL);
|
|
total_time = 1000 * (aes_end_time.tv_sec - aes_start_time.tv_sec) +
|
|
(aes_end_time.tv_usec - aes_start_time.tv_usec) / 1000;
|
|
if (!isparseonly)
|
|
fprintf(stdout, "total time: %lld ms\n", (long long int)total_time);
|
|
}
|
|
|
|
/*
|
|
* process slash command if one was given to -c
|
|
*/
|
|
else if (options.action == ACT_SINGLE_SLASH) {
|
|
PsqlScanState scan_state = NULL;
|
|
|
|
if (pset.echo == PSQL_ECHO_ALL)
|
|
puts(options.action_string);
|
|
|
|
scan_state = psql_scan_create();
|
|
psql_scan_setup(scan_state, options.action_string, (int)strlen(options.action_string));
|
|
|
|
successResult = HandleSlashCmds(scan_state, NULL) != PSQL_CMD_ERROR ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
psql_scan_destroy(scan_state);
|
|
}
|
|
|
|
/*
|
|
* If the query given to -c was a normal one, send it
|
|
*/
|
|
else if (options.action == ACT_SINGLE_QUERY) {
|
|
successResult = MainLoop(NULL, options.action_string);
|
|
rc = memset_s(options.action_string, strlen(options.action_string), 0, strlen(options.action_string));
|
|
securec_check_c(rc, "\0", "\0");
|
|
free(options.action_string);
|
|
options.action_string = NULL;
|
|
}
|
|
|
|
/*
|
|
* or otherwise enter interactive main loop
|
|
*/
|
|
else {
|
|
if (!options.no_psqlrc) {
|
|
process_psqlrc(argv[0]);
|
|
}
|
|
|
|
connection_warnings(true);
|
|
if (!pset.quiet && !pset.notty)
|
|
printf(_("Type \"help\" for help.\n\n"));
|
|
|
|
canAddHist = true;
|
|
initializeInput(options.no_readline ? 0 : 1);
|
|
successResult = MainLoop(stdin);
|
|
}
|
|
|
|
/* clean up */
|
|
if (pset.logfile != NULL) {
|
|
fclose(pset.logfile);
|
|
pset.logfile = NULL;
|
|
}
|
|
PQfinish(pset.db);
|
|
setQFout(NULL);
|
|
|
|
/* Free all the connection and guc info used in gsql parallel execute mode. */
|
|
free(pset.connInfo.keywords);
|
|
pset.connInfo.keywords = NULL;
|
|
|
|
for (int i = 0; i < PARAMS_ARRAY_SIZE; i++) {
|
|
if (pset.connInfo.values_free[i] && NULL != pset.connInfo.values[i]) {
|
|
if (strlen(pset.connInfo.values[i]) != 0) {
|
|
free(pset.connInfo.values[i]);
|
|
pset.connInfo.values[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
free(pset.connInfo.values);
|
|
pset.connInfo.values = NULL;
|
|
free(pset.connInfo.values_free);
|
|
pset.connInfo.values_free = NULL;
|
|
|
|
for (int i = 0; i < pset.num_guc_stmt; i++) {
|
|
free(pset.guc_stmt[i]);
|
|
pset.guc_stmt[i] = NULL;
|
|
}
|
|
|
|
if (pset.guc_stmt != NULL)
|
|
free(pset.guc_stmt);
|
|
|
|
pset.guc_stmt = NULL;
|
|
/* Free options.action_string, because it alloced memory when options.action is ACT_FILE*/
|
|
if (options.action == ACT_FILE && (options.action_string != NULL)) {
|
|
free(options.action_string);
|
|
options.action_string = NULL;
|
|
}
|
|
|
|
/* Clean up variables for query retry. */
|
|
pset.max_retry_times = 0;
|
|
ResetQueryRetryController();
|
|
EmptyRetryErrcodesList(pset.errcodes_list);
|
|
|
|
return successResult;
|
|
}
|
|
|
|
/*
|
|
* Parse command line options
|
|
*/
|
|
static void parse_psql_options(int argc, char* const argv[], struct adhoc_opts* options)
|
|
{
|
|
static struct option long_options[] = {
|
|
{"echo-all", no_argument, NULL, 'a'},
|
|
{"no-align", no_argument, NULL, 'A'},
|
|
{"command", required_argument, NULL, 'c'},
|
|
{"dbname", required_argument, NULL, 'd'},
|
|
{"echo-queries", no_argument, NULL, 'e'},
|
|
{"echo-hidden", no_argument, NULL, 'E'},
|
|
{"file", required_argument, NULL, 'f'},
|
|
{"field-separator", required_argument, NULL, 'F'},
|
|
{"field-separator-zero", no_argument, NULL, 'z'},
|
|
{"host", required_argument, NULL, 'h'},
|
|
{"html", no_argument, NULL, 'H'},
|
|
{"list", no_argument, NULL, 'l'},
|
|
{"log-file", required_argument, NULL, 'L'},
|
|
{"maintenance", no_argument, NULL, 'm'},
|
|
{"no-libedit", no_argument, NULL, 'n'},
|
|
{"single-transaction", no_argument, NULL, '1'},
|
|
{"output", required_argument, NULL, 'o'},
|
|
{"port", required_argument, NULL, 'p'},
|
|
{"pset", required_argument, NULL, 'P'},
|
|
{"quiet", no_argument, NULL, 'q'},
|
|
{"enable-client-encryption", no_argument, NULL, 'C'},
|
|
{"record-separator", required_argument, NULL, 'R'},
|
|
{"record-separator-zero", no_argument, NULL, '0'},
|
|
{"single-step", no_argument, NULL, 's'},
|
|
{"single-line", no_argument, NULL, 'S'},
|
|
{"tuples-only", no_argument, NULL, 't'},
|
|
{"table-attr", required_argument, NULL, 'T'},
|
|
{"username", required_argument, NULL, 'U'},
|
|
{"set", required_argument, NULL, 'v'},
|
|
{"variable", required_argument, NULL, 'v'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{"password", required_argument, NULL, 'W'},
|
|
{"expanded", no_argument, NULL, 'x'},
|
|
{"no-gsqlrc", no_argument, NULL, 'X'},
|
|
{"help", no_argument, NULL, '?'},
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
{"with-key", required_argument, NULL, 'k'},
|
|
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
|
|
{"sql-parse", no_argument, NULL, 'g'},
|
|
#endif
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
int optindex;
|
|
extern char* optarg;
|
|
extern int optind;
|
|
int c;
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
char* dencrypt_key = NULL;
|
|
errno_t rc = EOK;
|
|
#ifdef USE_READLINE
|
|
useReadline = false;
|
|
#endif
|
|
|
|
rc = memset_s(options, sizeof(*options), 0, sizeof(*options));
|
|
check_memset_s(rc);
|
|
|
|
while ((c = getopt_long(
|
|
argc, argv, "aAc:d:eEf:F:gh:Hlk:L:mno:p:P:qCR:rsStT:U:v:W:VxXz?01", long_options, &optindex)) != -1) {
|
|
switch (c) {
|
|
case 'a':
|
|
if (!SetVariable(pset.vars, "ECHO", "all")) {
|
|
psql_error("set variable %s failed.\n", "ECHO");
|
|
}
|
|
break;
|
|
case 'A':
|
|
pset.popt.topt.format = PRINT_UNALIGNED;
|
|
break;
|
|
case 'c':
|
|
options->action_string = optarg;
|
|
if (optarg[0] == '\\') {
|
|
options->action = ACT_SINGLE_SLASH;
|
|
options->action_string++;
|
|
} else {
|
|
options->action = ACT_SINGLE_QUERY;
|
|
options->action_string = pg_strdup(optarg); /* need to free in main() */
|
|
/* clear action string after -c command when it inludes sensitive info */
|
|
if (SensitiveStrCheck(optarg)) {
|
|
rc = memset_s(optarg, strlen(optarg), 0, strlen(optarg));
|
|
check_memset_s(rc);
|
|
}
|
|
}
|
|
break;
|
|
case 'd':
|
|
options->dbname = optarg;
|
|
break;
|
|
case 'e':
|
|
if (!SetVariable(pset.vars, "ECHO", "queries")) {
|
|
psql_error("set variable %s failed.\n", "ECHO");
|
|
}
|
|
break;
|
|
case 'E':
|
|
SetVariableBool(pset.vars, "ECHO_HIDDEN");
|
|
break;
|
|
case 'f':
|
|
if ((options->action_string != NULL) && options->action == ACT_FILE)
|
|
free(options->action_string);
|
|
options->action_string = pg_strdup(optarg);
|
|
options->action = ACT_FILE;
|
|
break;
|
|
case 'F':
|
|
if (pset.popt.topt.fieldSep.separator != NULL)
|
|
free(pset.popt.topt.fieldSep.separator);
|
|
pset.popt.topt.fieldSep.separator = pg_strdup(optarg);
|
|
pset.popt.topt.fieldSep.separator_zero = false;
|
|
break;
|
|
case 'h':
|
|
options->host = optarg;
|
|
break;
|
|
case 'H':
|
|
pset.popt.topt.format = PRINT_HTML;
|
|
break;
|
|
case 'l':
|
|
options->action = ACT_LIST_DB;
|
|
break;
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
case 'k': {
|
|
pset.decryptInfo.encryptInclude = true;
|
|
if (optarg != NULL) {
|
|
dencrypt_key = pg_strdup(optarg);
|
|
rc = memset_s(optarg, strlen(optarg), 0, strlen(optarg));
|
|
check_memset_s(rc);
|
|
set_aes_key(dencrypt_key);
|
|
}
|
|
break;
|
|
}
|
|
case 'L':
|
|
options->logfilename = optarg;
|
|
break;
|
|
case 'm':
|
|
pset.maintance = true;
|
|
break;
|
|
case 'n':
|
|
options->no_readline = true;
|
|
break;
|
|
case 'o':
|
|
setQFout(optarg);
|
|
break;
|
|
case 'p':
|
|
options->port = optarg;
|
|
break;
|
|
case 'P': {
|
|
char* value = NULL;
|
|
char* equal_loc = NULL;
|
|
bool result = false;
|
|
|
|
value = pg_strdup(optarg);
|
|
equal_loc = strchr(value, '=');
|
|
if (equal_loc == NULL)
|
|
result = do_pset(value, NULL, &pset.popt, true);
|
|
else {
|
|
*equal_loc = '\0';
|
|
result = do_pset(value, equal_loc + 1, &pset.popt, true);
|
|
}
|
|
|
|
if (!result) {
|
|
fprintf(stderr, _("%s: could not set printing parameter \"%s\"\n"), pset.progname, value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
free(value);
|
|
break;
|
|
}
|
|
case 'q':
|
|
SetVariableBool(pset.vars, "QUIET");
|
|
break;
|
|
case 'C':
|
|
pset.enable_client_encryption = true;
|
|
break;
|
|
case 'r':
|
|
#ifdef USE_READLINE
|
|
useReadline = true;
|
|
#endif
|
|
break;
|
|
case 'R':
|
|
if (pset.popt.topt.recordSep.separator != NULL)
|
|
free(pset.popt.topt.recordSep.separator);
|
|
pset.popt.topt.recordSep.separator = pg_strdup(optarg);
|
|
pset.popt.topt.recordSep.separator_zero = false;
|
|
break;
|
|
case 's':
|
|
SetVariableBool(pset.vars, "SINGLESTEP");
|
|
break;
|
|
case 'S':
|
|
SetVariableBool(pset.vars, "SINGLELINE");
|
|
break;
|
|
case 't':
|
|
pset.popt.topt.tuples_only = true;
|
|
break;
|
|
case 'T':
|
|
if (pset.popt.topt.tableAttr != NULL)
|
|
free(pset.popt.topt.tableAttr);
|
|
pset.popt.topt.tableAttr = pg_strdup(optarg);
|
|
break;
|
|
case 'U':
|
|
if (strlen(optarg) >= MAXPGPATH) {
|
|
fprintf(stderr, _("%s: invalid username, max username len:%d\n"), pset.progname, MAXPGPATH);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
options->username = optarg;
|
|
break;
|
|
case 'v': {
|
|
char* value = NULL;
|
|
char* equal_loc = NULL;
|
|
|
|
value = pg_strdup(optarg);
|
|
equal_loc = strchr(value, '=');
|
|
if (equal_loc == NULL) {
|
|
if (!DeleteVariable(pset.vars, value)) {
|
|
fprintf(stderr, _("%s: could not delete variable \"%s\"\n"), pset.progname, value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
*equal_loc = '\0';
|
|
if (!SetVariable(pset.vars, value, equal_loc + 1)) {
|
|
fprintf(stderr, _("%s: could not set variable \"%s\"\n"), pset.progname, value);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
free(value);
|
|
break;
|
|
}
|
|
case 'V':
|
|
showVersion();
|
|
exit(EXIT_SUCCESS);
|
|
case 'W':
|
|
pset.getPassword = TRI_YES;
|
|
if (optarg != NULL) {
|
|
options->passwd = pg_strdup(optarg);
|
|
rc = memset_s(optarg, strlen(optarg), 0, strlen(optarg));
|
|
check_memset_s(rc);
|
|
}
|
|
break;
|
|
case 'x':
|
|
pset.popt.topt.expanded = (unsigned short int)true;
|
|
break;
|
|
case 'X':
|
|
options->no_psqlrc = true;
|
|
break;
|
|
case 'z':
|
|
pset.popt.topt.fieldSep.separator_zero = true;
|
|
break;
|
|
case '0':
|
|
pset.popt.topt.recordSep.separator_zero = true;
|
|
break;
|
|
case '1':
|
|
options->single_txn = true;
|
|
break;
|
|
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
|
|
case 'g':
|
|
pset.parseonly = true;
|
|
break;
|
|
#endif
|
|
case '?':
|
|
/* Actual help option given */
|
|
if (strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0) {
|
|
usage();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
/* unknown option reported by getopt */
|
|
else {
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), pset.progname);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), pset.progname);
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if we still have arguments, use it as the database name and username
|
|
*/
|
|
while (argc - optind >= 1) {
|
|
if (options->dbname == NULL) {
|
|
options->dbname = argv[optind];
|
|
/* mask informations in URI string. */
|
|
if (strncmp(options->dbname, "postgresql://", strlen("postgresql://")) == 0) {
|
|
options->dbname = pg_strdup(argv[optind]);
|
|
char *off_argv = argv[optind] + strlen("postgresql://");
|
|
rc = memset_s(off_argv, strlen(off_argv), '*', strlen(off_argv));
|
|
check_memset_s(rc);
|
|
} else if (strncmp(options->dbname, "postgres://", strlen("postgres://")) == 0) {
|
|
options->dbname = pg_strdup(argv[optind]);
|
|
char *off_argv = argv[optind] + strlen("postgres://");
|
|
rc = memset_s(off_argv, strlen(off_argv), '*', strlen(off_argv));
|
|
check_memset_s(rc);
|
|
}
|
|
} else if (options->username == NULL) {
|
|
options->username = argv[optind];
|
|
} else if (!pset.quiet) {
|
|
fprintf(
|
|
stderr, _("%s: warning: extra command-line argument \"%s\" ignored\n"), pset.progname, argv[optind]);
|
|
}
|
|
optind++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Load .gsqlrc file, if found.
|
|
*/
|
|
static void process_psqlrc(const char* argv0)
|
|
{
|
|
char home[MAXPGPATH];
|
|
char rc_file[MAXPGPATH];
|
|
char my_exec_path[MAXPGPATH] = {'\0'};
|
|
char etc_path[MAXPGPATH];
|
|
char* envrc = GetEnvStr("PSQLRC");
|
|
bool withdecrypt = false;
|
|
errno_t rc = EOK;
|
|
|
|
/* we don't decrypt gsqlrc file here */
|
|
if (pset.decryptInfo.encryptInclude) {
|
|
pset.decryptInfo.encryptInclude = false;
|
|
withdecrypt = true;
|
|
}
|
|
|
|
find_my_exec(argv0, my_exec_path);
|
|
get_etc_path(my_exec_path, etc_path, sizeof(etc_path));
|
|
|
|
rc = sprintf_s(rc_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC);
|
|
check_sprintf_s(rc);
|
|
process_psqlrc_file(rc_file);
|
|
|
|
if (envrc != NULL && strlen(envrc) > 0) {
|
|
/* might need to free() this */
|
|
char* envrc_alloc = pg_strdup(envrc);
|
|
expand_tilde(&envrc_alloc);
|
|
process_psqlrc_file(envrc_alloc);
|
|
free(envrc_alloc);
|
|
envrc_alloc = NULL;
|
|
} else if (get_home_path(home, sizeof(home))) {
|
|
rc = sprintf_s(rc_file, MAXPGPATH, "%s/%s", home, PSQLRC);
|
|
check_sprintf_s(rc);
|
|
process_psqlrc_file(rc_file);
|
|
}
|
|
|
|
/* open the decrypt mark for other encrypt file */
|
|
if (withdecrypt)
|
|
pset.decryptInfo.encryptInclude = true;
|
|
|
|
if (envrc != NULL)
|
|
free(envrc);
|
|
envrc = NULL;
|
|
}
|
|
|
|
static void process_psqlrc_file(char* filename)
|
|
{
|
|
char *psqlrc_minor = NULL;
|
|
char *psqlrc_major = NULL;
|
|
errno_t err = EOK;
|
|
#if defined(WIN32) && (!defined(__MINGW32__))
|
|
#define R_OK 4
|
|
#endif
|
|
|
|
psqlrc_minor = (char*)pg_malloc(strlen(filename) + 1 + strlen(PG_VERSION) + 1);
|
|
err = sprintf_s(psqlrc_minor, strlen(filename) + 1 + strlen(PG_VERSION) + 1, "%s-%s", filename, PG_VERSION);
|
|
check_sprintf_s(err);
|
|
psqlrc_major = (char*)pg_malloc(strlen(filename) + 1 + strlen(PG_MAJORVERSION) + 1);
|
|
err =
|
|
sprintf_s(psqlrc_major, strlen(filename) + 1 + strlen(PG_MAJORVERSION) + 1, "%s-%s", filename, PG_MAJORVERSION);
|
|
check_sprintf_s(err);
|
|
/* check for minor version first, then major, then no version */
|
|
if (access(psqlrc_minor, R_OK) == 0)
|
|
(void)process_file(psqlrc_minor, false, false);
|
|
else if (access(psqlrc_major, R_OK) == 0)
|
|
(void)process_file(psqlrc_major, false, false);
|
|
else if (access(filename, R_OK) == 0)
|
|
(void)process_file(filename, false, false);
|
|
|
|
free(psqlrc_minor);
|
|
free(psqlrc_major);
|
|
}
|
|
|
|
/* showVersion
|
|
*
|
|
* This output format is intended to match GNU standards.
|
|
*/
|
|
static void showVersion(void)
|
|
{
|
|
#ifdef PGXC
|
|
puts("gsql " DEF_GS_VERSION);
|
|
#else
|
|
puts("gsql " DEF_GS_VERSION);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Assign hooks for psql variables.
|
|
*
|
|
* This isn't an amazingly good place for them, but neither is anywhere else.
|
|
*/
|
|
static void autocommit_hook(const char* newval)
|
|
{
|
|
pset.autocommit = ParseVariableBool(newval);
|
|
}
|
|
|
|
static void on_error_stop_hook(const char* newval)
|
|
{
|
|
pset.on_error_stop = ParseVariableBool(newval);
|
|
}
|
|
|
|
static void quiet_hook(const char* newval)
|
|
{
|
|
pset.quiet = ParseVariableBool(newval);
|
|
}
|
|
|
|
static void singleline_hook(const char* newval)
|
|
{
|
|
pset.singleline = ParseVariableBool(newval);
|
|
}
|
|
|
|
static void singlestep_hook(const char* newval)
|
|
{
|
|
pset.singlestep = ParseVariableBool(newval);
|
|
}
|
|
|
|
static void fetch_count_hook(const char* newval)
|
|
{
|
|
pset.fetch_count = ParseVariableNum(newval, -1, -1, false);
|
|
}
|
|
|
|
static void echo_hook(const char* newval)
|
|
{
|
|
if (newval == NULL)
|
|
pset.echo = PSQL_ECHO_NONE;
|
|
else if (strcmp(newval, "queries") == 0)
|
|
pset.echo = PSQL_ECHO_QUERIES;
|
|
else if (strcmp(newval, "all") == 0)
|
|
pset.echo = PSQL_ECHO_ALL;
|
|
else
|
|
pset.echo = PSQL_ECHO_NONE;
|
|
}
|
|
|
|
static void echo_hidden_hook(const char* newval)
|
|
{
|
|
if (newval == NULL)
|
|
pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
|
|
else if (strcmp(newval, "noexec") == 0)
|
|
pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC;
|
|
else if (pg_strcasecmp(newval, "off") == 0)
|
|
pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
|
|
else
|
|
pset.echo_hidden = PSQL_ECHO_HIDDEN_ON;
|
|
}
|
|
|
|
static void on_error_rollback_hook(const char* newval)
|
|
{
|
|
if (newval == NULL)
|
|
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
|
|
else if (pg_strcasecmp(newval, "interactive") == 0)
|
|
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE;
|
|
else if (pg_strcasecmp(newval, "off") == 0)
|
|
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
|
|
else
|
|
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON;
|
|
}
|
|
|
|
static void histcontrol_hook(const char* newval)
|
|
{
|
|
if (newval == NULL)
|
|
pset.histcontrol = hctl_none;
|
|
else if (strcmp(newval, "ignorespace") == 0)
|
|
pset.histcontrol = hctl_ignorespace;
|
|
else if (strcmp(newval, "ignoredups") == 0)
|
|
pset.histcontrol = hctl_ignoredups;
|
|
else if (strcmp(newval, "ignoreboth") == 0)
|
|
pset.histcontrol = hctl_ignoreboth;
|
|
else
|
|
pset.histcontrol = hctl_none;
|
|
}
|
|
|
|
static void prompt1_hook(const char* newval)
|
|
{
|
|
pset.prompt1 = newval != NULL ? newval : "";
|
|
}
|
|
|
|
static void prompt2_hook(const char* newval)
|
|
{
|
|
pset.prompt2 = newval != NULL ? newval : "";
|
|
}
|
|
|
|
static void prompt3_hook(const char* newval)
|
|
{
|
|
pset.prompt3 = newval != NULL ? newval : "";
|
|
}
|
|
|
|
static void verbosity_hook(const char* newval)
|
|
{
|
|
if (newval == NULL)
|
|
pset.verbosity = PQERRORS_DEFAULT;
|
|
else if (strcmp(newval, "default") == 0)
|
|
pset.verbosity = PQERRORS_DEFAULT;
|
|
else if (strcmp(newval, "terse") == 0)
|
|
pset.verbosity = PQERRORS_TERSE;
|
|
else if (strcmp(newval, "verbose") == 0)
|
|
pset.verbosity = PQERRORS_VERBOSE;
|
|
else
|
|
pset.verbosity = PQERRORS_DEFAULT;
|
|
|
|
(void)PQsetErrorVerbosity(pset.db, pset.verbosity);
|
|
}
|
|
|
|
// Iterate through the elements of ErrCodes and release allocated memory.
|
|
//
|
|
void EmptyRetryErrcodesList(ErrCodes& list)
|
|
{
|
|
for (int i = 0; i < (int)list.size(); i++) {
|
|
if (list[i] != NULL) {
|
|
free(list[i]);
|
|
list[i] = NULL;
|
|
}
|
|
}
|
|
|
|
// Removes all elements from the vector.
|
|
//
|
|
list.clear();
|
|
}
|
|
|
|
// Parse the retry errcodes config file, cache the errcodes in a vector 'pset.retry_errcodes'.
|
|
//
|
|
static bool ReadRetryErrcodesConfigFile(void)
|
|
{
|
|
char* gausshome_dir = NULL;
|
|
char self_path[MAXPGPATH] = {0};
|
|
char retry_errcodes_path[MAXPGPATH] = {0};
|
|
int nRet = 0;
|
|
FILE* fp = NULL;
|
|
char* line = NULL;
|
|
size_t len = 0;
|
|
ErrCodes list;
|
|
|
|
gausshome_dir = GetEnvStr("GAUSSHOME");
|
|
if (gausshome_dir == NULL) {
|
|
int r = (int)readlink("/proc/self/exe", self_path, sizeof(self_path) - 1);
|
|
if (r < 0 || r >= (int)(MAXPGPATH - sizeof("/retry_errcodes.conf"))) {
|
|
psql_error("Could not get proc self path.\n");
|
|
return false;
|
|
} else {
|
|
char* ptr = strrchr(self_path, '/');
|
|
|
|
if (NULL != ptr)
|
|
*ptr = '\0';
|
|
nRet = sprintf_s(retry_errcodes_path, MAXPGPATH, "%s/retry_errcodes.conf", self_path);
|
|
check_sprintf_s(nRet);
|
|
}
|
|
} else {
|
|
check_env_value(gausshome_dir);
|
|
nRet = sprintf_s(retry_errcodes_path, MAXPGPATH, "%s/bin/retry_errcodes.conf", gausshome_dir);
|
|
check_sprintf_s(nRet);
|
|
}
|
|
|
|
if (gausshome_dir != NULL)
|
|
free(gausshome_dir);
|
|
gausshome_dir = NULL;
|
|
|
|
canonicalize_path(retry_errcodes_path);
|
|
fp = fopen(retry_errcodes_path, "r");
|
|
if (fp == NULL) {
|
|
psql_error("Could not open retry errcodes config file.\n");
|
|
return false;
|
|
}
|
|
|
|
while (getline(&line, &len, fp) != -1) {
|
|
// Just check the length of errcode.
|
|
// Length must be 'ERRCODE_LENGTH + 1' with '\n' in the end.
|
|
//
|
|
if (strlen(line) != ERRCODE_LENGTH + 1) {
|
|
// If got wrong errcode, we should empty the errcodes list first.
|
|
//
|
|
EmptyRetryErrcodesList(list);
|
|
psql_error("Wrong errcodes in config file.\n");
|
|
fclose(fp);
|
|
|
|
free(line);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Add a new element at the end of the vector.
|
|
//
|
|
list.push_back(pg_strdup(line));
|
|
}
|
|
|
|
if (list.size() == 0) {
|
|
psql_error("No errcodes list in config file.\n");
|
|
fclose(fp);
|
|
|
|
if (line != NULL)
|
|
free(line);
|
|
|
|
return false;
|
|
} else {
|
|
// Empty the previous errcodes list and assign a new one to the vector.
|
|
//
|
|
EmptyRetryErrcodesList(pset.errcodes_list);
|
|
pset.errcodes_list = list;
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
if (line != NULL)
|
|
free(line);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void retry_hook(const char* newval)
|
|
{
|
|
int result = 0;
|
|
|
|
if (newval == NULL)
|
|
return;
|
|
|
|
if (PQtransactionStatus(pset.db) != PQTRANS_IDLE) {
|
|
psql_error("Retry within transaction is not supported.\n");
|
|
return;
|
|
}
|
|
|
|
if (!newval[0]) {
|
|
if (0 == pset.max_retry_times) {
|
|
result = DEFAULT_RETRY_TIMES;
|
|
} else {
|
|
printf(_("Retry is off.\n"));
|
|
pset.max_retry_times = 0;
|
|
ResetQueryRetryController();
|
|
EmptyRetryErrcodesList(pset.errcodes_list);
|
|
return;
|
|
}
|
|
} else {
|
|
char* endptr = NULL;
|
|
|
|
errno = 0;
|
|
result = (int)strtol(newval, &endptr, 10);
|
|
|
|
// Check for various possible errors.
|
|
if (errno == ERANGE || result != (int64)((int32)result)) {
|
|
psql_error("Value exceeds integer range.\n");
|
|
psql_error("Hint: The valid retry times is %d-%d.\n", DEFAULT_RETRY_TIMES, MAX_RETRY_TIMES);
|
|
return;
|
|
}
|
|
|
|
if (*endptr != '\0' || endptr == newval || result < DEFAULT_RETRY_TIMES || result > MAX_RETRY_TIMES) {
|
|
psql_error("Invalid retry times \"%s\".\n", newval);
|
|
psql_error("Hint: The valid retry times is %d-%d.\n", DEFAULT_RETRY_TIMES, MAX_RETRY_TIMES);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!ReadRetryErrcodesConfigFile()) {
|
|
return;
|
|
}
|
|
pset.max_retry_times = result;
|
|
if (!newval[0])
|
|
printf(_("Retry is on with default retry times: %d.\n"), pset.max_retry_times);
|
|
else
|
|
printf(_("Retry is on with retry times: %d.\n"), pset.max_retry_times);
|
|
}
|
|
|
|
static void EstablishVariableSpace(void)
|
|
{
|
|
pset.vars = CreateVariableSpace();
|
|
|
|
SetVariableAssignHook(pset.vars, "AUTOCOMMIT", autocommit_hook);
|
|
SetVariableAssignHook(pset.vars, "ON_ERROR_STOP", on_error_stop_hook);
|
|
SetVariableAssignHook(pset.vars, "QUIET", quiet_hook);
|
|
SetVariableAssignHook(pset.vars, "SINGLELINE", singleline_hook);
|
|
SetVariableAssignHook(pset.vars, "SINGLESTEP", singlestep_hook);
|
|
SetVariableAssignHook(pset.vars, "FETCH_COUNT", fetch_count_hook);
|
|
SetVariableAssignHook(pset.vars, "ECHO", echo_hook);
|
|
SetVariableAssignHook(pset.vars, "ECHO_HIDDEN", echo_hidden_hook);
|
|
SetVariableAssignHook(pset.vars, "ON_ERROR_ROLLBACK", on_error_rollback_hook);
|
|
SetVariableAssignHook(pset.vars, "HISTCONTROL", histcontrol_hook);
|
|
SetVariableAssignHook(pset.vars, "PROMPT1", prompt1_hook);
|
|
SetVariableAssignHook(pset.vars, "PROMPT2", prompt2_hook);
|
|
SetVariableAssignHook(pset.vars, "PROMPT3", prompt3_hook);
|
|
SetVariableAssignHook(pset.vars, "VERBOSITY", verbosity_hook);
|
|
(void)SetVariableAssignHook(pset.vars, "RETRY", retry_hook);
|
|
}
|
|
|
|
/* Database Security: Data importing/dumping support AES128. */
|
|
/*
|
|
* Funcation : set_aes_key
|
|
* Description: set aes g_key with dencryt_key if is not NULL
|
|
*
|
|
*/
|
|
static void set_aes_key(const char* dencrypt_key)
|
|
{
|
|
int tmpkeylen = 0;
|
|
errno_t rc;
|
|
|
|
if (dencrypt_key != NULL) {
|
|
tmpkeylen = (int)strlen(dencrypt_key);
|
|
} else {
|
|
fprintf(stderr, _("%s: missing key\n"), pset.progname);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (tmpkeylen == KEY_LEN && check_key((const char*)dencrypt_key, KEY_LEN)) {
|
|
rc = memset_s(pset.decryptInfo.Key, KEY_MAX_LEN, 0, KEY_MAX_LEN);
|
|
securec_check_c(rc, "\0", "\0");
|
|
rc = strncpy_s((char*)pset.decryptInfo.Key, KEY_MAX_LEN, dencrypt_key, KEY_MAX_LEN - 1);
|
|
securec_check_c(rc, "\0", "\0");
|
|
} else {
|
|
fprintf(stderr,
|
|
_("%s: the key is illegal,must be letters or numbers and the length must be %d\n"),
|
|
pset.progname,
|
|
KEY_LEN);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* GetEnvStr
|
|
*
|
|
* Note: malloc space for get the return of getenv() function, then return the malloc space.
|
|
* so, this space need be free.
|
|
*/
|
|
static char* GetEnvStr(const char* env)
|
|
{
|
|
char* tmpvar = NULL;
|
|
const char* temp = getenv(env);
|
|
errno_t rc = 0;
|
|
if (temp != NULL) {
|
|
size_t len = strlen(temp);
|
|
if (0 == len)
|
|
return NULL;
|
|
tmpvar = (char*)malloc(len + 1);
|
|
if (tmpvar != NULL) {
|
|
rc = strcpy_s(tmpvar, len + 1, temp);
|
|
securec_check_c(rc, "\0", "\0");
|
|
return tmpvar;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|