* psql - the openGauss 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
#include <io.h>
#include <win32.h>
#endif
#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"
#include <time.h>
#include "pgtime.h"
#include "mb/pg_wchar.h"
#ifndef WIN32
#include "libpq/libpq-int.h"
#endif
#include "nodes/pg_list.h"
#include "common_cipher.h"
* Global psql options
*/
PsqlSettings pset;
char* argv_para;
int argv_num;
static bool is_pipeline = false;
static bool is_interactive = true;
#ifndef ENABLE_MULTIPLE_NODES
const char *g_queryNodeState = "select local_role, db_state from pg_catalog.pg_stat_get_stream_replications();";
const char *g_expectedLocalRole = "Primary";
const char *g_expectedDbState = "Normal";
#endif
extern const char* libpqVersionString;
#ifndef WIN32
#define SYSPSQLRC "gsqlrc"
#define PSQLRC ".gsqlrc"
#else
#define SYSPSQLRC "gsqlrc"
#define PSQLRC "gsqlrc.conf"
#endif
#ifdef ENABLE_UT
#define static
#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 };
typedef struct SimpleActionListCell {
struct SimpleActionListCell *next;
enum _actions action;
char *val;
} SimpleActionListCell;
typedef struct SimpleActionList {
SimpleActionListCell *head;
SimpleActionListCell *tail;
} SimpleActionList;
struct adhoc_opts {
char* dbname;
char* host;
char* port;
char* username;
char* passwd;
char* logfilename;
enum _actions action;
SimpleActionList actionList;
bool no_readline;
bool no_psqlrc;
bool single_txn;
#ifndef ENABLE_MULTIPLE_NODES
bool multi_host;
uint32 hostCount;
List *hostList;
#endif
};
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 void get_password_pipeline(struct adhoc_opts* options);
static int SimpleActionListAppend(SimpleActionList *list, enum _actions action, const char *val);
static void SimpleActionListFree(SimpleActionList* list);
static bool SimpleActionListHasAction(SimpleActionList* list, enum _actions action);
static int ProcessSimpleActionList(
struct adhoc_opts options, bool isparseonly, const char* argv0);
static int ProcessActionFile(
struct adhoc_opts options, bool isparseonly, char* val);
static int ProcessActionSingleSlash(struct adhoc_opts options, char* val);
static int ProcessActionSingleQuery(struct adhoc_opts options, char* val);
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
bool check_parseonly_parameter(adhoc_opts options)
{
SimpleActionListCell* cell = NULL;
if (pset.parseonly) {
for (cell = options.actionList.head; cell; cell = cell->next) {
if (cell->action == ACT_FILE) {
break;
}
}
if (!cell) {
fprintf(stderr, "%s: %s", pset.progname, "-f and -g argument must be set together\n");
exit(EXIT_USER);
} else {
return true;
}
}
return false;
}
#endif
static void set_aes_key(const char* dencrypt_key);
#ifdef HAVE_CE
#define PARAMS_ARRAY_SIZE 12
#else
#define PARAMS_ARRAY_SIZE 11
#endif
#ifdef ENABLE_LITE_MODE
void pg_free(void* ptr)
{
if (ptr != NULL) {
free(ptr);
ptr = NULL;
}
}
static void list_free_private(List* list, bool deep)
{
ListCell* cell = NULL;
cell = list_head(list);
while (cell != NULL) {
ListCell* tmp = cell;
cell = lnext(cell);
if (deep) {
pg_free(lfirst(tmp));
}
pg_free(tmp);
}
if (list != NULL) {
pg_free(list);
}
}
void list_free(List* list)
{
list_free_private(list, false);
}
static List* new_list(NodeTag type)
{
List* new_list_val = NULL;
ListCell* new_head = NULL;
new_head = (ListCell*)pg_malloc(sizeof(*new_head));
new_head->next = NULL;
new_list_val = (List*)pg_malloc(sizeof(*new_list_val));
new_list_val->type = type;
new_list_val->length = 1;
new_list_val->head = new_head;
new_list_val->tail = new_head;
return new_list_val;
}
static void new_tail_cell(List* list)
{
ListCell* new_tail = NULL;
new_tail = (ListCell*)pg_malloc(sizeof(*new_tail));
new_tail->next = NULL;
list->tail->next = new_tail;
list->tail = new_tail;
list->length++;
}
List* lappend(List* list, void* datum)
{
if (list == NIL)
list = new_list(T_List);
else
new_tail_cell(list);
lfirst(list->tail) = datum;
return list;
}
static void new_head_cell(List* list)
{
ListCell* new_head = NULL;
new_head = (ListCell*)pg_malloc(sizeof(*new_head));
new_head->next = list->head;
list->head = new_head;
list->length++;
}
List* lcons(void* datum, List* list)
{
if (list == NIL) {
list = new_list(T_List);
} else {
new_head_cell(list);
}
lfirst(list->head) = datum;
return list;
}
List* list_delete_cell(List* list, ListCell* cell, ListCell* prev)
{
Assert(prev != NULL ? lnext(prev) == cell : list_head(list) == cell);
* If we're about to delete the last node from the list, free the whole
* list instead and return NIL, which is the only valid representation of
* a zero-length list.
*/
if (list->length == 1) {
list_free(list);
return NIL;
}
* Otherwise, adjust the necessary list links, deallocate the particular
* node we have just removed, and return the list we were given.
*/
list->length--;
if (prev != NULL) {
prev->next = cell->next;
} else {
list->head = cell->next;
}
if (list->tail == cell) {
list->tail = prev;
}
cell->next = NULL;
pg_free(cell);
return list;
}
List* list_delete_first(List* list)
{
if (list == NIL) {
return NIL;
}
return list_delete_cell(list, list_head(list), NULL);
}
#endif
#ifndef ENABLE_MULTIPLE_NODES
static bool IsPrimaryOfCentralizedCluster(PGconn *conn)
{
PGresult* res = PQexec(conn, g_queryNodeState);
if (res == NULL) {
return false;
}
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
PQclear(res);
return false;
}
if (PQntuples(res) <= 0) {
PQclear(res);
return false;
}
if (PQgetisnull(res, 0, 0)) {
PQclear(res);
return false;
}
char *localRole = pg_strdup(PQgetvalue(res, 0, 0));
if (localRole == NULL) {
PQclear(res);
return false;
}
if (strcmp(localRole, g_expectedLocalRole) != 0) {
free(localRole);
PQclear(res);
return false;
}
free(localRole);
if (PQgetisnull(res, 0, 1)) {
PQclear(res);
return false;
}
char *dbState = pg_strdup(PQgetvalue(res, 0, 1));
if (dbState == NULL) {
PQclear(res);
return false;
}
if (strcmp(dbState, g_expectedDbState) != 0) {
free(dbState);
PQclear(res);
return false;
}
free(dbState);
PQclear(res);
return true;
}
PGconn* PQconnectdbMultiHostParams(const char** keywords,
char** values,
struct adhoc_opts *option,
char **password,
const char *password_prompt)
{
PGconn* conn = NULL;
bool pass = false;
ConnStatusType status = CONNECTION_BAD;
char *hostname = NULL;
List *hostList = NULL;
bool isParseOnly = false;
while (option->hostCount > 0) {
hostList = option->hostList;
hostname = (char *)linitial(hostList);
values[0] = hostname;
do {
conn = PQconnectdbParams(keywords, values, (int)true);
pass = false;
status = CONNECTION_BAD;
if (conn == NULL) {
fprintf(stderr, "failed to create connection object for host %s\n", hostname);
break;
} else if (conn->sock < 0) {
fprintf(stderr,
"failed to connect %s:%s.\n",
((conn->pghost == NULL) ? "Unknown" : conn->pghost),
((conn->pgport == NULL) ? "Unknown" : conn->pgport));
break;
}
status = PQstatus(conn);
if (status == CONNECTION_BAD
&& (strstr(PQerrorMessage(conn), "password") != NULL)
&& (*password == NULL)
&& (pset.getPassword != TRI_NO)) {
PQfinish(conn);
*password = simple_prompt(password_prompt, MAX_PASSWORD_LENGTH, false);
pass = true;
values[3] = *password;
}
} while (pass);
if ((status == CONNECTION_OK) && IsPrimaryOfCentralizedCluster(conn)) {
fprintf(stderr, "Connect primary node %s\n", hostname);
break;
}
if (hostname != NULL) {
free(hostname);
}
if (option->hostCount > 1) {
option->hostList = list_delete_first(hostList);
option->hostCount--;
PQfinish(conn);
} else {
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
isParseOnly = check_parseonly_parameter(*option);
#endif
option->hostCount--;
if (status == CONNECTION_BAD && !isParseOnly) {
fprintf(stderr, "%s: %s", pset.progname, PQerrorMessage(conn));
PQfinish(conn);
exit(EXIT_BADCONN);
}
PQfinish(conn);
conn = NULL;
}
}
return conn;
}
#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;
bool has_action = false;
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();
pset.progname = "gsql";
pset.db = NULL;
setDecimalLocale();
pset.encoding = PQenv2encoding();
pset.queryFout = stdout;
pset.queryFoutPipe = false;
pset.copyStream = NULL;
pset.cur_cmd_source = stdin;
pset.cur_cmd_interactive = false;
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
pset.parseonly = false;
#endif
pset.popt.topt.format = PRINT_ALIGNED;
pset.popt.topt.feedback = true;
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;
initDecryptInfo(&pset.decryptInfo);
pset.maintance = false;
pset.enable_client_encryption = false;
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;
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");
}
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");
}
parse_psql_options(argc, argv, &options);
if (is_pipeline) {
get_password_pipeline(&options);
}
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);
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] = "application_name";
values[5] = (char*)(pset.progname);
keywords[6] = "fallback_application_name";
values[6] = (char*)(pset.progname);
keywords[7] = "client_encoding";
values[7] = (char*)((pset.notty || (tmpenv != NULL)) ? NULL : "auto");
keywords[8] = "connect_timeout";
values[8] = NULL;
#ifdef HAVE_CE
keywords[9] = "enable_ce";
if (!pset.enable_client_encryption_log) {
values[9] = (pset.enable_client_encryption) ? (char*)"1" : NULL;
} else {
values[9] = (pset.enable_client_encryption) ? (char*)"1_with_log" : NULL;
printf("startup with enable_client_encryption.\n");
}
#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;
#ifdef ENABLE_MULTIPLE_NODES
pset.db = PQconnectdbParams(keywords, values, (int)true);
#else
if (options.multi_host && options.hostCount > 1) {
pset.db = PQconnectdbMultiHostParams(keywords, values, &options, &password, password_prompt);
if (pset.db == NULL) {
fprintf(stderr, "failed to connect %s:%s.\n", options.host, options.port);
exit(EXIT_BADCONN);
}
} else {
pset.db = PQconnectdbParams(keywords, values, (int)true);
}
#endif
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);
}
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;
}
values_free[3] = true;
if (options.dbname != NULL) {
values_free[4] = true;
}
pset.connInfo.keywords = keywords;
pset.connInfo.values = values;
pset.connInfo.values_free = values_free;
pset.num_guc_stmt = 0;
pset.guc_stmt = NULL;
if (password != NULL) {
rc = memset_s(password, strlen(password), 0, strlen(password));
securec_check_c(rc, "\0", "\0");
}
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;
}
}
#ifdef ENABLE_MULTIPLE_NODES
while (new_pass);
#else
while (new_pass && options.multi_host == false);
#endif
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);
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();
pset.dbType = GetDatabaseType();
if (options.action == ACT_LIST_DB && !isparseonly) {
int success;
if (!options.no_psqlrc) {
process_psqlrc(argv[0]);
}
SimpleActionListFree(&options.actionList);
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);
if ((logfd >= 0) && (-1 == fchmod(logfd, S_IRUSR | S_IWUSR)))
fprintf(stderr, _("could not set permissions of file \"%s\"\n"), options.logfilename);
logfd = -1;
}
}
if (!isparseonly) {
(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;
}
}
if (options.actionList.head) {
successResult = ProcessSimpleActionList(options, isparseonly, argv[0]);
}
* 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);
}
if (pset.logfile != NULL) {
fclose(pset.logfile);
pset.logfile = NULL;
}
PQfinish(pset.db);
setQFout(NULL);
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) {
rc = memset_s(pset.connInfo.values[i],
strlen(pset.connInfo.values[i]),
0,
strlen(pset.connInfo.values[i]));
securec_check_c(rc, "\0", "\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;
SimpleActionListFree(&options.actionList);
pset.max_retry_times = 0;
ResetQueryRetryController();
EmptyRetryErrcodesList(pset.errcodes_list);
return successResult;
}
static void get_password_pipeline(struct adhoc_opts* options)
{
int pass_max_len = 1024;
char* pass_buf = NULL;
errno_t rc = EOK;
if (isatty(fileno(stdin))) {
fprintf(stderr, "%s: %s", pset.progname, "Terminal is not allowed to use --pipeline\n");
exit(EXIT_USER);
}
if (is_interactive) {
fprintf(stderr, "%s: %s", pset.progname, "--pipeline must be used with -c or -f\n");
exit(EXIT_USER);
}
pass_buf = (char*)pg_malloc(pass_max_len);
rc = memset_s(pass_buf, pass_max_len, 0, pass_max_len);
securec_check_c(rc, "\0", "\0");
if (NULL != fgets(pass_buf, pass_max_len, stdin)) {
pset.getPassword = TRI_YES;
pass_buf[strlen(pass_buf) - 1] = '\0';
options->passwd = pg_strdup(pass_buf);
}
rc = memset_s(pass_buf, pass_max_len, 0, pass_max_len);
securec_check_c(rc, "\0", "\0");
free(pass_buf);
pass_buf = NULL;
}
#ifndef ENABLE_MULTIPLE_NODES
static char* TrimHost(char* src)
{
char* s = 0;
char* e = 0;
char* c = 0;
for (c = src; (c != NULL) && (*c != '\0'); ++c) {
if (isspace(*c)) {
if (e == NULL) {
e = c;
}
} else {
if (s == NULL) {
s = c;
}
e = 0;
}
}
if (s == NULL) {
s = src;
}
if (e != NULL) {
*e = 0;
}
return s;
}
static void ParseHostArg(const char *arg, struct adhoc_opts *options)
{
const char *sep = ",";
options->multi_host = false;
options->hostList = NULL;
options->hostCount = 0;
if ((arg == NULL) || (strstr(arg, sep) == NULL)) {
return;
}
char *inputStr = pg_strdup(arg);
char *host = NULL;
for (char *subStr = strtok(inputStr, sep); subStr != NULL; subStr = strtok(NULL, sep)) {
host = pg_strdup(TrimHost(subStr));
if (strlen(host) == 0) {
free(host);
continue;
}
options->hostList = (options->hostList == NULL) ? list_make1(host) : lappend(options->hostList, host);
options->hostCount++;
}
if (options->hostCount > 1) {
options->multi_host = true;
}
free(inputStr);
}
#endif
* check if a required_argument option has a void argument
*/
void check_short_optOfVoid(char *optstring, int argc, char *const *argv)
{
for (int i = 0; i < argc; i++) {
char *optstr = argv[i];
int is_only_shortbar;
if (strlen(optstr) == 1) {
is_only_shortbar = optstr[0] == '-' ? 1 : 0;
} else {
is_only_shortbar = 0;
}
if (is_only_shortbar) {
fprintf(stderr, _("%s: The option '-' is not a valid option.\n"), pset.progname);
exit(1);
}
char *oli = strchr(optstring, optstr[1]);
int is_shortopt_with_space;
if (oli != NULL && strlen(optstr) >= 1 && strlen(oli) >= 2) {
is_shortopt_with_space =
optstr[0] == '-' && oli != NULL && oli[1] == ':' && oli[2] != ':' && optstr[2] == '\0';
} else {
is_shortopt_with_space = 0;
}
if (is_shortopt_with_space) {
if (i == argc - 1) {
fprintf(stderr, _("%s: The option '-%c' need a parameter.\n"), pset.progname, optstr[1]);
exit(1);
}
char *next_optstr = argv[i + 1];
char *next_oli = strchr(optstring, next_optstr[1]);
int is_arg_optionform = next_optstr[0] == '-' && next_oli != NULL;
if (is_arg_optionform) {
fprintf(stderr, _("%s: The option '-%c' need a parameter.\n"), pset.progname, optstr[1]);
exit(1);
}
}
}
}
* 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'},
{"pipeline", no_argument, NULL, '2'},
{"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, '?'},
{"with-key", required_argument, NULL, 'k'},
{"with-decryption", required_argument, NULL, 'D'},
{"with-module-params", required_argument, NULL, 'u'},
{"with-salt", required_argument, NULL, 1},
{"enable_client_encryption_log", no_argument, NULL, '3'},
#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;
char* dencrypt_key = NULL;
char* decrypt_salt = NULL;
char* module_params = NULL;
char* decrypt_type = NULL;
char* dbname = NULL;
errno_t rc = EOK;
#ifdef USE_READLINE
useReadline = false;
#endif
rc = memset_s(options, sizeof(*options), 0, sizeof(*options));
check_memset_s(rc);
check_short_optOfVoid("aAc:d:eEf:F:gh:Hlk:L:mno:p:P:qCR:rsStT:U:v:W:VxXz?0123", argc, argv);
while ((c = getopt_long(
argc, argv, "aAc:d:D:eEf:F:gh:Hlk:u:L:mno:p:P:qCR:rsStT:U:v:W:VxXz?0123", 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': {
int successResult;
if (optarg == NULL) {
break;
}
is_interactive = false;
options->action = ACT_NOTHING;
if (optarg[0] == '\\') {
successResult = SimpleActionListAppend(&options->actionList, ACT_SINGLE_SLASH, optarg + 1);
} else {
successResult = SimpleActionListAppend(&options->actionList, ACT_SINGLE_QUERY, optarg);
if (SensitiveStrCheck(optarg)) {
rc = memset_s(optarg, strlen(optarg), 0, strlen(optarg));
check_memset_s(rc);
}
}
if (successResult != EXIT_SUCCESS) {
exit(EXIT_FAILURE);
}
break;
}
case 'd':
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 (optarg == NULL) {
break;
}
is_interactive = false;
options->action = ACT_NOTHING;
SimpleActionListAppend(&options->actionList, ACT_FILE, optarg);
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;
#ifndef ENABLE_MULTIPLE_NODES
ParseHostArg(options->host, options);
#endif
break;
case 'H':
pset.popt.topt.format = PRINT_HTML;
break;
case 'l':
options->action = ACT_LIST_DB;
break;
case 'k': {
pset.decryptInfo.encryptInclude = true;
if (optarg == NULL) {
break;
}
dencrypt_key = pg_strdup(optarg);
rc = memset_s(optarg, strlen(optarg), 0, strlen(optarg));
check_memset_s(rc);
break;
}
case 'D':
decrypt_type = pg_strdup(optarg);
break;
case 'u':
module_params = pg_strdup(optarg);
break;
case 1:
decrypt_salt = pg_strdup(optarg);
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':
if (atoi(optarg) <= 0) {
fprintf(stderr, _("%s: Invalid port number \"%s\".\n"), pset.progname, optarg);
exit(1);
}
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 '3':
pset.enable_client_encryption_log = true;
if (pset.enable_client_encryption) {
printf("running psql with client_encryption.\n");
} else {
printf("running psql without client_encryption.\n");
}
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);
}
setHistSize(value, equal_loc + 1, false);
}
free(value);
break;
}
case 'V':
showVersion();
exit(EXIT_SUCCESS);
case 'W':
if (optarg == NULL) {
break;
}
if (options->passwd != NULL) {
free(options->passwd);
}
pset.getPassword = TRI_YES;
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;
case '2':
is_pipeline = true;
break;
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
case 'g':
pset.parseonly = true;
break;
#endif
case '?':
if (strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0) {
usage();
exit(EXIT_SUCCESS);
}
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 (module_params) {
CryptoModuleParamsCheck(&(pset.decryptInfo), module_params, decrypt_type, dencrypt_key, decrypt_salt);
free(module_params);
free(decrypt_type);
free(dencrypt_key);
free(decrypt_salt);
} else if (dencrypt_key){
set_aes_key(dencrypt_key);
free(dencrypt_key);
}
* if we still have arguments, use it as the database name and username
*/
while (argc - optind >= 1) {
if (dbname == NULL) {
dbname = argv[optind];
} 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++;
}
if (dbname != NULL) {
options->dbname = pg_strdup(dbname);
if (strncmp(dbname, "postgresql://", strlen("postgresql://")) == 0) {
char *off_argv = dbname + strlen("postgresql://");
rc = memset_s(off_argv, strlen(off_argv), '*', strlen(off_argv));
check_memset_s(rc);
} else if (strncmp(dbname, "postgres://", strlen("postgres://")) == 0) {
char *off_argv = dbname + strlen("postgres://");
rc = memset_s(off_argv, strlen(off_argv), '*', strlen(off_argv));
check_memset_s(rc);
}
char *temp = NULL;
if ((temp = strstr(dbname, "password")) != NULL) {
char *off_argv = temp + strlen("password");
rc = memset_s(off_argv, strlen(off_argv), '*', strlen(off_argv));
check_memset_s(rc);
}
if ((temp = strstr(options->dbname, "replication=")) != NULL) {
(void)fprintf(stderr, _("The 'replication' parameter is not supported by gsql.\n"));
exit(EXIT_FAILURE);
}
}
}
* 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;
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) {
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);
}
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);
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);
}
*
* 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);
}
void EmptyRetryErrcodesList(ErrCodes& list)
{
for (int i = 0; i < (int)list.size(); i++) {
if (list[i] != NULL) {
free(list[i]);
list[i] = NULL;
}
}
list.clear();
}
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) {
if (strlen(line) != ERRCODE_LENGTH + 1) {
EmptyRetryErrcodesList(list);
psql_error("Wrong errcodes in config file.\n");
fclose(fp);
free(line);
return false;
}
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 {
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);
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);
}
* 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)
{
errno_t rc;
if (dencrypt_key == NULL) {
fprintf(stderr, _("%s: missing key\n"), pset.progname);
exit(EXIT_FAILURE);
}
if (check_input_password(dencrypt_key)) {
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 input key must be %d~%d bytes and "
"contain at least three kinds of characters!\n"),
pset.progname,
MIN_KEY_LEN,
MAX_KEY_LEN);
exit(EXIT_FAILURE);
}
}
static int SimpleActionListAppend(SimpleActionList *list, enum _actions action, const char *val)
{
SimpleActionListCell* cell;
cell = (SimpleActionListCell*)pg_malloc(sizeof(SimpleActionListCell));
if (cell == NULL) {
return EXIT_FAILURE;
}
cell->next = NULL;
cell->action = action;
if (val) {
cell->val = pg_strdup(val);
} else {
cell->val = NULL;
}
if (list->tail) {
list->tail->next = cell;
} else {
list->head = cell;
}
list->tail = cell;
return EXIT_SUCCESS;
}
static void SimpleActionListFree(SimpleActionList* list)
{
SimpleActionListCell* cell = list->head;
SimpleActionListCell* next;
while (cell) {
next = cell->next;
free(cell->val);
free(cell);
cell = next;
}
list->head = NULL;
list->tail = NULL;
}
static bool SimpleActionListHasAction(SimpleActionList* list, enum _actions action)
{
SimpleActionListCell* cell = NULL;
for (cell = list->head; cell; cell = cell->next) {
if (cell->action == action) {
return true;
}
}
return false;
}
static int ProcessActionFile(struct adhoc_opts options,
bool isparseonly, char* val)
{
struct timeval aesStartTime;
struct timeval aesEndTime;
pg_time_t total_time = 0;
int successResult;
int scale = 1000;
gettimeofday(&aesStartTime, NULL);
successResult = process_file(val, false, false);
gettimeofday(&aesEndTime, NULL);
total_time = scale * (aesEndTime.tv_sec - aesStartTime.tv_sec) +
(aesEndTime.tv_usec - aesStartTime.tv_usec) / scale;
if (!isparseonly)
fprintf(stdout, "total time: %lld ms\n", (long long int)total_time);
return successResult;
}
static int ProcessActionSingleSlash(struct adhoc_opts options, char* val)
{
* process slash command if one was given to -c
*/
PsqlScanState scanState = NULL;
int successResult;
if (pset.echo == PSQL_ECHO_ALL) {
puts(val);
}
scanState = psql_scan_create();
psql_scan_setup(scanState, val, (int)strlen(val));
successResult = HandleSlashCmds(scanState, NULL) != PSQL_CMD_ERROR ? EXIT_SUCCESS : EXIT_FAILURE;
psql_scan_destroy(scanState);
return successResult;
}
static int ProcessActionSingleQuery(struct adhoc_opts options, char* val)
{
* If the query given to -c was a normal one, send it
*/
int successResult = MainLoop(NULL, val);
error_t rc = memset_s(val, strlen(val), 0, strlen(val));
securec_check_c(rc, "\0", "\0");
return successResult;
}
static int ProcessSimpleActionList(
struct adhoc_opts options, bool isparseonly, const char* argv0)
{
SimpleActionListCell* cell = NULL;
int successResult;
PGresult *res;
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
bool saveIsParseOnly = pset.parseonly;
#endif
if (!options.no_psqlrc &&
SimpleActionListHasAction(&options.actionList, ACT_FILE)) {
process_psqlrc(argv0);
}
if (options.single_txn) {
if ((res = PSQLexec("BEGIN", false)) == NULL) {
successResult = pset.on_error_stop ? EXIT_USER : EXIT_FAILURE;
return successResult;
} else {
PQclear(res);
}
}
for (cell = options.actionList.head; cell; cell = cell->next) {
if (cell->action == ACT_FILE) {
successResult = ProcessActionFile(options, isparseonly, cell->val);
} else if (cell->action == ACT_SINGLE_SLASH) {
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
pset.parseonly = false;
#endif
successResult = ProcessActionSingleSlash(options, cell->val);
} else if (cell->action == ACT_SINGLE_QUERY) {
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
pset.parseonly = false;
#endif
successResult = ProcessActionSingleQuery(options, cell->val);
} else {
Assert(false);
}
#if defined(USE_ASSERT_CHECKING) || defined(FASTCHECK)
pset.parseonly = saveIsParseOnly;
#endif
if (successResult != EXIT_SUCCESS && pset.on_error_stop) {
break;
}
}
if (options.single_txn) {
res = PSQLexec((successResult != EXIT_SUCCESS && pset.on_error_stop) ? "ROLLBACK" : "COMMIT", false);
if (res == NULL) {
successResult = pset.on_error_stop ? EXIT_USER : EXIT_FAILURE;
} else {
PQclear(res);
}
}
return successResult;
}