Files
openGauss-server/src/test/regress/pg_regress.cpp
2022-05-13 14:55:39 +08:00

7368 lines
246 KiB
C++

/*-------------------------------------------------------------------------
*
* pg_regress --- regression test driver
*
* This is a C implementation of the previous shell script for running
* the regression tests, and should be mostly compatible with it.
* Initial author of C translation: Magnus Hagander
*
* This code is released under the terms of the PostgreSQL License.
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/test/regress/pg_regress.c
*
*-------------------------------------------------------------------------
*/
#include "pg_regress.h"
#include <sys/stat.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/time.h>
#endif
#include <assert.h>
#include "getopt_long.h"
#include "pg_config_paths.h"
#define DATANODE 1
#define COORD 2
#define GTM 3
#define SHELL 4
#ifdef BUILD_BY_CMAKE
char* code_base_src = NULL;
char* current_exe_dir = NULL;
char* CMAKE_PGBINDIR = NULL;
char* CMAKE_LIBDIR = NULL;
char* CMAKE_PGSHAREDIR = NULL;
void get_value_from_env(char** out_dir, char* env_name)
{
char *p;
if ((p = getenv(env_name))) {
*out_dir = strdup(p);
} else {
fprintf(stderr, _("\nERROR: could not get %s env\n"), env_name);
exit_nicely(2);
}
}
void get_value_from_cwd(char** out_dir)
{
if ((*out_dir = getcwd(NULL, 0)) == NULL)
{
fprintf(stderr, _("\n: could not get value from getcwd"));
exit_nicely(2);
}
}
#endif
#ifdef PGXC
/*
* In Postgres-XC, a regression test check is run on 2 Coordinators
* and 2 Datanodes. Coordinator 1 is considered as being the default
* used in pg_regress.
* External connections are made to Coordinator 1.
* All connections to remote nodes are made from Coordinator 1.
* Here is the list of nodes identified with a unique ID.
*/
typedef enum { PGXC_COORD_1, PGXC_COORD_2, PGXC_DATANODE_1, PGXC_DATANODE_2, PGXC_GTM } PGXCNodeTypeNum;
#endif
pgxc_node_info myinfo;
/* for resultmap we need a list of pairs of strings */
typedef struct _resultmap {
char* test;
char* type;
char* resultfile;
struct _resultmap* next;
} _resultmap;
/* for parallel initdb */
typedef struct thread_desc {
int thn; /* how may threads to spawn */
int thi; /* current thread counter */
pthread_t* thd; /* pthread_t array */
char** args; /* arguments for each thread */
} thread_desc;
/*
* Values obtained from pg_config_paths.h and Makefile. The PG installation
* paths are only used in temp_install mode: we use these strings to find
* out where "make install" will put stuff under the temp_install directory.
* In non-temp_install mode, the only thing we need is the location of psql,
* which we expect to find in psqldir, or in the PATH if psqldir isn't given.
*
* XXX Because pg_regress is not installed in bindir, we can't support
* this for relocatable trees as it is. --psqldir would need to be
* specified in those cases.
*/
char* bindir = PGBINDIR;
char* libdir = LIBDIR;
char* datadir = PGSHAREDIR;
char* host_platform = HOST_TUPLE;
char* top_builddir = NULL;
#ifndef WIN32_ONLY_COMPILER
static char* makeprog = MAKEPROG;
#endif
#ifndef WIN32 /* not used in WIN32 case */
static char* shellprog = SHELLPROG;
#endif
/*
* On Windows we use -w in diff switches to avoid problems with inconsistent
* newline representation. The actual result files will generally have
* Windows-style newlines, but the comparison files might or might not.
*/
#ifndef WIN32
const char* basic_diff_opts = "";
const char* pretty_diff_opts = "-C3";
#else
const char* basic_diff_opts = "-w";
const char* pretty_diff_opts = "-w -C3";
#endif
#define LOCAL_IP_LEN 20
#define REGR_MAX_NUM_OF_LINE_BUFF 12
#define REGR_MAX_PARALLEL_TESTS 200
#define REGR_MAX_SCH_LINE_SIZE (32 * 1024)
/* Maximum length for the time string */
#define MAX_REG_TIME_STR_LEN 25
/* Maximum token length */
#define MAX_REGR_TOKEN_LEN 25
#define MAX_ADD_DATA_LEN 27
double g_dTotalTime;
double g_dGroupTotalTime;
char g_acTestStartTime[MAX_REG_TIME_STR_LEN];
char g_acDataPath[MAXPGPATH + MAX_ADD_DATA_LEN];
#define REGR_CHR_EOS '\0'
#define REGR_MCR_SIZE_NIL 0
#define REGR_MCR_SIZE_1B 1
#define REGR_MCR_SIZE_1KB 1024
#define REGR_MCR_SIZE_1MB 1048576
#define REGR_MCR_SIZE_1GB 1073741824
char** g_ppcBuf = NULL;
unsigned int g_uiCurLineBufIdx = 0;
unsigned int g_uiTotalBuf = 0;
/* Maximum line length in regress.conf file */
#define MAX_LINE_LEN (1024)
/* Maximum length for the name of a configuration item in
* regress.conf file */
#define MAX_CONF_ITEM_NAME_LEN (128)
/* Current number of configuration items in regress.conf file */
#define MAX_REG_CONF_ITEMS (4)
#define REGR_DELAY_FOR_PERF_COLLECTION (100000L)
#define REGR_DELAY_FOR_PID_FILE_OPEN (1000000L)
#define REGR_NUM_OF_RETRY_FOR_PID_FILE_OPEN (15)
#define REGR_PID_WAIT_FOR_ANY_CHILD (-1)
#define NO_CHILD_PROC_HAS_CHANGED_STATE (0)
#define REGR_SIZE_OF_INDEX_ELEM (sizeof(unsigned int))
/* Array index definition for items in aacRegConfItemName */
#define REGR_TUPONLY_ID 0
#define REGR_COL_SEP_ID 1
#define REGR_PERF_DATA_LOGGING_ID 2
#define REGR_DIAG_COLLECT_ID 3
#define REGR_SUCCESS 0
#define REGR_ERRCODE_MALLOC_FAILURE 1
#define REGR_ERRCODE_FOPEN_FAILURE 2
#define REGR_ERRCODE_ENV_FETCH_FAILED 3
#define REGR_ERRCODE_BUFF_NOT_ENOUGH 4
#define REGR_ERRCODE_RESRC_STAT_FETCH_FAILED 5
#define REGR_ERRCODE_SYNTAX_ERR_IN_SCH 6
#define REGR_EOF_REACHED 7
#define REGR_ERRCODE_MAX_NUM_OF_LINE_BUFF 8
/* The number by which the memory block for replacement patterns gets expanded.
* It is also indicating the initial number of slots that is getting allocated*/
#define REGR_NUM_OF_REPLC_ITEMS_CHUNK 25
/* This is for the rough estimation of the size of the memory that need to be
* allocated for the storage of the replacement pattern strings.
* This shows the average size of the replacement pattern string */
#define REGR_AVG_REPLC_PATT_LEN 64
/* This is for the rough estimation of the size of the memory that need to be
* allocated for the storage of the replacement pattern strings.
* This shows the average size of the replacement pattern string */
#define REGR_AVG_REPLC_PATT_VAL_LEN 512
/* INDICATOR for the start of the replacement pattern string */
#define REGR_REPLC_PATT_START_CHAR '@'
/* INDICATOR for the end of the replacement pattern string */
#define REGR_REPLC_PATT_END_CHAR '@'
#define REGR_FREE(pvAddr) \
do { \
free((pvAddr)); \
(pvAddr) = NULL; \
} while (0)
#define REGR_START_TIMER \
{ \
if (g_bEnablePerfDataPrint) { \
time_t timer; \
struct tm* pstTmInfo = NULL; \
(void)gettimeofday(&g_stRegrStartTime, NULL); \
(void)time(&timer); \
pstTmInfo = localtime(&timer); \
if (NULL != pstTmInfo) \
(void)strftime(g_acTestStartTime, MAX_REG_TIME_STR_LEN, "%Y-%m-%d %H:%M:%S", pstTmInfo); \
} \
}
#define REGR_STOP_TIMER \
{ \
if (g_bEnablePerfDataPrint) { \
(void)gettimeofday(&g_stRegrStopTime, NULL); \
regrGetElapsedTime(); \
} \
}
#define REGR_PRINT_ELAPSED_TIME \
{ \
if (g_bEnablePerfDataPrint) { \
status(_("TIME START: [%s]. TIME TAKEN: [%9.3lf] %%UCPU: [%5.2lf] " \
"%%SCPU: [%5.2lf] %%MEM: [%5.2lf] MEM: [%-9s]\n"), \
g_acTestStartTime, \
g_dTotalTime, \
g_stResourceUsageDetails.dUAvgcpuUsage, \
g_stResourceUsageDetails.dSAvgcpuUsage, \
g_stResourceUsageDetails.dAvgMemUsagePct, \
g_stResourceUsageDetails.acMemUsage); \
status_end(); \
} \
}
typedef struct tagREGR_AVG_RSRCE_USAGE_STRU {
double dUAvgcpuUsage;
double dSAvgcpuUsage;
double dAvgMemUsage;
double dAvgMemUsagePct;
long unsigned int ulCount;
char acMemUsage[MAX_REGR_TOKEN_LEN];
} REGR_AVG_RSRCE_USAGE_STRU;
typedef struct tagREGR_RESRC_STAT_STRU {
long unsigned int ulUtimeTicks;
long int lCutimeTicks;
long unsigned int ulStimeTicks;
long int lCstimeTicks;
long unsigned int vsize;
long unsigned int rss; /* virtual memory size in bytes */
long unsigned int ulCpuTotalTime; /* Resident Set Size in bytes*/
double dMemPct; /* %MEM */
} REGR_RESRC_STAT_STRU;
/* Stores the start time of a test file execution */
#define REG_MAX_NUM (50)
double g_dOneGroupTotalTime;
double g_dOneRegrTotalTime[REG_MAX_NUM];
char g_acOneRegrTestStartTime[REG_MAX_NUM][MAX_REG_TIME_STR_LEN];
static struct timeval g_stRegrStartTimeTmp[REG_MAX_NUM];
/* Stores the end time of a test file execution */
static struct timeval g_stRegrStopTimeTmp[REG_MAX_NUM];
#define REGR_START_TIMER_TEMP(i) \
{ \
if (g_bEnablePerfDataPrint) { \
time_t timer; \
struct tm* pstTmInfo = NULL; \
(void)gettimeofday(&g_stRegrStartTimeTmp[i], NULL); \
(void)time(&timer); \
pstTmInfo = localtime(&timer); \
if (NULL != pstTmInfo) \
(void)strftime(g_acOneRegrTestStartTime[i], MAX_REG_TIME_STR_LEN, "%Y-%m-%d %H:%M:%S", pstTmInfo); \
} \
}
#define REGR_STOP_TIMER_TEMP(i) \
{ \
if (g_bEnablePerfDataPrint) { \
(void)gettimeofday(&g_stRegrStopTimeTmp[i], NULL); \
regrGetOneRegrElapsedTime(i); \
} \
}
#define REGR_PRINT_ELAPSED_TIME_TEMP(i) \
{ \
if (g_bEnablePerfDataPrint) { \
status(_("TIME TAKEN: [%9.3lf]"), g_dOneRegrTotalTime[i]); \
status_end(); \
} \
}
#define REGR_PRINT_ONEGROUP_ELAPSED_TIME \
{ \
if (g_bEnablePerfDataPrint) { \
struct timeval stRegrEndTime; \
(void)gettimeofday(&stRegrEndTime, NULL); \
g_dOneGroupTotalTime = (stRegrEndTime.tv_sec - g_stRegrStartTimeTmp[0].tv_sec) + \
(stRegrEndTime.tv_usec - g_stRegrStartTimeTmp[0].tv_usec) * 0.000001; \
g_dGroupTotalTime = g_dGroupTotalTime + g_dOneGroupTotalTime; \
g_dOneGroupTotalTime = 0; \
} \
}
static void regrGetOneRegrElapsedTime(int i)
{
g_dOneRegrTotalTime[i] = (g_stRegrStopTimeTmp[i].tv_sec - g_stRegrStartTimeTmp[i].tv_sec) +
(g_stRegrStopTimeTmp[i].tv_usec - g_stRegrStartTimeTmp[i].tv_usec) * 0.000001;
}
REGR_RESRC_STAT_STRU g_stPrevCpuUsage;
REGR_RESRC_STAT_STRU g_stCurrCpuUsage;
REGR_AVG_RSRCE_USAGE_STRU g_stResourceUsageDetails;
/* Names of the configuration items in regress.conf file */
char aacRegConfItemName[][MAX_CONF_ITEM_NAME_LEN] = {
"column_name_present", "column_separator", "performance_data_printing", "diagnostic_collect_on_fail"};
/* options settable from command line */
_stringlist* dblist = NULL;
bool debug = false;
char* inputdir = ".";
char* outputdir = ".";
char* psqldir = PGBINDIR;
char* launcher = NULL;
char* gausshomedir = "oldgausshome";
char* loginuser = NULL;
char pgbenchdir[MAXPGPATH];
static const int UPGRADE_GRAY_STAGE = 1;
static const int UPGRADE_GRAY_STAGE_ROLLBACK = 2;
static const int UPGRADE_GRAY_OBSERVE_STAGE = 3;
static const int UPGRADE_GRAY_OBSERVE_STAGE_ROLLBACK = 4;
static const int UPGRADE_FINASH = 5;
static _stringlist* loadlanguage = NULL;
static _stringlist* loadextension = NULL;
static int max_connections = 0;
static char* encoding = NULL;
static _stringlist* schedulelist = NULL;
static int upgrade_cn_num = 1;
static int upgrade_dn_num = 4;
static _stringlist* upgrade_schedulelist = NULL;
static _stringlist* extra_tests = NULL;
static char* pcRegConfFile = NULL;
static char* temp_install = NULL;
static char* temp_config = NULL;
static bool nolocale = false;
static bool use_existing = false;
static char* hostname = NULL;
static int port = -1;
static bool comm_tcp_mode = true;
static char* hdfshostname = "nohostname";
static char* hdfscfgpath = "noconfigpath";
static char* hdfsport = "noport";
static char* hdfsstoreplus = "nohdfsstoreplus";
/*define obs server ip and bucked name*/
static char* obshostname = "nohostname";
static char* obsbucket = "nobucket";
static char* ak = "noaccesskey";
static char* sk = "nosecretaccesskey";
static int dop = 1;
static bool port_specified_by_user = false;
static char* dlpath = PKGLIBDIR;
static char* user = NULL;
static _stringlist* extraroles = NULL;
static _stringlist* extra_install = NULL;
static FILE* start_script;
static FILE* stop_script;
/* define ai engine ip and port */
static char* g_aiehost = "nohostname";
static char* g_aieport = "noport";
static bool g_enable_segment = false;
/* internal variables */
static const char* progname;
static char* logfilename;
static FILE* logfile;
char* difffilename;
static bool clean = true;
static _resultmap* resultmap = NULL;
static bool postmaster_running = false;
static bool standby_defined = false;
static int success_count = 0;
static int fail_count = 0;
static int fail_ignore_count = 0;
static bool securitymode = false;
static bool ignore_exitcode = false;
static bool keep_run = false;
/* TRUE if diagnostic data need to be collected on test failure */
static bool g_bEnableDiagCollection = false;
/* TRUE if performance related data need to be printed,asPer the config value */
static bool g_bEnablePerfDataPrint = false;
static PID_TYPE g_iPostmasterPid = INVALID_PID;
/* Stores the start time of a test file execution */
static struct timeval g_stRegrStartTime;
/* Stores the end time of a test file execution */
static struct timeval g_stRegrStopTime;
/* To store the values of the regress.conf values */
REGR_CONF_ITEMS_STRU g_stRegrConfItems;
/* Module-global structure instance for the storing the details of the
* replacement pattern strings */
static REGR_REPLACE_PATTERNS_STRU g_stRegrReplcPatt;
/* In default, we do not change initial password */
static bool change_password = false;
/* Only init database, for inplace upgrade test use */
static bool init_database = false;
/* Do inplace upgrade before run regression tests */
static bool inplace_upgrade = false;
static bool parallel_initdb = false;
static char* data_base_dir = "../upgrade";
static char* old_bin_dir = "./tmp_check/bin";
static int grayscale_upgrade = -1;
static int upgrade_from = 0;
static char* upgrade_script_dir = "../upgrade";
static bool super_user_altered = true;
static bool passwd_altered = true;
bool test_single_node = false;
static char* platform = "euleros2.0_sp2_x86_64";
/* client logic jdbc run regression tests */
static bool use_jdbc_client = false;
static bool to_create_jdbc_user = false;
static bool is_skip_environment_cleanup = false;
static char* client_logic_hook = "encryption";
static _stringlist* destination_files = NULL;
static bool directory_exists(const char* dir);
static void make_directory(const char* dir);
static void convertSourcefilesIn(char*, char*, char*, char*);
static void loadRegressConf(char* pcRegConfFile);
static void processTuplesOnlyConfItem(char* pcConfigValue);
static void processColSepConfItem(const char* pcConfigValue);
static int regressStrncasecmp(const char* s1, const char* s2, int maxLenToCmp);
static void processPerfDataLoggingConf(const char* pcConfigValue);
static bool regrValidateBoolConfigVal(const char* pcConfigValue, int iConfId);
static void regrGetElapsedTime(void);
static int regrGetResrcUsage(const pid_t pid, REGR_RESRC_STAT_STRU* result);
static void regrCalcCpuUsagePct(const REGR_RESRC_STAT_STRU* pstCurrUsage, const REGR_RESRC_STAT_STRU* pstPrevUsage,
double* pdUcpuUsagePct, double* pdScpuUsagePct);
static void regrConvertSizeInBytesToReadableForm(unsigned long int ulValue, char* pcBuf, unsigned int uiBufSize);
static void kill_node(int i, int type);
static void cleanup_environment();
static void header(const char* fmt, ...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
static void status(const char* fmt, ...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
static void psql_command(const char* database, const char* query, ...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
#ifdef WIN32
typedef BOOL(WINAPI* __CreateRestrictedToken)(
HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
/* Windows API define missing from some versions of MingW headers */
#ifndef DISABLE_MAX_PRIVILEGE
#define DISABLE_MAX_PRIVILEGE 0x1
#endif
#endif
#ifdef PGXC
static void psql_command_node(const char* database, int i, int type, const char* query, ...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format(PG_PRINTF_ATTRIBUTE, 4, 5)));
#endif
static char* get_node_info_name(int i, int type, bool is_node_name);
static void initdb_node_info_parallel(bool standby);
static void setBinAndLibPath(bool);
/*
* allow core files if possible.
*/
void restartPostmaster(bool isOld);
void checkProcInsert();
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
static void unlimit_core_size(void)
{
struct rlimit lim;
(void)getrlimit(RLIMIT_CORE, &lim);
if (lim.rlim_max == 0) {
fprintf(stderr, _("%s: could not set core size: disallowed by hard limit\n"), progname);
return;
} else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max) {
lim.rlim_cur = lim.rlim_max;
(void)setrlimit(RLIMIT_CORE, &lim);
}
}
#endif
/*
* Converts the size (represented in bytes), to human readable form
* (in KB, MB or in GB) and the result is placed in the buffer.
*/
static void regrConvertSizeInBytesToReadableForm(unsigned long int ulValue, char* pcBuf, unsigned int uiBufSize)
{
/* Representing the passed value as such because it is less than 1 KB */
if (ulValue < REGR_MCR_SIZE_1KB)
(void)snprintf(pcBuf, uiBufSize, "%lu B", ulValue);
else if ((ulValue >= REGR_MCR_SIZE_1KB) && (ulValue < REGR_MCR_SIZE_1MB)) {
double dSize = (ulValue / (double)REGR_MCR_SIZE_1KB);
/* Representing the value in KB */
(void)snprintf(pcBuf, uiBufSize, "%-7.2f KB", dSize);
} else if ((ulValue >= REGR_MCR_SIZE_1MB) && (ulValue < REGR_MCR_SIZE_1GB)) {
double dSize = (ulValue / (double)REGR_MCR_SIZE_1MB);
/* Representing the value in MB */
(void)snprintf(pcBuf, uiBufSize, "%-7.2f MB", dSize);
} else if (ulValue >= REGR_MCR_SIZE_1GB) {
double dSize = (ulValue / (double)REGR_MCR_SIZE_1GB);
/* Representing the value in GB */
(void)snprintf(pcBuf, uiBufSize, "%-7.2f GB", dSize);
}
}
#define TwoPhaseTxntTestDirInput "2PCTxnt/input"
#define TwoPhaseTxntTestDirOutput "2PCTxnt/output"
/*
* Gets the process id of the GAUSS Server process and the result will be
* stored in g_iPostmasterPid
*/
static int regrGetServPid(char* pcBuff, unsigned int uiBuffLen,
unsigned int* puiPidChanged)
{
PID_TYPE iPid = INVALID_PID;
int iIter = REGR_NUM_OF_RETRY_FOR_PID_FILE_OPEN;
FILE* fpPMPid = NULL;
int type = test_single_node ? DATANODE : COORD;
const char* data_folder = get_node_info_name(0, type, false);
char cwd[MAXPGPATH] = {0};
#ifdef BUILD_BY_CMAKE // temp_install
if (!snprintf(cwd, MAXPGPATH, "%s", temp_install)) {
fprintf(stderr, _("\n Get temp_install dir fail.\n"));
return -1;
}
if (strlen(cwd) + strlen(data_folder) + strlen("/postmaster.pid") >= uiBuffLen) {
fprintf(stderr,
_("\n Buffer space not enough for holding the path of "
"the \'postmaster.pid\' file. Buffer Len: %u. Buffer "
"length needed: %lu.\n"),
uiBuffLen,
(strlen(cwd) + strlen(data_folder) + strlen("/postmaster.pid") + 1));
return REGR_ERRCODE_BUFF_NOT_ENOUGH;
}
(void)memset(pcBuff, '\0', uiBuffLen);
(void)snprintf(pcBuff, uiBuffLen, SYSTEMQUOTE "%s/%s/postmaster.pid" SYSTEMQUOTE, cwd, data_folder);
#else
if (!getcwd(cwd, MAXPGPATH)) {
fprintf(stderr, _("\n Get current dir fail.\n"));
return -1;
}
if (strlen(cwd) + strlen("/tmp_check/") + strlen(data_folder) + strlen("/postmaster.pid") >= uiBuffLen) {
fprintf(stderr,
_("\n Buffer space not enough for holding the path of "
"the \'postmaster.pid\' file. Buffer Len: %u. Buffer "
"length needed: %lu.\n"),
uiBuffLen,
(strlen(cwd) + strlen("/") + strlen("tmp_check/") + strlen(data_folder) + strlen("/postmaster.pid") + 1));
return REGR_ERRCODE_BUFF_NOT_ENOUGH;
}
(void)memset(pcBuff, '\0', uiBuffLen);
(void)snprintf(pcBuff, uiBuffLen, SYSTEMQUOTE "%s/tmp_check/%s/postmaster.pid" SYSTEMQUOTE, cwd, data_folder);
#endif
do {
fpPMPid = fopen(pcBuff, "r");
if (NULL != fpPMPid)
break;
iIter--;
pg_usleep(REGR_DELAY_FOR_PID_FILE_OPEN);
} while (iIter > 0);
free((char*)data_folder);
if (NULL == fpPMPid) {
fprintf(stderr, _("\n Could not open: [%s] for getting Server PID. \n"), pcBuff);
return REGR_ERRCODE_FOPEN_FAILURE;
}
iPid = g_iPostmasterPid;
g_iPostmasterPid = INVALID_PID;
fscanf(fpPMPid, "%d", &g_iPostmasterPid);
fclose(fpPMPid);
if ((INVALID_PID != iPid) && (g_iPostmasterPid != iPid)) {
*puiPidChanged = true;
}
return REGR_SUCCESS;
}
/*
* read /proc data into the passed REGR_RESRC_STAT_STRU
* returns 0 on success, -1 on error
*/
static int regrGetResrcUsage(const pid_t pid, REGR_RESRC_STAT_STRU* result)
{
char pid_s[20]; /* convert pid to string */
int i;
int iRet = 0;
long int slRss;
unsigned long int aulCpuTime[10] = {0};
unsigned long int ulTotalMemory = 0;
FILE* fpstat = NULL;
FILE* fcstat = NULL;
FILE* fpMemInfo = NULL;
char stat_filepath[30] = "/proc/";
(void)snprintf(pid_s, sizeof(pid_s), "%d", pid);
strncat(stat_filepath, pid_s, (sizeof(stat_filepath) - strlen(stat_filepath) - 1));
strncat(stat_filepath, "/stat", (sizeof(stat_filepath) - strlen(stat_filepath) - 1));
fpstat = fopen(stat_filepath, "r");
if (fpstat == NULL) {
fprintf(stderr, "fopen %s : %s", stat_filepath, strerror(errno));
return -1;
}
fcstat = fopen("/proc/stat", "r");
if (fcstat == NULL) {
iRet = -2;
goto LB_ERR_LVL_1;
}
fpMemInfo = fopen("/proc/meminfo", "r");
if (NULL == fpMemInfo) {
iRet = -3;
goto LB_ERR_LVL_2;
}
(void)memset(result, 0, sizeof(REGR_RESRC_STAT_STRU));
/* Read values from /proc/pid/stat */
if (fscanf(fpstat,
"%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
"%lu %ld %ld %*d %*d %*d %*d %*u %lu %ld",
&result->ulUtimeTicks,
&result->ulStimeTicks,
&result->lCutimeTicks,
&result->lCstimeTicks,
&result->vsize,
&slRss) == EOF) {
iRet = -4;
goto LB_ERR_LVL_3;
}
/* slRss will show the total number of pages actually in memory */
result->rss = slRss * getpagesize();
/* Read and calculate 'cpu total time' from /proc/stat */
if (fscanf(fcstat,
"%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
&aulCpuTime[0],
&aulCpuTime[1],
&aulCpuTime[2],
&aulCpuTime[3],
&aulCpuTime[4],
&aulCpuTime[5],
&aulCpuTime[6],
&aulCpuTime[7],
&aulCpuTime[8],
&aulCpuTime[9]) == EOF) {
iRet = -5;
goto LB_ERR_LVL_3;
}
for (i = 0; i < 10; i++)
result->ulCpuTotalTime += aulCpuTime[i];
/* Get the total memory (in kb) from /proc/meminfo file */
if (fscanf(fpMemInfo, "%*s %lu", &ulTotalMemory) == EOF) {
iRet = -6;
goto LB_ERR_LVL_3;
}
ulTotalMemory *= 1024;
result->dMemPct = (result->rss * 100) / ulTotalMemory;
/*printf("\n Rss (size of pages in memory): [%lu bytes]. Total Memory:"
" [%lu bytes]. Mem Pct: [%lf]...\n", result->rss, ulTotalMemory, result->dMemPct);*/
iRet = 0;
LB_ERR_LVL_3:
fclose(fpMemInfo);
LB_ERR_LVL_2:
fclose(fcstat);
LB_ERR_LVL_1:
fclose(fpstat);
return iRet;
}
/* Calculates the elapsed CPU usage between 2 measuring points. in percent */
static void regrCalcCpuUsagePct(const REGR_RESRC_STAT_STRU* pstCurrUsage, const REGR_RESRC_STAT_STRU* pstPrevUsage,
double* pdUcpuUsagePct, double* pdScpuUsagePct)
{
const long unsigned int total_time_diff = pstCurrUsage->ulCpuTotalTime - pstPrevUsage->ulCpuTotalTime;
if (0 != total_time_diff) {
*pdUcpuUsagePct = 100 * (((pstCurrUsage->ulUtimeTicks + pstCurrUsage->lCutimeTicks) -
(pstPrevUsage->ulUtimeTicks + pstPrevUsage->lCutimeTicks)) /
(double)total_time_diff);
*pdScpuUsagePct = 100 * ((((pstCurrUsage->ulStimeTicks + pstCurrUsage->lCstimeTicks) -
(pstPrevUsage->ulStimeTicks + pstPrevUsage->lCstimeTicks))) /
(double)total_time_diff);
} else {
*pdUcpuUsagePct = 0.0;
*pdScpuUsagePct = 0.0;
}
}
/* Get the CPU and Memory usage details from /proc/[pid]/stat file. If the
* server pid has become invalid (may be because of the server restart by the
* recovery framework), then the new server pid is fetched again and with this
* new value, we try to get CPU usage details. If the server PID has changed
* then puiPidChanged will be set to TRUE.
* In this case, all the readings taken for the old server is obsolete.
* So, the readings which are taken till that point, will be discarded. Thus
* the readings will not be accurate, for the tests which involves recovery.
*/
static int regrGetRsrcUsageWithRepeatOnFail(REGR_RESRC_STAT_STRU* pstResrcUsageStat, unsigned int* puiPidChanged)
{
int iRet = REGR_SUCCESS;
int iRetServPid = REGR_SUCCESS;
unsigned int uiExit = false;
do {
iRet = regrGetResrcUsage(g_iPostmasterPid, pstResrcUsageStat);
if (REGR_SUCCESS == iRet)
break;
/* Could not get the CPU usage for the current PID value.
* Server may have been restarted. So, get the updated ServerPID value*/
if ((true == uiExit) ||
(REGR_SUCCESS !=
(iRetServPid = regrGetServPid((char*)g_acDataPath, MAXPGPATH + MAX_ADD_DATA_LEN, puiPidChanged)))) {
fprintf(stderr,
_("\n Could not get the Resource usage details. "
"Internal error code: %d. regrGetServPid EC: %d. "
"Exit flag status: %u. \n"),
iRet,
iRetServPid,
uiExit);
return REGR_ERRCODE_RESRC_STAT_FETCH_FAILED;
}
if (true == *puiPidChanged) {
/* Server would have restarted. Thus PID changed. So reset the
* values based on the old PID */
(void)memset(&g_stResourceUsageDetails, 0, sizeof(REGR_AVG_RSRCE_USAGE_STRU));
}
uiExit = true;
} while (true);
return REGR_SUCCESS;
}
/* Get the subsequent reading for the CPU and Memory usage. For calculating
* the CPU usage, we need two set of readings of REGR_RESRC_STAT_STRU instance*/
static void regrGetSubsequentUsage()
{
unsigned int uiPidChanged = false;
double dUcpuUsagePct = 0.0;
double dScpuUsagePct = 0.0;
/* If the first CPU usage reading is not available, then there is no point
* in getting second CPU usage readings */
if (0 == g_stPrevCpuUsage.ulCpuTotalTime) {
return;
}
/* Getting the CPU Usage details */
if (REGR_SUCCESS != regrGetRsrcUsageWithRepeatOnFail(&g_stCurrCpuUsage, &uiPidChanged)) {
fprintf(stderr, _("Could not get the CPU Usage info!! \n"));
return;
}
if (false == uiPidChanged) {
/* In case of Server restart i.e. uiPidChanged is TRUE, g_stPrevCpuUsage
* value is based on the old pid which can't be compared against the
* current value. So, cannot call the below function. Otherwise,
* getting the cpu usage in percentage, for the previous and
* current values */
regrCalcCpuUsagePct(&g_stCurrCpuUsage, &g_stPrevCpuUsage, &dUcpuUsagePct, &dScpuUsagePct);
}
/* The current reading is set as the 'previous' reading so that next time
* this can be used to calculate the CPU %, because CPU % calculation
* requires two set of readings. */
g_stPrevCpuUsage = g_stCurrCpuUsage;
/* Memory usage is added up (in MBs) and finally the sum will be divided by
* the total reading count to get the average */
g_stResourceUsageDetails.dAvgMemUsage += (g_stCurrCpuUsage.rss / REGR_MCR_SIZE_1MB);
/* Adding up the Memory percents */
g_stResourceUsageDetails.dAvgMemUsagePct += g_stCurrCpuUsage.dMemPct;
/* Adding up the User CPU usage detail */
g_stResourceUsageDetails.dUAvgcpuUsage += dUcpuUsagePct;
/* Adding up the Kernel(system) CPU usage detail */
g_stResourceUsageDetails.dSAvgcpuUsage += dScpuUsagePct;
/* Updating the total sample counts */
g_stResourceUsageDetails.ulCount++;
}
/*
* Add an item at the end of a stringlist.
*/
void add_stringlist_item(_stringlist** listhead, const char* str)
{
_stringlist* newentry = (_stringlist*)malloc(sizeof(_stringlist));
_stringlist* oldentry = NULL;
newentry->str = strdup(str);
newentry->next = NULL;
if (*listhead == NULL) {
*listhead = newentry;
} else {
for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
/* skip */;
oldentry->next = newentry;
}
}
/*
* Free a stringlist.
*/
static void free_stringlist(_stringlist** listhead)
{
if (listhead == NULL || *listhead == NULL) {
return;
}
if ((*listhead)->next != NULL) {
free_stringlist(&((*listhead)->next));
}
free((*listhead)->str);
free(*listhead);
*listhead = NULL;
}
/*
* Split a delimited string into a stringlist
*/
static void split_to_stringlist(const char* s, const char* delim, _stringlist** listhead)
{
char* sc = strdup(s);
char* tmp_token = NULL;
char* token = strtok_r(sc, delim, &tmp_token);
while (token) {
add_stringlist_item(listhead, token);
token = strtok_r(NULL, delim, &tmp_token);
}
free(sc);
}
/* Trim LHS and RHS of the passed string for white spaces */
static void regrTrimString(char** ppcString)
{
while ((**ppcString == ' ') || (**ppcString == '\t'))
(*ppcString)++;
if ((**ppcString) != '\0') {
int len = strlen(*ppcString);
char* ptr = *ppcString + (len - 1);
while (((len--) > 0) && ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')))
ptr--;
*(ptr + 1) = '\0';
}
}
/*
* Print a progress banner on stdout.
*/
static void header(const char* fmt, ...)
{
char tmp[1024];
va_list ap;
va_start(ap, fmt);
(void)vsnprintf(tmp, sizeof(tmp), fmt, ap);
va_end(ap);
fprintf(stdout, "============== %-38s ==============\n", tmp);
(void)fflush(stdout);
}
/*
* Print "doing something ..." --- supplied text should not end with newline
*/
static void status(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void)vfprintf(stdout, fmt, ap);
(void)fflush(stdout);
va_end(ap);
if (logfile) {
va_start(ap, fmt);
(void)vfprintf(logfile, fmt, ap);
va_end(ap);
}
}
/*
* Done "doing something ..."
*/
static void status_end(void)
{
fprintf(stdout, "\n");
(void)fflush(stdout);
if (logfile) {
fprintf(logfile, "\n");
}
}
static int regr_system(const char* command)
{
int ret = system(command);
if (ignore_exitcode)
return 0;
return ret;
}
#ifdef PGXC
/*
* Stop GTM process
*/
static void stop_gtm(void)
{
const char* data_folder = get_node_info_name(0, GTM, false);
int r;
char buf[MAXPGPATH * 2];
/* On Windows, system() seems not to force fflush, so... */
(void)fflush(stdout);
(void)fflush(stderr);
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gtm_ctl\" stop -Z gtm -D \"%s/%s\" -m fast" SYSTEMQUOTE,
bindir,
temp_install,
data_folder);
r = system(buf);
if (r != 0) {
memset(buf, 0, sizeof(buf));
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gtm_ctl\" stop -Z gtm -D \"%s/%s\" -m i" SYSTEMQUOTE,
bindir,
temp_install,
data_folder);
r = regr_system(buf); /* second time we don't check exit code if --ignore-exitcode*/
if (r != 0) {
fprintf(stderr, _("\n%s: could not stop GTM: exit code was %d\n"), progname, r);
free((char*)data_folder);
exit(2); /* not exit(), that would be recursive */
}
}
free((char*)data_folder);
}
static void gen_startstop_script()
{
int i;
int port_number;
char buf[MAXPGPATH * 4];
if (temp_install == NULL)
return;
(void)snprintf(buf, sizeof(buf), "%s/start.sh", temp_install);
start_script = fopen(buf, PG_BINARY_W);
(void)snprintf(buf, sizeof(buf), "%s/stop.sh", temp_install);
stop_script = fopen(buf, PG_BINARY_W);
const char* data_folder = get_node_info_name(0, GTM, false);
fprintf(start_script, "#! /bin/bash \n");
fprintf(start_script, "find %s -name postmaster.pid | xargs rm -rf \n", temp_install);
fprintf(start_script, "%s/gs_gtm -n gtm -D %s/%s -p %d & \n", bindir, temp_install, data_folder, myinfo.gtm_port);
free((char*)data_folder);
fprintf(stop_script, "#! /bin/bash \n");
for (i = 0; i < myinfo.co_num; i++) {
data_folder = get_node_info_name(i, COORD, false);
port_number = myinfo.co_port[i];
fprintf(start_script,
"%s/gaussdb --coordinator -i -p %d -D %s/%s -c log_statement=all -c logging_collector=true -c "
"log_filename='cn.log' & \n",
bindir,
port_number,
temp_install,
data_folder);
fprintf(stop_script, "%s/gs_ctl stop -D %s/%s \n", bindir, temp_install, data_folder);
free((char*)data_folder);
}
for (i = 0; i < myinfo.dn_num; i++) {
data_folder = get_node_info_name(i, DATANODE, false);
port_number = myinfo.dn_port[i];
fprintf(start_script,
"%s/gaussdb --datanode -i -p %d -D %s/%s -c log_statement=all -c logging_collector=true -c "
"log_filename='dn.log' & \n",
bindir,
port_number,
temp_install,
data_folder);
fprintf(stop_script, "%s/gs_ctl stop -D %s/%s \n", bindir, temp_install, data_folder);
free((char*)data_folder);
}
const char* gtm_folder = get_node_info_name(0, GTM, false);
fprintf(stop_script, "%s/gtm_ctl stop -Z gtm -D %s/%s -m fast \n", bindir, temp_install, gtm_folder);
(void)snprintf(buf, sizeof(buf), "chmod +x %s/start.sh", temp_install);
(void)system(buf);
(void)snprintf(buf, sizeof(buf), "chmod +x %s/stop.sh", temp_install);
(void)system(buf);
fclose(start_script);
fclose(stop_script);
free((char*)gtm_folder);
}
/*
* Stop the given node
*/
static void stop_node(int i, int type, bool standby)
{
char* data_folder = get_node_info_name(i, type, false);
char buf[MAXPGPATH * 2];
int r;
/* On Windows, system() seems not to force fflush, so... */
(void)fflush(stdout);
(void)fflush(stderr);
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_ctl\" stop -D \"%s/%s\" -s -m fast >>/dev/null" SYSTEMQUOTE,
bindir,
temp_install,
data_folder);
r = system(buf);
if (r != 0) {
memset(buf, 0, sizeof(buf));
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_ctl\" stop -D \"%s/%s\" -s -m i >>/dev/null" SYSTEMQUOTE,
bindir,
temp_install,
data_folder);
r = regr_system(buf);
if (r != 0) {
fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"), progname, r);
exit(2); /* not exit(), that would be recursive */
}
}
if (!standby) {
free(data_folder);
return;
}
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_ctl\" stop -D \"%s/%s_standby\" -s -m fast >>/dev/null" SYSTEMQUOTE,
bindir,
temp_install,
data_folder);
r = system(buf);
if (r != 0) {
memset(buf, 0, sizeof(buf));
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_ctl\" stop -D \"%s/%s_standby\" -s -m i >>/dev/null" SYSTEMQUOTE,
bindir,
temp_install,
data_folder);
r = regr_system(buf);
if (r != 0) {
fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"), progname, r);
exit(2); /* not exit(), that would be recursive */
}
}
free(data_folder);
}
#endif
/*
* shut down temp postmaster
*/
static void stop_postmaster(void)
{
int i;
printf("stop postmaster now!\n");
if (postmaster_running) {
#ifdef PGXC
/*
* stop cn\dn
*/
for (i = 0; i < myinfo.co_num; i++) {
stop_node(i, COORD, false);
}
for (i = 0; i < myinfo.dn_num; i++) {
stop_node(i, DATANODE, standby_defined);
}
for (i = 0; i < myinfo.shell_count; i++) {
(void)kill(myinfo.shell_pid[i], SIGKILL);
}
if (!test_single_node) {
/* Stop GTM at the end */
stop_gtm();
}
#else
/* We use pg_ctl to issue the kill and wait for stop */
char buf[MAXPGPATH * 2];
int r;
/* On Windows, system() seems not to force fflush, so... */
fflush(stdout);
fflush(stderr);
snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_ctl\" stop -D \"%s/data\" -s -m fast" SYSTEMQUOTE,
bindir,
temp_install);
r = regr_system(buf);
if (r != 0) {
fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"), progname, r);
exit(2); /* not exit(), that would be recursive */
}
#endif
postmaster_running = false;
}
}
static void atexit_cleanup()
{
stop_postmaster();
cleanup_environment();
}
#ifdef PGXC
/*
* Handy subroutine for setting an environment variable "var" to "val"
*/
/*lint -e429*/
static void doputenv(const char* var, const char* val)
{
char* s = (char*)malloc(strlen(var) + strlen(val) + 2);
sprintf(s, "%s=%s", var, val);
(void)putenv(s);
}
/*lint +e429*/
/*
* Get node port of associated node
*/
static int get_port_number(int i, int type)
{
switch (type) {
case COORD:
return (test_single_node ? 0 : myinfo.co_port[i]);
case DATANODE:
return myinfo.dn_port[i];
case GTM:
return myinfo.gtm_port;
default:
/* Should not happen */
return -1;
}
}
/*
* Get PID number for given node
*/
static PID_TYPE get_node_pid(int i, int type)
{
switch (type) {
case COORD:
return myinfo.co_pid[i];
case DATANODE:
return myinfo.dn_pid[i];
case GTM:
return myinfo.gtm_pid;
default:
/* Should not happen */
return -1;
}
}
/*
* Start GTM process
*/
static void start_gtm(void)
{
char buf[MAXPGPATH * 4];
const char* data_folder = get_node_info_name(0, GTM, false);
PID_TYPE node_pid;
FILE* gtm_conf_file = NULL;
(void)snprintf(buf, sizeof(buf), "%s/%s/gtm.conf", temp_install, data_folder);
gtm_conf_file = fopen(buf, PG_BINARY_W);
if (gtm_conf_file == NULL) {
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
if (fclose(gtm_conf_file)) {
fprintf(stderr, _("%s: could not write file \"%s\": %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
header(_("starting GTM process"));
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_gtm\" -n \"gtm\" -D \"%s/%s\" -p %d > \"%s/log/gtm.log\" 2>&1" SYSTEMQUOTE,
bindir,
temp_install,
data_folder,
myinfo.gtm_port,
outputdir);
/* Start process */
node_pid = spawn_process(buf);
if (node_pid == INVALID_PID) {
fprintf(stderr, _("\n%s: could not spawn GTM: %s\n"), progname, strerror(errno));
exit_nicely(2);
}
/* Save static PID number */
myinfo.gtm_pid = node_pid;
}
/* Start single datanode for test */
static void start_single_node()
{
char buf[MAXPGPATH * 4];
int port_number = myinfo.dn_port[0];
char* data_folder = get_node_info_name(0, DATANODE, false);
PID_TYPE guc_pid;
char guc_buf[MAXPGPATH * 4];
char upgrade_from_str[MAXPGPATH] = {'\0'};
if (inplace_upgrade || grayscale_upgrade != -1) {
(void)snprintf(guc_buf,
sizeof(guc_buf),
SYSTEMQUOTE "\"%s/gs_guc\" set -Z datanode -D \"%s/%s\" -c \"%s\" > /dev/null 2>&1" SYSTEMQUOTE,
bindir,
temp_install,
data_folder,
upgrade_from == 0 ? "upgrade_mode=0" : grayscale_upgrade == -1 ? "upgrade_mode=1" : "upgrade_mode=2");
guc_pid = spawn_process(guc_buf);
if (guc_pid == INVALID_PID) {
fprintf(stderr, _("\n%s: could not spawn GUC parameter: %s\n"), progname, strerror(errno));
exit_nicely(2);
}
// check if guc parameter is set successfully
int result = 0;
int count = 0;
while (result != 1) {
count++;
char cmd[MAXPGPATH] = {'\0'};
#ifdef BUILD_BY_CMAKE
(void)snprintf(cmd,
sizeof(cmd),
"find %s/%s/ -name \"postgresql.conf\" | xargs grep \"upgrade_mode = %s\" | wc -l",
temp_install,
data_folder,
upgrade_from == 0 ? "0" : grayscale_upgrade == -1 ? "1" : "2");
#else
(void)snprintf(cmd,
sizeof(cmd),
"sync; find ./tmp_check/%s/ -name \"postgresql.conf\" | xargs grep \"upgrade_mode = %s\" | wc -l",
data_folder,
upgrade_from == 0 ? "0" : grayscale_upgrade == -1 ? "1" : "2");
#endif
FILE* fstream = NULL;
char buf[1000];
memset(buf, 0, sizeof(buf));
fstream = popen(cmd, "r");
if (NULL != fgets(buf, sizeof(buf), fstream)) {
result = atoi(buf);
}
if (result != 1) {
sleep(1);
}
if (count > 180) {
fprintf(stderr, _("\n%s: fail to set upgrade_mode GUC\n count %d"), progname, count);
exit(2);
}
if (NULL != fstream) {
pclose(fstream);
}
}
if (upgrade_from)
(void)snprintf(
upgrade_from_str, sizeof(upgrade_from_str), "-u %d -c allow_system_table_mods=true", upgrade_from);
}
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gaussdb\" %s %s %s -i -p %d -D \"%s/%s\"%s -c log_statement=all -c logging_collector=true -c "
"\"listen_addresses=%s\" > \"%s/log/postmaster_%s.log\" 2>&1" SYSTEMQUOTE,
bindir,
upgrade_from_str,
"--single_node",
(securitymode) ? "--securitymode" : " ",
port_number,
temp_install,
data_folder,
debug ? " -d 5" : "",
hostname ? hostname : "*",
outputdir,
data_folder);
header( _("\nstart cmd is : %s\n"), buf);
PID_TYPE datanode_pid = spawn_process(buf);
if (datanode_pid == INVALID_PID) {
fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"), progname, strerror(errno));
exit_nicely(2);
}
printf("Single datanode: node_pid %d.\n", datanode_pid);
myinfo.dn_pid[0] = datanode_pid;
free(data_folder);
}
/*
* Start given node
*/
static void start_my_node(int i, int type, bool is_main, bool standby, int upgrade_from)
{
int port_number;
char* data_folder = get_node_info_name(i, type, false);
if (type == COORD) {
port_number = myinfo.co_port[i];
} else {
port_number = myinfo.dn_port[i];
}
PID_TYPE guc_pid;
PID_TYPE node_pid;
char guc_buf[MAXPGPATH * 4];
char buf[MAXPGPATH * 4];
char upgrade_from_str[MAXPGPATH] = {'\0'};
(void)snprintf(guc_buf,
sizeof(guc_buf),
SYSTEMQUOTE "\"%s/gs_guc\" set -Z %s -D \"%s/%s\" -c \"%s\" " SYSTEMQUOTE,
bindir,
(type == COORD) ? "coordinator" : "datanode",
temp_install,
data_folder,
upgrade_from == 0 ? "upgrade_mode=0" : grayscale_upgrade == -1 ? "upgrade_mode=1" : "upgrade_mode=2");
guc_pid = spawn_process(guc_buf);
if (guc_pid == INVALID_PID) {
fprintf(stderr, _("\n%s: could not spawn GUC parameter: %s\n"), progname, strerror(errno));
exit_nicely(2);
}
// check if guc parameter is set successfully
int result = 0;
int count = 0;
while (result != 1) {
count++;
char cmd[MAXPGPATH] = {'\0'};
#ifdef BUILD_BY_CMAKE
(void)snprintf(cmd,
sizeof(cmd),
"find %s/%s/ -name \"postgresql.conf\" | xargs grep \"upgrade_mode = %s\" | wc -l",
temp_install,
data_folder,
upgrade_from == 0 ? "0" : grayscale_upgrade == -1 ? "1" : "2");
#else
(void)snprintf(cmd,
sizeof(cmd),
"find ./tmp_check/%s/ -name \"postgresql.conf\" | xargs grep \"upgrade_mode = %s\" | wc -l",
data_folder,
upgrade_from == 0 ? "0" : grayscale_upgrade == -1 ? "1" : "2");
#endif
FILE* fstream = NULL;
char buf[50];
memset(buf, 0, sizeof(buf));
fstream = popen(cmd, "r");
if (NULL != fgets(buf, sizeof(buf), fstream)) {
result = atoi(buf);
}
if (result != 1) {
sleep(1);
}
if (count > 180) {
fprintf(stderr, _("\n%s: fail to set upgrade_mode GUC\n count %d"), progname, count);
exit(2);
}
if (NULL != fstream) {
pclose(fstream);
}
}
if (upgrade_from)
(void)snprintf(
upgrade_from_str, sizeof(upgrade_from_str), "-u %d -c allow_system_table_mods=true", upgrade_from);
/* Start the node */
if (is_main) {
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE
"\"%s/gaussdb\" %s %s %s -i -p %d -D \"%s/%s\"%s -c log_statement=all -c logging_collector=true -c "
"\"listen_addresses=%s\" > \"%s/log/postmaster_%s.log\" 2>&1" SYSTEMQUOTE,
bindir,
upgrade_from_str,
(type == COORD) ? "--coordinator" : "--datanode",
(securitymode) ? "--securitymode" : " ",
port_number,
temp_install,
data_folder,
debug ? " -d 5" : "",
hostname ? hostname : "*",
outputdir,
data_folder);
} else {
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gaussdb\" %s %s %s -i -p %d -c log_statement=all -M %s -c logging_collector=true -D "
"\"%s/%s\"%s > \"%s/log/postmaster_%s.log\" 2>&1" SYSTEMQUOTE,
bindir,
upgrade_from_str,
(type == COORD) ? "--coordinator" : "--datanode",
(securitymode) ? "--securitymode" : " ",
port_number,
(standby) ? "primary" : "normal",
temp_install,
data_folder,
debug ? " -d 5" : "",
outputdir,
data_folder);
}
header( _("\nstart cmd is : %s\n"), buf);
node_pid = spawn_process(buf);
if (node_pid == INVALID_PID) {
fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"), progname, strerror(errno));
exit_nicely(2);
}
printf("node name: %s%d, node_pid: %d.\n", (type == COORD ? "Coordinator" : "Datanode"), i + 1, node_pid);
/* Save static PID number */
if (type == COORD) {
myinfo.co_pid[i] = node_pid;
} else {
myinfo.dn_pid[i] = node_pid;
}
free(data_folder);
}
/*
* Use gs_ctl build to setup datanode replication if requested.
*/
static void setup_datanode_replication(int index)
{
char* data_folder = get_node_info_name(index, DATANODE, false);
PID_TYPE node_pid;
char buf[MAXPGPATH * 4];
if (!standby_defined) {
free(data_folder);
return; /* nothing to do */
}
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_ctl\" build -b full -Z datanode -D \"%s/%s_standby\" > \"%s/log/build_%s_standby.log\" "
"2>&1" SYSTEMQUOTE,
bindir,
temp_install,
data_folder,
outputdir,
data_folder);
node_pid = spawn_process(buf);
if (node_pid == INVALID_PID) {
fprintf(stderr, _("\n%s: could not spawn gs_ctl: %s\n"), progname, strerror(errno));
exit_nicely(2);
}
free(data_folder);
}
static char* get_node_info_name(int i, int type, bool is_node_name)
{
char* mybuf = NULL;
int len = 100 * sizeof(char);
mybuf = (char*)malloc(len);
switch (type) {
case COORD: {
if (is_node_name) {
(void)snprintf(mybuf, len, "coordinator%d", i + 1);
} else {
(void)snprintf(mybuf, len, "coordinator%d", i + 1);
}
} break;
case DATANODE: {
if (is_node_name) {
(void)snprintf(mybuf, len, "datanode%d", i + 1);
} else {
(void)snprintf(mybuf, len, "datanode%d", i + 1);
}
} break;
case GTM: {
(void)snprintf(mybuf, len, "datagtm");
} break;
}
return mybuf;
}
/*
* Initialize GTM
*/
static void init_gtm(void)
{
if (test_single_node)
return;
const char* data_folder = get_node_info_name(0, GTM, false);
char buf[MAXPGPATH * 4];
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_initgtm\" -Z gtm -D \"%s/%s\" --noclean%s > \"%s/log/initgtm.log\" 2>&1 &" SYSTEMQUOTE,
bindir,
temp_install,
data_folder,
debug ? " --debug" : "",
outputdir);
free((char*)data_folder);
if (regr_system(buf)) {
fprintf(stderr,
_("\n%s: gs_initgtm failed\nExamine %s/log/initgtm.log for the reason.\nCommand was: %s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
}
thread_desc* create_thread_desc(int thn)
{
thread_desc* ret = (thread_desc*)malloc(sizeof(thread_desc));
ret->thn = thn;
ret->thi = 0;
ret->thd = (pthread_t*)malloc(sizeof(pthread_t) * thn);
ret->args = (char**)malloc(sizeof(char*) * thn);
for (int i = 0; i < ret->thn; i++) {
ret->args[i] = (char*)malloc(sizeof(char) * (MAXPGPATH * 4));
}
return ret;
}
void free_thread_desc(thread_desc* desc)
{
int i;
/* free memory */
for (i = 0; i < desc->thn; i++) {
free(desc->args[i]);
}
free(desc->args);
free(desc->thd);
free(desc);
}
/*
* the thread function that invoke the gs_initdb
*
* @arg: the argument for the thread
*/
static void* thread_initdb(void* arg)
{
if (regr_system((const char*)arg)) {
pthread_exit((void*)"fail");
}
pthread_exit((void*)"ok");
}
/*
* formulate the args to gs_initdb and spawn a thread to run
*
* @is_cn: cn or dn?
* @is_standby: is standby ?
* @i: which thread?
* @thd: pthread_t array
* @thi: thread counter
* @args: arguments for each thread
*/
static void start_thread(thread_desc* thread, bool is_cn, bool is_standby, int i)
{
char* data_folder = get_node_info_name(i, is_cn ? COORD : DATANODE, false);
char** args = thread->args;
int thi = thread->thi;
pthread_t* thd = &(thread->thd[thi]);
printf("bindir: %s\n", bindir);
(void)snprintf(args[thi],
MAXPGPATH * 4,
SYSTEMQUOTE "\"%s/gs_initdb\" --nodename %s %s -w \"gauss@123\" -D \"%s/%s%s\" -L \"%s\" --noclean%s%s > "
"\"%s/log/initdb%d.log\" 2>&1" SYSTEMQUOTE,
bindir,
(char*)get_node_info_name(i, is_cn ? COORD : DATANODE, true),
init_database ? "-U upcheck" : "",
temp_install,
data_folder,
is_standby ? "_standby" : "",
datadir,
debug ? " --debug" : "",
nolocale ? " --no-locale" : "",
outputdir,
thi);
/* ready to play */
if (0 != pthread_create(thd, NULL, &thread_initdb, (void*)args[thi])) {
fprintf(stderr, _("\n%s: gs_initdb failed to spawn thread for gs_initdb\n"), progname);
exit(2);
} else {
fprintf(stdout, " %d", thi);
(void)fflush(stdout);
}
thread->thi++;
free(data_folder);
}
/*
* wait the thread to finish
*
* @thn: how many thread to wait
* @thd: pthread_t array
* @args: arguments for each thread
*/
static void wait_thread(thread_desc* thread)
{
int i;
bool has_error = false;
pthread_t* thd = thread->thd;
char** args = thread->args;
int thn = thread->thn;
fprintf(stdout, "\n==============waithing for thread to finish==============\n");
(void)fflush(stdout);
/* wait for thread join and free memory */
for (i = 0; i < thn; i++) {
char* retval = NULL;
if (0 != pthread_join(thd[i], (void**)&retval)) {
fprintf(stderr, _("\n%s: gs_initdb failed to join threads\n"), progname);
has_error = true;
}
if (0 != strncmp(retval, "ok", 2)) {
fprintf(stderr,
_("\n%s: thread %d failed to initdb, please check \"%s/log/initdb%d.log\", command was %s\n"),
progname,
i,
outputdir,
i,
args[i]);
has_error = true;
}
}
if (has_error) {
fprintf(stdout, "==============initdb failed==============\n");
exit(2);
} else {
fprintf(stdout, "==============initdb finished==============\n");
(void)fflush(stdout);
}
}
/* Copy data file to data dir */
static void CopyFile()
{
char dstDatadir[MAXPGPATH];
char cmdBuf[MAXPGPATH * 2];
errno_t rc = snprintf_s(dstDatadir, sizeof(dstDatadir), sizeof(dstDatadir) - 1, "%s/%s/pg_copydir",
temp_install, get_node_info_name(0, DATANODE, false));
securec_check_ss_c(rc, "", "");
rc = snprintf_s(cmdBuf, sizeof(cmdBuf), sizeof(cmdBuf) - 1,
SYSTEMQUOTE "mkdir -p %s ; cp ./data/* %s -r" SYSTEMQUOTE, dstDatadir, dstDatadir);
securec_check_ss_c(rc, "", "");
if (regr_system(cmdBuf)) {
(void)fprintf(stderr,
_("\n%s: cp data file failed\nCommand was: %s\n"),
progname,
cmdBuf);
exit_nicely(2);
}
/* mkdir results dir */
rc = snprintf_s(dstDatadir, sizeof(dstDatadir), sizeof(dstDatadir) - 1, "%s/%s/pg_copydir/results",
temp_install, get_node_info_name(0, DATANODE, false));
securec_check_ss_c(rc, "", "");
rc = snprintf_s(cmdBuf, sizeof(cmdBuf), sizeof(cmdBuf) - 1, SYSTEMQUOTE "mkdir -p %s" SYSTEMQUOTE, dstDatadir);
securec_check_ss_c(rc, "", "");
if (regr_system(cmdBuf)) {
(void)fprintf(stderr,
_("\n%s: mkdir failed\nCommand was: %s\n"),
progname,
cmdBuf);
exit_nicely(2);
}
}
/*
* initdb for cn and dn
*
* @standby: if init standby node
*/
static void initdb_node_info_parallel(bool standby)
{
int i;
thread_desc* thread = create_thread_desc(myinfo.co_num + myinfo.dn_num * (standby ? 2 : 1));
fprintf(stdout, "==============spawned thread");
(void)fflush(stdout);
for (i = 0; i < myinfo.co_num; i++) {
start_thread(thread, true, false, i);
}
for (i = 0; i < myinfo.dn_num; i++) {
start_thread(thread, false, false, i);
}
if (standby) {
for (i = 0; i < myinfo.dn_num; i++) {
start_thread(thread, false, true, i);
}
}
fprintf(stdout, " for parallel initdb==============");
(void)fflush(stdout);
/* wait for thread join */
wait_thread(thread);
/* free the memory */
free_thread_desc(thread);
CopyFile();
}
static void initdb_node_info(bool standby)
{
int i;
for (i = 0; i < myinfo.co_num; i++) {
char buf[MAXPGPATH * 4];
printf("bindir: %s\n", bindir);
char* data_folder = get_node_info_name(i, COORD, false);
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_initdb\" --nodename %s %s -w \"gauss@123\" -D \"%s/%s\" -L \"%s\" --noclean%s%s > "
"\"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
bindir,
(char*)get_node_info_name(i, COORD, true),
init_database ? "-U upcheck" : "",
temp_install,
data_folder,
datadir,
debug ? " --debug" : "",
nolocale ? " --no-locale" : "",
outputdir);
free(data_folder);
if (regr_system(buf)) {
fprintf(stderr,
_("\n%s: gs_initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
}
for (i = 0; i < myinfo.dn_num; i++) {
char buf[MAXPGPATH * 4];
char* data_folder = get_node_info_name(i, DATANODE, false);
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_initdb\" --nodename %s %s -w \"gauss@123\" -D \"%s/%s\" -L \"%s\" --noclean%s%s > "
"\"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
bindir,
(char*)get_node_info_name(i, DATANODE, true),
init_database ? "-U upcheck" : "",
temp_install,
data_folder,
datadir,
debug ? " --debug" : "",
nolocale ? " --no-locale" : "",
outputdir);
if (regr_system(buf)) {
fprintf(stderr,
_("\n%s: gs_initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
free(data_folder);
}
CopyFile();
/* no standby configed, skip to init standby. */
if (!standby)
return;
for (i = 0; i < myinfo.dn_num; i++) {
char buf[MAXPGPATH * 4];
char* data_folder = get_node_info_name(i, DATANODE, false);
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "\"%s/gs_initdb\" --nodename %s %s -w \"gauss@123\" -D \"%s/%s_standby\" -L \"%s\" "
"--noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
bindir,
(char*)get_node_info_name(i, DATANODE, true),
init_database ? "-U upcheck" : "",
temp_install,
data_folder,
datadir,
debug ? " --debug" : "",
nolocale ? " --no-locale" : "",
outputdir);
if (regr_system(buf)) {
fprintf(stderr,
_("\n%s: gs_initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
free(data_folder);
}
}
int get_local_ip(char* local_ip)
{
char buf[100];
struct addrinfo hints;
struct addrinfo *res, *curr;
struct sockaddr_in* sa;
errno_t ss_rc = 0;
ss_rc = memset_s(&hints, sizeof(addrinfo), 0x0, sizeof(addrinfo));
securec_check_c(ss_rc, "\0", "\0");
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
if (gethostname(buf, sizeof(buf)) < 0) {
return -1;
}
if (getaddrinfo(buf, 0, &hints, &res) != 0) {
return -1;
}
if (NULL == res) {
return -1;
}
curr = res;
sa = (struct sockaddr_in*)curr->ai_addr;
(void)snprintf(local_ip, LOCAL_IP_LEN, "%s", inet_ntop(AF_INET, &sa->sin_addr.s_addr, buf, sizeof(buf)));
if (res) {
freeaddrinfo(res);
}
return 0;
}
static void config_cn();
static void config_dn(bool standby);
static void config_standby();
static void initdb_node_config_file(bool standby)
{
config_cn();
config_dn(standby);
if (!standby)
return;
config_standby();
}
static void config_cn()
{
int i;
char local_ip[LOCAL_IP_LEN];
for (i = 0; i < myinfo.co_num; i++) {
char* data_folder = get_node_info_name(i, COORD, false);
FILE* pg_conf = NULL;
char buf[MAXPGPATH * 4];
(void)snprintf(buf, sizeof(buf), "%s/%s/postgresql.conf", temp_install, data_folder);
pg_conf = fopen(buf, "a");
if (pg_conf == NULL) {
fprintf(
stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
(void)snprintf(buf, sizeof(buf), "port = %d\n", myinfo.co_port[i]);
fputs(buf, pg_conf);
/*
* Cluster uses 2PC for write transactions involving multiple nodes
* This has to be set at least to a value corresponding to the maximum
* number of tests run in parallel.
*/
fputs("max_prepared_transactions = 50\n", pg_conf);
/* Set GTM connection information */
fputs("gtm_host = 'localhost'\n", pg_conf);
(void)snprintf(buf, sizeof(buf), "gtm_port = %d\n", myinfo.gtm_port);
fputs(buf, pg_conf);
(void)snprintf(buf, sizeof(buf), "pooler_port = %d\n", myinfo.co_pool_port[i]);
fputs(buf, pg_conf);
/*Turn on the guc control support_extended_features*/
fputs("support_extended_features = on\n", pg_conf);
/* make enable_fast_query_shipping to true */
fputs("enable_fast_query_shipping = on\n", pg_conf);
/* always use LLVM optimization */
fputs("codegen_cost_threshold=0\n", pg_conf);
#ifdef USE_ASSERT_CHECKING
if (g_enable_segment) {
fputs("enable_segment = on\n", pg_conf);
}
#endif
if (dop > 0) {
(void)snprintf(buf, sizeof(buf), "query_dop = %d\n", dop);
fputs(buf, pg_conf);
}
if (temp_config != NULL) {
FILE* extra_conf = NULL;
char line_buf[1024];
extra_conf = fopen(temp_config, "r");
if (extra_conf == NULL) {
fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"),
progname, temp_config, strerror(errno));
exit_nicely(2);
}
while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL) {
fputs(line_buf, pg_conf);
}
fclose(extra_conf);
}
fclose(pg_conf);
memset(local_ip, 0, sizeof(local_ip));
if (0 != get_local_ip(local_ip)) {
strncpy(local_ip, "localhost", LOCAL_IP_LEN);
}
(void)snprintf(buf, sizeof(buf), "%s/%s/pg_hba.conf", temp_install, data_folder);
pg_conf = fopen(buf, "a");
if (pg_conf == NULL) {
fprintf(
stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
(void)snprintf(buf, sizeof(buf), " host all all %s/32 trust\n", local_ip);
fputs(buf, pg_conf);
fclose(pg_conf);
free(data_folder);
}
}
static void config_dn(bool standby)
{
int i;
char local_ip[LOCAL_IP_LEN];
for (i = 0; i < myinfo.dn_num; i++) {
char* data_folder = get_node_info_name(i, DATANODE, false);
FILE* pg_conf = NULL;
char buf[MAXPGPATH * 4];
(void)snprintf(buf, sizeof(buf), "%s/%s/postgresql.conf", temp_install, data_folder);
pg_conf = fopen(buf, "a");
if (pg_conf == NULL) {
fprintf(
stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
(void)snprintf(buf, sizeof(buf), "port = %d\n", myinfo.dn_port[i]);
fputs(buf, pg_conf);
/*
* Cluster uses 2PC for write transactions involving multiple nodes
* This has to be set at least to a value corresponding to the maximum
* number of tests run in parallel.
*/
fputs("max_prepared_transactions = 50\n", pg_conf);
if (!test_single_node) {
(void)snprintf(buf, sizeof(buf), "pooler_port = %d\n", myinfo.dn_pool_port[i]);
fputs(buf, pg_conf);
if (comm_tcp_mode == false)
fputs("comm_tcp_mode = false\n", pg_conf);
}
/*Turn on the guc control support_extended_features*/
fputs("support_extended_features = on\n", pg_conf);
if (!test_single_node) {
/* make enable_fast_query_shipping to true */
fputs("enable_fast_query_shipping = on\n", pg_conf);
}
/* always use LLVM optimization */
fputs("codegen_cost_threshold=0\n", pg_conf);
#ifdef USE_ASSERT_CHECKING
if (g_enable_segment) {
fputs("enable_segment = on\n", pg_conf);
}
#endif
if (dop > 0) {
(void)snprintf(buf, sizeof(buf), "query_dop = %d\n", dop);
fputs(buf, pg_conf);
}
if (!test_single_node) {
/* Set GTM connection information */
fputs("gtm_host = 'localhost'\n", pg_conf);
(void)snprintf(buf, sizeof(buf), "gtm_port = %d\n", myinfo.gtm_port);
fputs(buf, pg_conf);
}
if (standby) {
(void)snprintf(buf, sizeof(buf),
"replconninfo1 = 'localhost=127.0.0.1 localport=%d remotehost=127.0.0.1 remoteport=%d'\n",
myinfo.dn_primary_port[i], myinfo.dn_standby_port[i]);
fputs(buf, pg_conf);
(void)snprintf(buf, sizeof(buf),
"replconninfo2 = 'localhost=127.0.0.1 localport=%d remotehost=127.0.0.1 remoteport=%d'\n",
myinfo.dn_primary_port[i], myinfo.dn_secondary_port[i]);
fputs(buf, pg_conf);
}
if (temp_config != NULL) {
FILE* extra_conf = NULL;
char line_buf[1024];
extra_conf = fopen(temp_config, "r");
if (extra_conf == NULL) {
fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"),
progname, temp_config, strerror(errno));
exit_nicely(2);
}
while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL) {
if (test_single_node && (strncmp(line_buf, "comm_tcp_mode", 13) == 0 ||
strncmp(line_buf, "enable_stateless_pooler_reuse", 29) == 0)) {
continue;
}
fputs(line_buf, pg_conf);
}
fclose(extra_conf);
}
fclose(pg_conf);
memset(local_ip, 0, sizeof(local_ip));
if (0 != get_local_ip(local_ip)) {
strncpy(local_ip, "localhost", LOCAL_IP_LEN);
}
(void)snprintf(buf, sizeof(buf), "%s/%s/pg_hba.conf", temp_install, data_folder);
pg_conf = fopen(buf, "a");
if (pg_conf == NULL) {
fprintf(
stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
(void)snprintf(buf, sizeof(buf), " host all all %s/32 trust\n", local_ip);
fputs(buf, pg_conf);
fclose(pg_conf);
free(data_folder);
}
}
static void config_standby()
{
int i;
for (i = 0; i < myinfo.dn_num; i++) {
char* data_folder = get_node_info_name(i, DATANODE, false);
FILE* pg_conf = NULL;
char buf[MAXPGPATH * 4];
(void)snprintf(buf, sizeof(buf), "%s/%s_standby/postgresql.conf", temp_install, data_folder);
pg_conf = fopen(buf, "a");
if (pg_conf == NULL) {
fprintf(
stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
(void)snprintf(buf, sizeof(buf), "port = %d\n", myinfo.dns_port[i]);
fputs(buf, pg_conf);
(void)snprintf(buf, sizeof(buf),
"replconninfo1 = 'localhost=127.0.0.1 localport=%d remotehost=127.0.0.1 remoteport=%d'\n",
myinfo.dn_standby_port[i], myinfo.dn_primary_port[i]);
fputs(buf, pg_conf);
(void)snprintf(buf, sizeof(buf),
"replconninfo2 = 'localhost=127.0.0.1 localport=%d remotehost=127.0.0.1 remoteport=%d'\n",
myinfo.dn_standby_port[i], myinfo.dn_secondary_port[i]);
fputs(buf, pg_conf);
if (temp_config != NULL) {
FILE* extra_conf = NULL;
char line_buf[1024];
extra_conf = fopen(temp_config, "r");
if (extra_conf == NULL) {
fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"),
progname, temp_config, strerror(errno));
exit_nicely(2);
}
while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL) {
fputs(line_buf, pg_conf);
}
fclose(extra_conf);
}
fclose(pg_conf);
free(data_folder);
}
}
static void psql_command(const char* database, const char* query, ...)
{
char query_formatted[1024];
char query_escaped[2048];
char psql_cmd[MAXPGPATH + 2048];
va_list args;
char* s = NULL;
char* d = NULL;
/* Generate the query with insertion of sprintf arguments */
va_start(args, query);
(void)vsnprintf(query_formatted, sizeof(query_formatted), query, args);
va_end(args);
/* Now escape any shell double-quote metacharacters */
d = query_escaped;
for (s = query_formatted; *s; s++) {
if (strchr("\\\"$`", *s)) {
*d++ = '\\';
}
*d++ = *s;
}
*d = '\0';
int port = test_single_node ? myinfo.dn_port[0] : myinfo.co_port[0];
/* And now we can build and execute the shell command */
(void)snprintf(psql_cmd,
sizeof(psql_cmd),
SYSTEMQUOTE "\"%s%sgsql\" -X %s -p %d -c \"%s\" \"%s\"" SYSTEMQUOTE,
psqldir ? psqldir : "",
psqldir ? "/" : "",
super_user_altered ? "" : (passwd_altered ? "-U upcheck -W Gauss@123" : "-U upcheck -W gauss@123"),
port,
query_escaped,
database);
if (regr_system(psql_cmd) != 0) {
/* psql probably already reported the error */
fprintf(stderr, _("command failed: %s\n"), psql_cmd);
exit_nicely(2);
}
}
/*
* Issue a command via psql, connecting to the specified database on wanted node
* Since we use system(), this doesn't return until the operation finishes
* This code is duplicated with psql_command but this way code impact is limited.
*/
static void psql_command_node(const char* database, int i, int type, const char* query, ...)
{
char query_formatted[1024];
char query_escaped[2048];
char psql_cmd[MAXPGPATH + 2048];
va_list args;
char* s = NULL;
char* d = NULL;
/* Generate the query with insertion of sprintf arguments */
va_start(args, query);
(void)vsnprintf((char*)query_formatted, sizeof(query_formatted), query, args);
va_end(args);
/* Now escape any shell double-quote metacharacters */
d = query_escaped;
for (s = query_formatted; *s; s++) {
if (strchr("\\\"$`", *s)) {
*d++ = '\\';
}
*d++ = *s;
}
*d = '\0';
int port = test_single_node ? myinfo.dn_port[0] : get_port_number(i, type);
/* And now we can build and execute the shell command */
(void)snprintf(psql_cmd,
sizeof(psql_cmd),
SYSTEMQUOTE "\"%s%sgsql\" %s %s -X -p %d -c \"%s\" \"%s\"" SYSTEMQUOTE,
psqldir ? psqldir : "",
psqldir ? "/" : "",
super_user_altered ? "" : (passwd_altered ? "-U upcheck -W Gauss@123" : "-U upcheck -W gauss@123"),
(type == DATANODE && !super_user_altered) ? "-m" : "",
port,
query_escaped,
database);
if (regr_system(psql_cmd) != 0) {
/* psql probably already reported the error */
fprintf(stderr, _("command failed: %s\n"), psql_cmd);
exit_nicely(2);
}
}
/*
* strdup() and malloc() replacements that prints an error and exits
* if something goes wrong. Can never return NULL.
*/
static char* xstrdup(const char* s)
{
char* result = NULL;
result = strdup(s);
if (!result) {
fprintf(stderr, _("%s: out of memory\n"), progname);
exit(1);
}
return result;
}
/*
* find the current user
*
* on unix make sure it isn't really root
*/
static char* get_id(void)
{
struct passwd* pw;
if (geteuid() == 0) /* 0 is root's uid */
{
fprintf(stderr,
_("%s: cannot be run as root\n"
"Please log in (using, e.g., \"su\") as the "
"(unprivileged) user that will\n"
"own the server process.\n"),
progname);
exit(1);
}
pw = getpwuid(geteuid());
if (!pw) {
fprintf(stderr, _("%s: could not obtain information about current user: %s\n"), progname, strerror(errno));
exit(1);
}
return xstrdup(pw->pw_name);
}
static void rebuild_node_group()
{
int i = 0;
int j = 0;
char nname_list[256];
char create_nodegroup[512];
char drop_nodegroup[512];
char* pname = NULL;
sprintf(nname_list, "(");
for (j = 0; j < myinfo.dn_num; j++) {
pname = get_node_info_name(j, DATANODE, true);
strcat(nname_list, pname);
if (j < myinfo.dn_num - 1)
strcat(nname_list, ",");
free(pname);
pname = NULL;
}
strcat(nname_list, ")");
sprintf(drop_nodegroup, "DROP NODE GROUP group1");
sprintf(create_nodegroup, "CREATE NODE GROUP group1 WITH %s", nname_list);
fprintf(stderr, "%s\n", drop_nodegroup);
fprintf(stderr, "%s\n", create_nodegroup);
for (i = 0; i < myinfo.co_num; i++) {
psql_command_node("postgres", i, COORD, "%s", drop_nodegroup);
psql_command_node("postgres", i, COORD, "%s", create_nodegroup);
}
for (i = 0; i < myinfo.co_num; i++) {
psql_command_node("postgres", i, COORD, "SELECT pgxc_pool_reload();");
psql_command_node("postgres",
i,
COORD,
"SELECT oid, node_name, node_type,"
"node_host, node_port, node_host1, node_port1, "
"sctp_port, control_port, sctp_port1, control_port1 "
"from pgxc_node;");
}
}
/*
* Setup connection information to remote nodes for coordinator running regression
*/
static void setup_connection_information(bool standby)
{
header(_("setting connection information"));
/* Datanodes on Coordinator 1*/
header(_("sleeping"));
if (temp_config && strcmp(temp_config, "./make_wlmcheck_postgresql.conf") == 0)
sleep(25);
sleep(60);
int i, j;
char nname_list[256], sql_tmp[512];
char* pname = NULL;
char local_ip[LOCAL_IP_LEN];
memset(local_ip, 0, sizeof(local_ip));
if (0 != get_local_ip(local_ip)) {
strncpy(local_ip, "localhost", LOCAL_IP_LEN);
}
for (i = 0; i < myinfo.co_num; i++) {
for (j = 0; j < myinfo.dn_num; j++) {
char* node_info_name = get_node_info_name(j, DATANODE, true);
if (standby) {
psql_command_node("postgres",
i,
COORD,
"CREATE NODE %s WITH (type='datanode', "
"host='%s', port=%d, host1='%s', port1=%d, "
"sctp_port=%d, control_port=%d, sctp_port1=%d, control_port1=%d);",
node_info_name,
"localhost",
get_port_number(j, DATANODE),
"localhost",
myinfo.dns_port[j],
myinfo.dn_sctp_port[j],
myinfo.dn_ctl_port[j],
myinfo.dns_sctp_port[j],
myinfo.dns_ctl_port[j]);
} else {
psql_command_node("postgres",
i,
COORD,
"CREATE NODE %s WITH (type='datanode', "
"host='%s', port=%d, "
"sctp_port=%d, control_port=%d);",
node_info_name,
"localhost",
get_port_number(j, DATANODE),
myinfo.dn_sctp_port[j],
myinfo.dn_ctl_port[j]);
}
free(node_info_name);
}
}
sprintf(nname_list, "(");
for (j = 0; j < myinfo.dn_num; j++) {
pname = get_node_info_name(j, DATANODE, true);
strcat(nname_list, pname);
if (j < myinfo.dn_num - 1)
strcat(nname_list, ",");
free(pname);
pname = NULL;
}
strcat(nname_list, ")");
sprintf(sql_tmp, "CREATE NODE GROUP group1 WITH %s", nname_list);
fprintf(stderr, "%s\n", sql_tmp);
for (i = 0; i < myinfo.co_num; i++)
psql_command_node("postgres", i, COORD, "%s", sql_tmp);
header(_("sleeping"));
sleep(10);
/* Remote Coordinator on other Coordinator */
for (i = 0; i < myinfo.co_num; i++) {
for (j = 0; j < myinfo.co_num; j++) {
if (j != i) {
const char* central_node = "";
if (j == 1)
central_node = ", NODEIS_CENTRAL=true";
char* node_info_name = get_node_info_name(j, COORD, true);
psql_command_node("postgres",
i,
COORD,
"CREATE NODE %s WITH (HOST = '%s',"
" type = 'coordinator', PORT = %d,"
" sctp_port = %d, control_port = %d%s);",
node_info_name,
"localhost",
get_port_number(j, COORD),
myinfo.co_sctp_port[j],
myinfo.co_ctl_port[j],
central_node);
free(node_info_name);
}
}
}
/* We only need do this in MAKEFASTCHECK */
if (change_password) {
char* current_user = get_id();
sleep(10);
psql_command("postgres",
"Alter user \"%s\" identified by 'Gauss@123' replace 'gauss@123';",
inplace_upgrade ? "upcheck" : current_user);
free(current_user);
passwd_altered = true;
}
/* Then reload the connection data */
for (i = 0; i < myinfo.co_num; i++) {
psql_command_node("postgres",
i,
COORD,
"%s",
"UPDATE pgxc_group SET is_installation = true "
"WHERE group_name = 'group1'");
psql_command_node("postgres", i, COORD, "SELECT pgxc_pool_reload();");
psql_command_node("postgres",
i,
COORD,
"SELECT oid, node_name, node_type,"
"node_host, node_port, node_host1, node_port1, "
"sctp_port, control_port, sctp_port1, control_port1 "
"from pgxc_node;");
}
}
/*
* Check if given node has failed during startup
*/
static void check_node_fail(int i, int type)
{
PID_TYPE pid_number = INVALID_PID;
switch (type) {
case DATANODE:
pid_number = myinfo.dn_pid[i];
break;
case GTM:
pid_number = myinfo.gtm_pid;
break;
case COORD:
pid_number = myinfo.co_pid[i];
break;
}
#ifndef WIN32
if (kill(pid_number, 0) != 0)
#else
if (WaitForSingleObject(pid_number, 0) == WAIT_OBJECT_0)
#endif /* WIN32 */
{
fprintf(stderr,
_("\n%s: postmaster failed for %s%d.\nExamine %s/log/postmaster_%d.log for the reason\n"),
progname,
type == GTM ? "gtm" : (type == COORD ? "Coordinator" : "Datanode"),
i + 1,
outputdir,
pid_number);
exit_nicely(2);
}
}
/*
* Kill given node but do not exit
*/
static void kill_node(int i, int type)
{
PID_TYPE pid_number = INVALID_PID;
switch (type) {
case DATANODE:
pid_number = myinfo.co_pid[i];
break;
case GTM:
pid_number = myinfo.gtm_pid;
break;
case COORD:
pid_number = myinfo.dn_pid[i];
break;
case SHELL:
pid_number = myinfo.shell_pid[i];
break;
}
fprintf(stderr,
_("\n%s: postmaster did not respond within 60 seconds\nExamine %s/log/postmaster_%d.log for the reason\n"),
progname,
outputdir,
pid_number);
#ifndef WIN32
if (kill(pid_number, SIGKILL) != 0 && errno != ESRCH)
fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n"), progname, strerror(errno));
#else
if (TerminateProcess(pid_number, 255) == 0)
fprintf(stderr, _("\n%s: could not kill failed postmaster: %lu\n"), progname, GetLastError());
#endif
}
#endif
/*
* Always exit through here, not through plain exit(), to ensure we make
* an effort to shut down a temp postmaster
*/
void exit_nicely(int code)
{
if (temp_install && clean) {
stop_postmaster();
}
if (temp_install) {
free(temp_install);
temp_install = NULL;
}
exit(code);
}
/*
* Check whether string matches pattern
*
* In the original shell script, this function was implemented using expr(1),
* which provides basic regular expressions restricted to match starting at
* the string start (in conventional regex terms, there's an implicit "^"
* at the start of the pattern --- but no implicit "$" at the end).
*
* For now, we only support "." and ".*" as non-literal metacharacters,
* because that's all that anyone has found use for in resultmap. This
* code could be extended if more functionality is needed.
*/
static bool string_matches_pattern(const char* str, const char* pattern)
{
while (*str && *pattern) {
if (*pattern == '.' && pattern[1] == '*') {
pattern += 2;
/* Trailing .* matches everything. */
if (*pattern == '\0') {
return true;
}
/*
* Otherwise, scan for a text position at which we can match the
* rest of the pattern.
*/
while (*str) {
/*
* Optimization to prevent most recursion: don't recurse
* unless first pattern char might match this text char.
*/
if (*str == *pattern || *pattern == '.') {
if (string_matches_pattern(str, pattern)) {
return true;
}
}
str++;
}
/*
* End of text with no match.
*/
return false;
} else if (*pattern != '.' && *str != *pattern) {
/*
* Not the single-character wildcard and no explicit match? Then
* time to quit...
*/
return false;
}
str++;
pattern++;
}
if (*pattern == '\0') {
return true;
} /* end of pattern, so declare match */
/* End of input string. Do we have matching pattern remaining? */
while (*pattern == '.' && pattern[1] == '*') {
pattern += 2;
}
if (*pattern == '\0') {
return true;
} /* end of pattern, so declare match */
return false;
}
/*
* Replace all occurances of a string in a string with a different string.
* NOTE: Assumes there is enough room in the target buffer!
*/
void replace_string(char* string, char* replace, char* replacement)
{
char* ptr = NULL;
while ((ptr = strstr(string, replace)) != NULL) {
char* cdup = strdup(string);
(void)strlcpy(string, cdup, ptr - string + 1);
strcat(string, replacement);
strcat(string, cdup + (ptr - string) + strlen(replace));
free(cdup);
}
}
/*
* Check for sub directories within the input folders and convert the
* .source files to appropriate .sql files within the sql folder.
*
* Return true if the object is an existing directory otherwise return false
*
*/
static bool CheckForSubdirAndConvertSourceFiles(char* source, char* destination, char* pcSourceSubdir, char* pcDestDir,
char* pcDestSubdir, char* indir, char* object, char* suffix)
{
(void)snprintf(source, MAXPGPATH, "%s/%s", indir, object);
if (!directory_exists(source))
return false;
/* If the directory name is starting with '.' char, then it is meta folder
* like .svn , .gitignore etc. These folders can't be considered for test
* script scanning. So skip them and return false indicating sub directory
* is not found for 'pcSourceSubdir' */
if (*object == '.')
return false;
sprintf(source, "%s/%s", pcSourceSubdir, object);
sprintf(destination, "%s/%s", pcDestSubdir, object);
convertSourcefilesIn(source, pcDestDir, destination, suffix);
return true;
}
bool is_cmdline(const char* src)
{
const char* ptr = src;
while (isspace(*ptr)) {
ptr++;
}
return (strncmp(ptr, "@@", 2) == 0);
}
static void get_cmdline(const char* src, char* dsr)
{
const char* ptr = src;
while (isspace(*ptr)) {
ptr++;
}
if (!is_cmdline(ptr)) {
return;
}
ptr += 2;
strcpy(dsr, ptr);
}
static void exec_cmds_from_inputfiles()
{
char indir[MAXPGPATH];
struct stat st;
int ret;
char** name;
char** names;
int count = 0;
FILE* infile = NULL;
(void)snprintf(indir, MAXPGPATH, "%s/%s", inputdir, "input");
/* Check that indir actually exists and is a directory */
ret = stat(indir, &st);
if (ret != 0 || !S_ISDIR(st.st_mode)) {
/*
* No warning, to avoid noise in tests that do not have these
* directories; for example, ecpg, contrib and src/pl.
*/
return;
}
names = pgfnames(indir);
if (!names)
/* Error logged in pgfnames */
{
exit_nicely(2);
}
/* finally loop on each file and do the replacement */
for (name = names; *name; name++) {
char srcfile[MAXPGPATH + MAXPGPATH + 2];
char prefix[MAXPGPATH];
char line[1024];
/* reject filenames not finishing in ".source" */
if (strlen(*name) < 8) {
continue;
}
if (strcmp(*name + strlen(*name) - 7, ".source") != 0) {
continue;
}
count++;
/* build the full actual paths to open */
(void)snprintf(prefix, strlen(*name) - 6, "%s", *name);
(void)snprintf(srcfile, MAXPGPATH + strlen(*name) + 2, "%s/%s", indir, *name);
infile = fopen(srcfile, "r");
if (!infile) {
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, srcfile, strerror(errno));
exit_nicely(2);
}
while (fgets(line, sizeof(line), infile)) {
if (is_cmdline(line)) {
char* cmdline = (char*)malloc(1024);
get_cmdline(line, cmdline);
replace_string(cmdline, "@login_user@", loginuser);
replace_string(cmdline, "@abs_bindir@", bindir);
replace_string(cmdline, "@abs_srcdir@", inputdir);
replace_string(cmdline, "@abs_builddir@", outputdir);
replace_string(cmdline, "@hdfshostname@", hdfshostname);
replace_string(cmdline, "@hdfsstoreplus@", hdfsstoreplus);
replace_string(cmdline, "@hdfscfgpath@", hdfscfgpath);
replace_string(cmdline, "@hdfsport@", hdfsport);
replace_string(cmdline, "@obshostname@", obshostname);
replace_string(cmdline, "@obsbucket@", obsbucket);
replace_string(cmdline, "@ak@", ak);
replace_string(cmdline, "@sk@", sk);
replace_string(cmdline, "@aie_host@", g_aiehost);
replace_string(cmdline, "@aie_port@", g_aieport);
replace_string(cmdline, "@abs_gausshome@", gausshomedir);
replace_string(cmdline, "@pgbench_dir@", pgbenchdir);
myinfo.shell_pid[myinfo.shell_count++] = spawn_process(cmdline);
free(cmdline);
}
}
fclose(infile);
}
/*
* If we didn't process any files, complain because it probably means
* somebody neglected to pass the needed --inputdir argument.
*/
if (count <= 0) {
fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"), progname, indir);
exit_nicely(2);
}
pgfnames_cleanup(names);
}
// Get start node SQL command
//
static char* GetStartNodeCmdString(int i, int type)
{
int port_number;
const char* data_folder = get_node_info_name(i, type, false);
if (type == COORD) {
port_number = test_single_node ? 0 : myinfo.co_port[i];
} else {
port_number = myinfo.dn_port[i];
}
// PID_TYPE node_pid;
char* buf = (char*)malloc(MAXPGPATH * 4);
MemSet(buf, 0, MAXPGPATH * 4);
(void)snprintf(buf,
MAXPGPATH * 4,
SYSTEMQUOTE "\\! \"%s/gaussdb\" %s -i -p %d -D %s/%s >>startup.log 2>&1 &" SYSTEMQUOTE,
bindir,
(type == COORD) ? "\"--coordinator\"" : "\"--datanode\"",
port_number,
temp_install,
data_folder);
free((char*)data_folder);
return buf;
}
// Get pgxc_clean node SQL command
//
static char* GetPgxcCleanCmdString(int i, int type)
{
int port_number;
if (type == COORD) {
port_number = test_single_node ? 0 : myinfo.co_port[i];
} else {
port_number = myinfo.dn_port[i];
}
// PID_TYPE node_pid;
char* buf = (char*)malloc(MAXPGPATH * 4);
MemSet(buf, 0, MAXPGPATH * 4);
(void)snprintf(buf,
MAXPGPATH * 4,
SYSTEMQUOTE "\\! \"%s/gs_clean\" -s -v -a -p %d -r >>gs_clean_test.log 2>&1 &" SYSTEMQUOTE,
bindir,
port_number);
return buf;
}
/* Get gs_gtm node SQL command */
static char* GetStartGtmCmdString(int i, int type)
{
const char* data_folder = get_node_info_name(i, type, false);
if (*data_folder == '\0')
return NULL;
char* buf = (char*)malloc(MAXPGPATH * 4);
MemSet(buf, 0, MAXPGPATH * 4);
(void)snprintf(buf,
MAXPGPATH * 4,
SYSTEMQUOTE "\\! \"%s/gs_gtm\" -r -n gtm -D %s/%s -p %d" SYSTEMQUOTE,
bindir,
temp_install,
data_folder,
myinfo.gtm_port);
free((char*)data_folder);
return buf;
}
/* Get gtm_ctl SQL command to stop GTM */
static char* GetStopGtmCmdString(int i, int type)
{
const char* data_folder = get_node_info_name(i, type, false);
if (*data_folder == '\0')
return NULL;
char* buf = (char*)malloc(MAXPGPATH * 4);
MemSet(buf, 0, MAXPGPATH * 4);
(void)snprintf(buf,
MAXPGPATH * 4,
SYSTEMQUOTE "\\! \"%s/gtm_ctl\" stop -Z gtm -D %s/%s -m i >>gtm.log 2>&1 &" SYSTEMQUOTE,
bindir,
temp_install,
data_folder);
free((void*)data_folder);
return buf;
}
/*
* Convert *.source found in the "source" directory, replacing certain tokens
* in the file contents with their intended values, and put the resulting files
* in the "dest" directory, replacing the ".source" prefix in their names with
* the given suffix.
*/
static void convertSourcefilesIn(char* pcSourceSubdir, char* pcDestDir, char* pcDestSubdir, char* suffix)
{
char testtablespace[MAXPGPATH];
char testtablespace_my[MAXPGPATH];
char indir[MAXPGPATH];
struct stat st;
int iIter;
int ret;
char* datadirPath = NULL;
char** name;
char** names;
int count = 0;
bool bIisContainingDir = false;
(void)snprintf(indir, MAXPGPATH, "%s/%s", inputdir, pcSourceSubdir);
/* Check that indir actually exists and is a directory */
ret = stat(indir, &st);
if (ret != 0 || !S_ISDIR(st.st_mode)) {
/*
* No warning, to avoid noise in tests that do not have these
* directories; for example, ecpg, contrib and src/pl.
*/
return;
}
names = pgfnames(indir);
if (!names)
/* Error logged in pgfnames */
{
exit_nicely(2);
}
(void)snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
(void)snprintf(testtablespace_my, MAXPGPATH, "%s/gausstablespace", outputdir);
#ifdef WIN32
/*
* On Windows only, clean out the test tablespace dir, or create it if it
* doesn't exist. On other platforms we expect the Makefile to take care
* of that. (We don't migrate that functionality in here because it'd be
* harder to cope with platform-specific issues such as SELinux.)
*
* XXX it would be better if pg_regress.c had nothing at all to do with
* testtablespace, and this were handled by a .BAT file or similar on
* Windows. See pgsql-hackers discussion of 2008-01-18.
*/
if (directory_exists(testtablespace))
rmtree(testtablespace, true);
make_directory(testtablespace);
if (directory_exists(testtablespace_my))
rmtree(testtablespace_my, true);
make_directory(testtablespace_my);
#endif
/* finally loop on each file and do the replacement */
for (name = names; *name; name++) {
char srcfile[MAXPGPATH * 2 + 2];
char destfile[MAXPGPATH * 2 + 4];
char prefix[MAXPGPATH];
FILE *infile, *outfile;
char line[1024];
char bufTemp[MAXPGPATH];
/* reject filenames not finishing in ".source" */
if (strlen(*name) < 8) {
if (true == CheckForSubdirAndConvertSourceFiles(
srcfile, destfile, pcSourceSubdir, pcDestDir, pcDestSubdir, indir, *name, suffix))
bIisContainingDir = true;
continue;
}
if (strcmp(*name + strlen(*name) - 7, ".source") != 0) {
if (true == CheckForSubdirAndConvertSourceFiles(
srcfile, destfile, pcSourceSubdir, pcDestDir, pcDestSubdir, indir, *name, suffix))
bIisContainingDir = true;
continue;
}
count++;
/* build the full actual paths to open */
(void)snprintf(prefix, strlen(*name) - 6, "%s", *name);
(void)snprintf(srcfile, MAXPGPATH + strlen(*name) + 2, "%s/%s", indir, *name);
(void)snprintf(destfile, MAXPGPATH * 2 + 4, "%s/%s/%s.%s", pcDestDir, pcDestSubdir, prefix, suffix);
infile = fopen(srcfile, "r");
if (!infile) {
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, srcfile, strerror(errno));
exit_nicely(2);
}
outfile = fopen(destfile, "w");
if (!outfile) {
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, destfile, strerror(errno));
exit_nicely(2);
}
add_stringlist_item(&destination_files, destfile);
while (fgets(line, sizeof(line), infile)) {
if (!is_cmdline(line)) {
for (iIter = 0; iIter < g_stRegrReplcPatt.iNumOfPatterns; iIter++)
replace_string(line,
&g_stRegrReplcPatt.pcBuf[g_stRegrReplcPatt.puiPatternOffset[iIter]],
&g_stRegrReplcPatt.pcBuf[g_stRegrReplcPatt.puiPattReplValOffset[iIter]]);
replace_string(line, "@login_user@", loginuser);
replace_string(line, "@abs_srcdir@", inputdir);
replace_string(line, "@abs_builddir@", outputdir);
replace_string(line, "@abs_bindir@", psqldir);
replace_string(line, "@testtablespace@", testtablespace);
replace_string(line, "@gausstablespace@", testtablespace_my);
replace_string(line, "@libdir@", dlpath);
replace_string(line, "@DLSUFFIX@", DLSUFFIX);
replace_string(line, "@gsqldir@", psqldir);
replace_string(line, "@datanode1@", get_node_info_name(0, DATANODE, true));
replace_string(line, "@datanode2@", get_node_info_name(1, DATANODE, true));
replace_string(line, "@coordinator1@", get_node_info_name(0, COORD, true));
replace_string(line, "@coordinator2@", get_node_info_name(1, COORD, true));
replace_string(line, "@pgbench_dir@", pgbenchdir);
replace_string(line, "@client_logic_hook@", client_logic_hook);
char* ptr = GetStartNodeCmdString(0, DATANODE);
replace_string(line, "@start_datanode1@", ptr);
free(ptr);
#ifdef ENABLE_MULTIPLE_NODES
ptr = GetStartNodeCmdString(1, DATANODE);
replace_string(line, "@start_datanode2@", ptr);
free(ptr);
ptr = GetStartNodeCmdString(0, COORD);
replace_string(line, "@start_coordinator1@", ptr);
free(ptr);
ptr = GetStartNodeCmdString(1, COORD);
replace_string(line, "@start_coordinator2@", ptr);
free(ptr);
#endif
ptr = GetStopGtmCmdString(0, GTM);
replace_string(line, "@stop_gtm@", ptr);
free(ptr);
ptr = GetStartGtmCmdString(0, GTM);
replace_string(line, "@start_gtm@", ptr);
free(ptr);
ptr = GetPgxcCleanCmdString(0, COORD);
replace_string(line, "@start_pgxc_clean@", ptr);
free(ptr);
char s[16];
sprintf(s, "%d", get_port_number(0, DATANODE));
replace_string(line, "@portstring@", s);
sprintf(s, "%d", get_port_number(1, COORD));
replace_string(line, "@cn2_portstring@", s);
if (temp_install) {
char tmpdatadir[1024];
(void)snprintf(tmpdatadir, MAXPGPATH, "\"%s\"", temp_install);
replace_string(line, "@cndata@", tmpdatadir);
(void)snprintf(bufTemp, MAXPGPATH, "\"%s/data\"", temp_install);
replace_string(line, "@datadir@", bufTemp);
(void)snprintf(bufTemp, MAXPGPATH, "\"%s/data1\"", temp_install);
replace_string(line, "@data1dir@", bufTemp);
(void)snprintf(bufTemp, MAXPGPATH, "-p%u", 0xC000 | (PG_VERSION_NUM & 0x3FFF));
replace_string(line, "@psqlport@", bufTemp);
} else {
datadirPath = getenv("GAUSSDATA");
if (datadirPath) {
(void)snprintf(bufTemp, MAXPGPATH, "\"%s\"", datadirPath);
replace_string(line, "@datadir@", bufTemp);
} else
replace_string(line, "@datadir@", ".");
datadirPath = getenv("GAUSSDATA1");
if (datadirPath) {
(void)snprintf(bufTemp, MAXPGPATH, "\"%s\"", datadirPath);
replace_string(line, "@data1dir@", bufTemp);
} else
replace_string(line, "@data1dir@", ".");
replace_string(line, "@psqlport@", "");
}
replace_string(line, "@hdfshostname@", hdfshostname);
replace_string(line, "@hdfsstoreplus@", hdfsstoreplus);
replace_string(line, "@hdfscfgpath@", hdfscfgpath);
replace_string(line, "@hdfsport@", hdfsport);
replace_string(line, "@obshostname@", obshostname);
replace_string(line, "@obsbucket@", obsbucket);
replace_string(line, "@ak@", ak);
replace_string(line, "@sk@", sk);
replace_string(line, "@aie_host@", g_aiehost);
replace_string(line, "@aie_port@", g_aieport);
replace_string(line, "@abs_gausshome@", gausshomedir);
sprintf(s, "%d", get_port_number(0, DATANODE));
replace_string(line, "@dn1port@", s);
fputs(line, outfile);
}
}
fclose(infile);
fclose(outfile);
}
/*
* If we didn't process any files, complain because it probably means
* somebody neglected to pass the needed --inputdir argument.
*/
if (count <= 0 && !bIisContainingDir) {
fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"), progname, indir);
exit_nicely(2);
}
pgfnames_cleanup(names);
}
/* Create the .sql and .out files from the .source files, if any */
static void convert_sourcefiles(void)
{
errno_t rc = EOK;
convertSourcefilesIn("input", inputdir, "sql", "sql");
convertSourcefilesIn("output", outputdir, "expected", "out");
// Convert source file to SQL file according some rules
//
convertSourcefilesIn(TwoPhaseTxntTestDirInput, inputdir, "sql", "sql");
convertSourcefilesIn(TwoPhaseTxntTestDirOutput, outputdir, "expected", "out");
if (g_stRegrReplcPatt.puiPatternOffset != NULL)
REGR_FREE(g_stRegrReplcPatt.puiPatternOffset);
/*
* backup the sql directory
*/
char buff[MAXPGPATH * 4] = {0};
char src[MAXPGPATH] = {0};
char dest[MAXPGPATH] = {0};
rc = snprintf_s(src, sizeof(src), sizeof(src) - 1, "%s/sql", inputdir);
securec_check_ss_c(rc, "", "");
rc = snprintf_s(dest, sizeof(dest), sizeof(dest) - 1, "%s/.sql", inputdir);
securec_check_ss_c(rc, "", "");
rc = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "cp -r %s %s", src, dest);
securec_check_ss_c(rc, "", "");
system(buff);
}
/*
* Scan resultmap file to find which platform-specific expected files to use.
*
* The format of each line of the file is
* testname/hostplatformpattern=substitutefile
* where the hostplatformpattern is evaluated per the rules of expr(1),
* namely, it is a standard regular expression with an implicit ^ at the start.
* (We currently support only a very limited subset of regular expressions,
* see string_matches_pattern() above.) What hostplatformpattern will be
* matched against is the config.guess output. (In the shell-script version,
* we also provided an indication of whether gcc or another compiler was in
* use, but that facility isn't used anymore.)
*/
static void load_resultmap(void)
{
char buf[MAXPGPATH];
FILE* f = NULL;
/* scan the file ... */
(void)snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
f = fopen(buf, "r");
if (!f) {
/* OK if it doesn't exist, else complain */
if (errno == ENOENT) {
return;
}
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, buf, strerror(errno));
exit_nicely(2);
}
while (fgets(buf, sizeof(buf), f)) {
char* platform = NULL;
char* file_type = NULL;
char* expected = NULL;
int i;
/* strip trailing whitespace, especially the newline */
i = strlen(buf);
while (i > 0 && isspace((unsigned char)buf[i - 1])) {
buf[--i] = '\0';
}
/* parse out the line fields */
file_type = strchr(buf, ':');
if (!file_type) {
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"), buf);
exit_nicely(2);
}
*file_type++ = '\0';
platform = strchr(file_type, ':');
if (!platform) {
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"), buf);
exit_nicely(2);
}
*platform++ = '\0';
expected = strchr(platform, '=');
if (!expected) {
fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"), buf);
exit_nicely(2);
}
*expected++ = '\0';
/*
* if it's for current platform, save it in resultmap list. Note: by
* adding at the front of the list, we ensure that in ambiguous cases,
* the last match in the resultmap file is used. This mimics the
* behavior of the old shell script.
*/
if (string_matches_pattern(host_platform, platform)) {
_resultmap* entry = (_resultmap*)malloc(sizeof(_resultmap));
entry->test = strdup(buf);
entry->type = strdup(file_type);
entry->resultfile = strdup(expected);
entry->next = resultmap;
resultmap = entry;
}
}
fclose(f);
}
/*
* Check in resultmap if we should be looking at a different file
*/
static const char* get_expectfile(const char* testname, const char* file)
{
char* file_type = NULL;
_resultmap* rm = NULL;
/*
* Determine the file type from the file name. This is just what is
* following the last dot in the file name.
*/
if (!file || !(file_type = strrchr((char*)file, '.'))) {
return NULL;
}
file_type++;
for (rm = resultmap; rm != NULL; rm = rm->next) {
if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0) {
return rm->resultfile;
}
}
return NULL;
}
#ifndef PGXC
/*
* Handy subroutine for setting an environment variable "var" to "val"
*/
static void doputenv(const char* var, const char* val)
{
char* s = (char*)malloc(strlen(var) + strlen(val) + 2);
sprintf(s, "%s=%s", var, val);
putenv(s);
}
#endif
/*
* Set the environment variable "pathname", prepending "addval" to its
* old value (if any).
*/
/*lint -e429*/
static void add_to_path(const char* pathname, char separator, const char* addval)
{
char* oldval = getenv(pathname);
char* newval = NULL;
if (!oldval || !oldval[0]) {
/* no previous value */
newval = (char*)malloc(strlen(pathname) + strlen(addval) + 2);
sprintf(newval, "%s=%s", pathname, addval);
} else {
newval = (char*)malloc(strlen(pathname) + strlen(addval) + strlen(oldval) + 3);
sprintf(newval, "%s=%s%c%s", pathname, addval, separator, oldval);
}
(void)putenv(newval);
}
/*lint +e429*/
/*
* Prepare environment variables for running regression tests
*/
static void initialize_environment(void)
{
if (nolocale) {
/*
* Clear out any non-C locale settings
*/
unsetenv("LC_COLLATE");
unsetenv("LC_CTYPE");
unsetenv("LC_MONETARY");
unsetenv("LC_NUMERIC");
unsetenv("LC_TIME");
unsetenv("LANG");
/* On Windows the default locale cannot be English, so force it */
#if defined(WIN32) || defined(__CYGWIN__)
putenv("LANG=en");
#endif
}
/*
* Set translation-related settings to English; otherwise psql will
* produce translated messages and produce diffs. (XXX If we ever support
* translation of pg_regress, this needs to be moved elsewhere, where psql
* is actually called.)
*/
unsetenv("LANGUAGE");
unsetenv("LC_ALL");
(void)putenv("LC_MESSAGES=C");
/* test for a bug of psqlrc contain ~ */
(void)putenv("PSQLRC='~/x'");
/*
* Set encoding as requested
*/
if (encoding) {
doputenv("PGCLIENTENCODING", encoding);
} else {
unsetenv("PGCLIENTENCODING");
}
/*
* Set timezone and datestyle for datetime-related tests
*/
(void)putenv("PGTZ=PST8PDT");
(void)putenv("PGDATESTYLE=Postgres, MDY");
/*
* Likewise set intervalstyle to ensure consistent results. This is a bit
* more painful because we must use PGOPTIONS, and we want to preserve the
* user's ability to set other variables through that.
*/
/*lint -e429*/
{
const char* my_pgoptions = "-c intervalstyle=postgres_verbose";
const char* old_pgoptions = getenv("PGOPTIONS");
char* new_pgoptions = NULL;
if (!old_pgoptions) {
old_pgoptions = "";
}
new_pgoptions = (char*)malloc(strlen(old_pgoptions) + strlen(my_pgoptions) + 12);
(void)sprintf(new_pgoptions, "PGOPTIONS=%s %s", old_pgoptions, my_pgoptions);
(void)putenv(new_pgoptions);
}
/*lint +e429*/
if (temp_install) {
/*
* Clear out any environment vars that might cause psql to connect to
* the wrong postmaster, or otherwise behave in nondefault ways. (Note
* we also use psql's -X switch consistently, so that ~/.psqlrc files
* won't mess things up.) Also, set PGPORT to the temp port, and set
* or unset PGHOST depending on whether we are using TCP or Unix
* sockets.
*/
unsetenv("PGDATABASE");
unsetenv("PGUSER");
unsetenv("PGSERVICE");
unsetenv("PGSSLMODE");
unsetenv("PGREQUIRESSL");
unsetenv("PGCONNECT_TIMEOUT");
unsetenv("PGDATA");
if (hostname != NULL) {
doputenv("PGHOST", hostname);
} else {
unsetenv("PGHOST");
}
unsetenv("PGHOSTADDR");
if (port != -1) {
char s[16];
sprintf(s, "%d", port);
doputenv("PGPORT", s);
}
setBinAndLibPath(false);
} else {
const char* pghost = NULL;
const char* pgport = NULL;
/*
* When testing an existing install, we honor existing environment
* variables, except if they're overridden by command line options.
*/
if (hostname != NULL) {
doputenv("PGHOST", hostname);
unsetenv("PGHOSTADDR");
}
if (port != -1) {
char s[16];
sprintf(s, "%d", port);
doputenv("PGPORT", s);
}
if (user != NULL) {
doputenv("PGUSER", user);
}
/*
* Report what we're connecting to
*/
pghost = getenv("PGHOST");
pgport = getenv("PGPORT");
#ifndef HAVE_UNIX_SOCKETS
if (!pghost) {
pghost = "localhost";
}
#endif
if (pghost && pgport) {
printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport);
}
if (pghost && !pgport) {
printf(_("(using postmaster on %s, default port)\n"), pghost);
}
if (!pghost && pgport) {
printf(_("(using postmaster on Unix socket, port %s)\n"), pgport);
}
if (!pghost && !pgport) {
printf(_("(using postmaster on Unix socket, default port)\n"));
}
}
convert_sourcefiles();
load_resultmap();
}
static void cleanup_environment()
{
if (is_skip_environment_cleanup) {
return;
}
for (; destination_files != NULL; destination_files = destination_files->next) {
unlink(destination_files->str);
}
for (; destination_files != NULL; destination_files = destination_files->next) {
unlink(destination_files->str);
}
}
#define MAKEFILE_BUF_MORE_LEN 32
static void setBinAndLibPath(bool isOld)
{
char* tmp = NULL;
// set to old binary
if (isOld) {
/*
* Adjust path variables to point into the temp-install tree
*/
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen("/bin"));
sprintf(tmp, "%s/%s", temp_install, "bin");
bindir = tmp;
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen("/lib"));
sprintf(tmp, "%s/%s", temp_install, "lib");
libdir = tmp;
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen("/share/postgresql"));
sprintf(tmp, "%s/%s", temp_install, "share/postgresql");
datadir = tmp;
/* psql will be installed into temp-install bindir */
psqldir = bindir;
/*
* Set up shared library paths to include the temp install.
*
* LD_LIBRARY_PATH covers many platforms. DYLD_LIBRARY_PATH works on
* Darwin, and maybe other Mach-based systems. LIBPATH is for AIX.
* Windows needs shared libraries in PATH (only those linked into
* executables, not dlopen'ed ones). Feel free to account for others
* as well.
*/
add_to_path("LD_LIBRARY_PATH", ':', libdir);
add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
add_to_path("LIBPATH", ':', libdir);
#if defined(WIN32)
add_to_path("PATH", ';', libdir);
#elif defined(__CYGWIN__)
add_to_path("PATH", ':', libdir);
#endif
} else { // set to new binary
/*
* Adjust path variables to point into the temp-install tree
*/
#ifdef BUILD_BY_CMAKE
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen(CMAKE_PGBINDIR));
sprintf(tmp, "%s/install/%s/bin", temp_install, CMAKE_PGBINDIR);
bindir = tmp;
printf("bindir: %s\n", bindir);
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen(CMAKE_LIBDIR));
sprintf(tmp, "%s/install/%s/lib", temp_install, CMAKE_LIBDIR);
libdir = tmp;
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen(CMAKE_PGSHAREDIR));
sprintf(tmp, "%s/install/%s/share/postgresql", temp_install, CMAKE_PGSHAREDIR);
datadir = tmp;
psqldir = bindir;
#else
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen(PGBINDIR));
sprintf(tmp, "%s/install/%s", temp_install, PGBINDIR);
bindir = tmp;
printf("bindir: %s\n", bindir);
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen(LIBDIR));
sprintf(tmp, "%s/install/%s", temp_install, LIBDIR);
libdir = tmp;
tmp = (char*)malloc(strlen(temp_install) + MAKEFILE_BUF_MORE_LEN + strlen(PGSHAREDIR));
sprintf(tmp, "%s/install/%s", temp_install, PGSHAREDIR);
datadir = tmp;
psqldir = bindir;
#endif
add_to_path("LD_LIBRARY_PATH", ':', libdir);
add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
add_to_path("LIBPATH", ':', libdir);
#if defined(WIN32)
add_to_path("PATH", ';', libdir);
#elif defined(__CYGWIN__)
add_to_path("PATH", ':', libdir);
#endif
}
}
/*
* Spawn a process to execute the given shell command; don't wait for it
*
* Returns the process ID (or HANDLE) so we can wait for it later
*/
PID_TYPE
spawn_process(const char* cmdline)
{
#ifndef WIN32
pid_t pid;
/*
* Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here
* ... does anyone still care about systems where that doesn't work?
*/
(void)fflush(stdout);
(void)fflush(stderr);
if (logfile) {
(void)fflush(logfile);
}
pid = fork();
if (pid == -1) {
fprintf(stderr, _("%s: could not fork: %s\n"), progname, strerror(errno));
exit_nicely(2);
}
/*lint -e429*/
if (pid == 0) {
/*
* In child
*
* Instead of using system(), exec the shell directly, and tell it to
* "exec" the command too. This saves two useless processes per
* parallel test case.
*/
char* cmdline2 = (char*)malloc(strlen(cmdline) + 6);
sprintf(cmdline2, "exec %s", cmdline);
(void)execl(shellprog, shellprog, "-c", cmdline2, (char*)NULL);
fprintf(stderr, _("%s: could not exec \"%s\": %s\n"), progname, shellprog, strerror(errno));
exit(1); /* not exit here... */
}
/*lint +e429*/
/* in parent */
return pid;
#else
char* cmdline2 = NULL;
BOOL b;
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE origToken;
HANDLE restrictedToken;
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
SID_AND_ATTRIBUTES dropSids[2];
__CreateRestrictedToken _CreateRestrictedToken = NULL;
HANDLE Advapi32Handle;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
if (Advapi32Handle != NULL) {
_CreateRestrictedToken = (__CreateRestrictedToken)GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
}
if (_CreateRestrictedToken == NULL) {
if (Advapi32Handle != NULL) {
FreeLibrary(Advapi32Handle);
}
fprintf(stderr, _("%s: cannot create restricted tokens on this platform\n"), progname);
exit_nicely(2);
}
/* Open the current token to use as base for the restricted one */
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken)) {
fprintf(stderr, _("could not open process token: error code %lu\n"), GetLastError());
exit_nicely(2);
}
/* Allocate list of SIDs to remove */
ZeroMemory(&dropSids, sizeof(dropSids));
if (!AllocateAndInitializeSid(&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&dropSids[0].Sid) ||
!AllocateAndInitializeSid(&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_POWER_USERS,
0,
0,
0,
0,
0,
0,
&dropSids[1].Sid)) {
fprintf(stderr, _("could not allocate SIDs: %lu\n"), GetLastError());
exit_nicely(2);
}
b = _CreateRestrictedToken(origToken,
DISABLE_MAX_PRIVILEGE,
sizeof(dropSids) / sizeof(dropSids[0]),
dropSids,
0,
NULL,
0,
NULL,
&restrictedToken);
FreeSid(dropSids[1].Sid);
FreeSid(dropSids[0].Sid);
CloseHandle(origToken);
FreeLibrary(Advapi32Handle);
if (!b) {
fprintf(stderr, _("could not create restricted token: error code %lu\n"), GetLastError());
exit_nicely(2);
}
cmdline2 = malloc(strlen(cmdline) + 8);
sprintf(cmdline2, "cmd /c %s", cmdline);
#ifndef __CYGWIN__
AddUserToTokenDacl(restrictedToken);
#endif
if (!CreateProcessAsUser(
restrictedToken, NULL, cmdline2, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
fprintf(stderr, _("could not start process for \"%s\": %lu\n"), cmdline2, GetLastError());
exit_nicely(2);
}
free(cmdline2);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
return pi.hProcess;
#endif
}
/* start a script execution process for specified file and return process ID */
static PID_TYPE scriptExecute(
const char* scriptname, _stringlist** resultfiles, _stringlist** expectfiles, _stringlist** tags)
{
PID_TYPE pid;
char infile[MAXPGPATH];
char outfile[MAXPGPATH];
char expectfile[MAXPGPATH];
char scriptCmd[MAXPGPATH * 3];
size_t offset = 0;
/*
* Look for files in the output dir first, consistent with a vpath search.
* This is mainly to create more reasonable error messages if the file is
* not found. It also allows local test overrides when running pg_regress
* outside of the source tree.
*/
(void)snprintf(infile, sizeof(infile), "%s/scripts/%s", outputdir, scriptname);
if (!file_exists(infile))
(void)snprintf(infile, sizeof(infile), "%s/scripts/%s", inputdir, scriptname);
(void)snprintf(outfile, sizeof(outfile), "%s/results/%s.out", outputdir, scriptname);
(void)snprintf(expectfile, sizeof(expectfile), "%s/expected/%s.out", outputdir, scriptname);
if (!file_exists(expectfile))
(void)snprintf(expectfile, sizeof(expectfile), "%s/expected/%s.out", inputdir, scriptname);
add_stringlist_item(resultfiles, outfile);
add_stringlist_item(expectfiles, expectfile);
if (launcher)
offset += snprintf(scriptCmd + offset, sizeof(scriptCmd) - offset, "%s ", launcher);
(void)snprintf(scriptCmd + offset,
sizeof(scriptCmd) - offset,
SYSTEMQUOTE "\"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
infile,
outfile);
pid = spawn_process(scriptCmd);
if (pid == INVALID_PID) {
fprintf(stderr, _("could not start process for test %s\n"), scriptname);
exit(2);
}
return pid;
}
/*
* Count bytes in file
*/
static long file_size(const char* file)
{
long r;
FILE* f = fopen(file, "r");
if (!f) {
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, file, strerror(errno));
return -1;
}
fseek(f, 0, SEEK_END);
r = ftell(f);
fclose(f);
return r;
}
/*
* Count lines in file
*/
static int file_line_count(const char* file)
{
int c;
int l = 0;
FILE* f = fopen(file, "r");
if (!f) {
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, file, strerror(errno));
return -1;
}
while ((c = fgetc(f)) != EOF) {
if (c == '\n') {
l++;
}
}
fclose(f);
return l;
}
bool file_exists(const char* file)
{
FILE* f = fopen(file, "r");
if (!f) {
return false;
}
fclose(f);
return true;
}
static bool directory_exists(const char* dir)
{
struct stat st;
if (stat(dir, &st) != 0)
return false;
if (S_ISDIR(st.st_mode))
return true;
return false;
}
/* Create nested directory structure */
static void makeNestedDirectory(const char* results_file_path)
{
char* pcOutFileLastSep = NULL;
char outfile[MAXPGPATH];
int iSepCount = 0;
char cSeparator;
(void)snprintf(outfile, sizeof(outfile), "%s/results/%s.out", outputdir, results_file_path);
do {
pcOutFileLastSep = strrchr(outfile, '/');
if (NULL == pcOutFileLastSep)
pcOutFileLastSep = strrchr(outfile, '\\');
if (NULL != pcOutFileLastSep) {
cSeparator = *pcOutFileLastSep;
*pcOutFileLastSep = '\0';
iSepCount++;
if (directory_exists(outfile)) {
do {
*pcOutFileLastSep = cSeparator;
iSepCount--;
if ((iSepCount > 0) && (!directory_exists(outfile))) {
make_directory(outfile);
pcOutFileLastSep = outfile + strlen(outfile);
}
} while (iSepCount > 0);
}
}
} while (iSepCount > 0);
}
/* Create a directory */
static void make_directory(const char* dir)
{
if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"), progname, dir, strerror(errno));
exit_nicely(2);
}
}
/*
* In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
*/
static char* get_alternative_expectfile(const char* expectfile, int i)
{
char* last_dot = NULL;
int ssize = strlen(expectfile) + 2 + 1;
char* tmp = (char*)malloc(ssize);
char* s = (char*)malloc(ssize);
strcpy(tmp, expectfile);
last_dot = strrchr(tmp, '.');
if (!last_dot) {
free(tmp);
free(s);
return NULL;
}
*last_dot = '\0';
(void)snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
free(tmp);
return s;
}
/*
* Run a "diff" command and also check that it didn't crash
*/
static int run_diff(const char* cmd, const char* filename)
{
int r;
r = system(cmd);
if (!WIFEXITED(r) || WEXITSTATUS(r) > 1) {
fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
exit_nicely(2);
}
#ifdef WIN32
/*
* On WIN32, if the 'diff' command cannot be found, system() returns 1,
* but produces nothing to stdout, so we check for that here.
*/
if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0) {
fprintf(stderr, _("diff command not found: %s\n"), cmd);
exit_nicely(2);
}
#endif
return WEXITSTATUS(r);
}
/*
* Check the actual result file for the given test against expected results
*
* Returns true if different (failure), false if correct match found.
* In the true case, the diff is appended to the diffs file.
*/
static bool results_differ(const char* testname, const char* resultsfile, const char* default_expectfile)
{
char expectfile[MAXPGPATH];
char smartmatchfile[MAXPGPATH + 12];
char diff[MAXPGPATH];
char smartmatch[MAXPGPATH];
char cmd[MAXPGPATH * 3];
char acBestExpectFile[MAXPGPATH];
FILE* difffile = NULL;
int best_line_count;
int i;
int l;
const char* platform_expectfile = NULL;
int result;
char* pcPrettyDiffFile = NULL;
/*
* We can pass either the resultsfile or the expectfile, they should have
* the same type (filename.type) anyway.
*/
platform_expectfile = get_expectfile(testname, resultsfile);
strlcpy(expectfile, default_expectfile, sizeof(expectfile));
if (platform_expectfile) {
/*
* Replace everything afer the last slash in expectfile with what the
* platform_expectfile contains.
*/
char* p = strrchr(expectfile, '/');
if (p) {
strlcpy(++p, platform_expectfile, MAXPGPATH);
}
}
/* Name to use for temporary diff file */
(void)snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
/* OK, run the diff */
(void)snprintf(cmd,
sizeof(cmd),
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
basic_diff_opts,
expectfile,
resultsfile,
diff);
/* Is the diff file empty? */
if (run_diff(cmd, diff) == 0) {
unlink(diff);
return false;
}
/* There may be secondary comparison files that match better */
best_line_count = file_line_count(diff);
strlcpy(acBestExpectFile, expectfile, sizeof(acBestExpectFile));
/* framing smartmatch.pl script complete file name */
int rc = snprintf_s(smartmatch, sizeof(smartmatch), sizeof(smartmatch) - 1, "%s/%s", outputdir, "smartmatch.pl");
securec_check_ss_c(rc, "\0", "\0");
if (!file_exists(smartmatch)) {
int rc = snprintf_s(smartmatch, sizeof(smartmatch), sizeof(smartmatch) - 1, "%s/%s", inputdir, "smartmatch.pl");
securec_check_ss_c(rc, "\0", "\0");
}
for (i = 0; i <= 9; i++) {
char* alt_expectfile = NULL;
alt_expectfile = get_alternative_expectfile(expectfile, i);
if (!file_exists(alt_expectfile)) {
free(alt_expectfile);
continue;
}
(void)snprintf(cmd,
sizeof(cmd),
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
basic_diff_opts,
alt_expectfile,
resultsfile,
diff);
if (run_diff(cmd, diff) == 0) {
unlink(diff);
free(alt_expectfile);
return false;
}
l = file_line_count(diff);
if (l < best_line_count) {
/* This diff was a better match than the last one */
best_line_count = l;
strlcpy(acBestExpectFile, alt_expectfile, sizeof(acBestExpectFile));
}
if (file_exists(smartmatch)) {
/* Name to use for temporary diff file */
rc = snprintf_s(smartmatchfile, sizeof(smartmatchfile), sizeof(smartmatchfile) - 1, "%s.smartmatch",
acBestExpectFile);
securec_check_ss_c(rc, "\0", "\0");
/*
* Is the unordered result set is leading to test failure? check and
* verify the unordered result set as if test specifies that the result
* can vary with comments of unordered.
*/
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, SYSTEMQUOTE "%s \"%s\" \"%s\" \"%s\" " SYSTEMQUOTE,
smartmatch, alt_expectfile, resultsfile, smartmatchfile);
securec_check_ss_c(rc, "\0", "\0");
result = system(cmd);
if (WEXITSTATUS(result) == 1) {
/* In a success of smartmatch, there is no need of smartmatch file */
unlink(smartmatchfile);
unlink(diff);
free(alt_expectfile);
return false;
} else if (WEXITSTATUS(result) == 2) {
unlink(smartmatchfile);
pcPrettyDiffFile = acBestExpectFile;
} else
pcPrettyDiffFile = smartmatchfile;
} else
pcPrettyDiffFile = acBestExpectFile;
/*
* Use the best comparison file to generate the "pretty" diff, which we
* append to the diffs summary file.
*/
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1,
SYSTEMQUOTE "diff %s \"%s\" \"%s\" >> \"%s\" 2>&1" SYSTEMQUOTE, pretty_diff_opts, pcPrettyDiffFile,
resultsfile, difffilename);
securec_check_ss_c(rc, "\0", "\0");
(void)run_diff(cmd, difffilename);
l = file_line_count(diff);
if (l < best_line_count) {
/* This diff was a better match than the last one */
best_line_count = l;
strlcpy(acBestExpectFile, pcPrettyDiffFile, sizeof(acBestExpectFile));
}
free(alt_expectfile);
}
/*
* fall back on the canonical results file if we haven't tried it yet and
* haven't found a complete match yet.
*/
if (platform_expectfile) {
(void)snprintf(cmd,
sizeof(cmd),
SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
basic_diff_opts,
default_expectfile,
resultsfile,
diff);
if (run_diff(cmd, diff) == 0) {
/* No diff = no changes = good */
unlink(diff);
return false;
}
l = file_line_count(diff);
if (l < best_line_count) {
/* This diff was a better match than the last one */
best_line_count = l;
strlcpy(acBestExpectFile, default_expectfile, sizeof(acBestExpectFile));
}
}
if (file_exists(smartmatch)) {
/* Name to use for temporary diff file */
(void)snprintf(smartmatchfile, sizeof(smartmatchfile), "%s.smartmatch", acBestExpectFile);
/*
* Is the unordered result set is leading to test failure? check and
* verify the unordered result set as if test specifies that the result
* can vary with comments of unordered.
*/
(void)snprintf(cmd,
sizeof(cmd),
SYSTEMQUOTE "%s \"%s\" \"%s\" \"%s\" " SYSTEMQUOTE,
smartmatch,
default_expectfile,
resultsfile,
smartmatchfile);
result = system(cmd);
if (WEXITSTATUS(result) == 1) {
/* In a success of smartmatch, there is no need of smartmatch file */
unlink(smartmatchfile);
unlink(diff);
return false;
} else if (WEXITSTATUS(result) == 2) {
unlink(smartmatchfile);
pcPrettyDiffFile = acBestExpectFile;
} else
pcPrettyDiffFile = smartmatchfile;
} else
pcPrettyDiffFile = acBestExpectFile;
/*
* Use the best comparison file to generate the "pretty" diff, which we
* append to the diffs summary file.
*/
(void)snprintf(cmd,
sizeof(cmd),
SYSTEMQUOTE "diff %s \"%s\" \"%s\" >> \"%s\" 2>&1" SYSTEMQUOTE,
pretty_diff_opts,
pcPrettyDiffFile,
resultsfile,
difffilename);
(void)run_diff(cmd, difffilename);
/* And append a separator */
difffile = fopen(difffilename, "a");
if (difffile) {
fprintf(difffile, "\n======================================================================\n\n");
fclose(difffile);
}
unlink(diff);
return true;
}
/*
* Wait for specified subprocesses to finish, and return their exit
* statuses into statuses[]
*
* If names isn't NULL, print each subprocess's name as it finishes
*
* Note: it's OK to scribble on the pids array, but not on the names array
*/
static void wait_for_tests(PID_TYPE* pids, int* statuses, char** names, int num_tests)
{
int tests_left;
int i;
#ifdef WIN32
PID_TYPE* active_pids = malloc(num_tests * sizeof(PID_TYPE));
memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
#endif
if (g_bEnablePerfDataPrint) {
memset(&g_stResourceUsageDetails, 0, sizeof(REGR_AVG_RSRCE_USAGE_STRU));
}
tests_left = num_tests;
while (tests_left > 0) {
PID_TYPE p;
#ifndef WIN32
int exit_status;
unsigned int uiPidChanged;
if (false == g_bEnablePerfDataPrint) {
p = wait(&exit_status);
if (p == INVALID_PID) {
fprintf(stderr, _("failed to wait for subprocesses: %s\n"), gs_strerror(errno));
exit(2);
}
} else {
if (REGR_SUCCESS != regrGetRsrcUsageWithRepeatOnFail(&g_stPrevCpuUsage, &uiPidChanged)) {
fprintf(stderr, _("Could not get the CPU Usage info! \n"));
g_stPrevCpuUsage.ulCpuTotalTime = 0;
}
do {
p = waitpid(REGR_PID_WAIT_FOR_ANY_CHILD, &exit_status, WNOHANG);
if (p == INVALID_PID) {
fprintf(stderr, _("failed to wait for subprocesses: %s\n"), gs_strerror(errno));
exit(2);
}
if (NO_CHILD_PROC_HAS_CHANGED_STATE != p) {
/* Getting subsequent CpuMemUsageReading to get usage in %*/
regrGetSubsequentUsage();
break;
}
pg_usleep(REGR_DELAY_FOR_PERF_COLLECTION);
/* Getting the subsequent CPU / Memory usage details */
regrGetSubsequentUsage();
} while (true);
}
#else
DWORD exit_status;
int r;
r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left) {
fprintf(stderr, _("failed to wait for subprocesses: %lu\n"), GetLastError());
exit_nicely(2);
}
p = active_pids[r - WAIT_OBJECT_0];
/* compact the active_pids array */
active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
#endif /* WIN32 */
for (i = 0; i < num_tests; i++) {
if (p == pids[i]) {
#ifdef WIN32
GetExitCodeProcess(pids[i], &exit_status);
CloseHandle(pids[i]);
#endif
pids[i] = INVALID_PID;
statuses[i] = (int)exit_status;
if (names) {
status(" %s", names[i]);
}
tests_left--;
REGR_STOP_TIMER_TEMP(i);
break;
}
}
}
if ((g_bEnablePerfDataPrint) && (0 != g_stResourceUsageDetails.ulCount)) {
/* Average System CPU Usage */
g_stResourceUsageDetails.dSAvgcpuUsage /= g_stResourceUsageDetails.ulCount;
/* Average User CPU usage */
g_stResourceUsageDetails.dUAvgcpuUsage /= g_stResourceUsageDetails.ulCount;
/* Average Memory Usage in MBs and Converting the MB value into Bytes */
g_stResourceUsageDetails.dAvgMemUsage =
(g_stResourceUsageDetails.dAvgMemUsage / g_stResourceUsageDetails.ulCount) * REGR_MCR_SIZE_1MB;
regrConvertSizeInBytesToReadableForm((unsigned long)g_stResourceUsageDetails.dAvgMemUsage,
(char*)g_stResourceUsageDetails.acMemUsage,
(unsigned int)MAX_REGR_TOKEN_LEN);
/* Average Memory Usage in percentage */
g_stResourceUsageDetails.dAvgMemUsagePct /= g_stResourceUsageDetails.ulCount;
}
#ifdef WIN32
free(active_pids);
#endif
}
/*
* report nonzero exit code from a test process
*/
static void log_child_failure(int exitstatus)
{
if (WIFEXITED(exitstatus))
status(_(" (test process exited with exit code %d)"), WEXITSTATUS(exitstatus));
else if (WIFSIGNALED(exitstatus)) {
#if defined(WIN32)
status(_(" (test process was terminated by exception 0x%X)"), WTERMSIG(exitstatus));
#elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
status(_(" (test process was terminated by signal %d: %s)"),
WTERMSIG(exitstatus),
WTERMSIG(exitstatus) < NSIG ? sys_siglist[WTERMSIG(exitstatus)] : "(unknown))");
#else
status(_(" (test process was terminated by signal %d)"), WTERMSIG(exitstatus));
#endif
} else
status(_(" (test process exited with unrecognized status %d)"), exitstatus);
}
/* Allocates a block of memory which is required to execute run_schedule().
*
* When the function is called for the first time, i.e. iOldSize is 0 and
* *pppcMemBlockStart is NULL:
* 1. A fresh block of memory is allocated and the pointer for holding the
* 'start of the memory block' is updated. (pppcMemBlockStart is the address
* of the pointer holding memory block)
* 2. Various other pointers, in the caller function (like resultfiles
* expectfiles etc), which are supposed to point to the different parts of
* this memory block, are updated.
*
* When the initial block of memory is exhausted, then this function is called
* subsequently to:
* 1. Backup the old memory block address
* 2. Get a fresh block of memory based on the value: iReqSize
* 3. Various other pointers, in the caller function (like resultfiles
* expectfiles etc), which are pointing to the different parts of the old
* memory block, are updated to point to the appropriate area in the new
* memory block
* 4. Copy the contents of the old block (based on the iOldSize) into the
* appropriate area of the new block.
*
* Returns 0 in case of success and REGR_ERRCODE_MALLOC_FAILURE on memory
* allocation failure
*/
static int regrMallocForTest(int iReqSize, int iOldSize, char*** pppcMemBlockStart, _stringlist*** pppstResultfiles,
_stringlist*** pppstExpectfiles, _stringlist*** pppstTags, PID_TYPE** ppPids, int** ppiStatuses)
{
char** ppcOldMemBlockStart = NULL;
char* pcTmp = NULL;
/* When the function is called for the subsequent times, *pppcMemBlockStart
* will be pointing to the old memory block. This address need to be
* backuped, so that the contents can be copied into the newly allocating
* block */
if (*pppcMemBlockStart != NULL)
ppcOldMemBlockStart = *pppcMemBlockStart;
/* Allocating memory for test file path storage, result file path storage,
* expected file path storage, tags (not used as of now) storage, PID TYPE
* storage and the status storage for the scheduler test execution
*
* Memory Layout:
* +-----------+-----------+-----------+-----------+-----------+----------+
* | Test | Result | Expected | Tags | PID | Statuses |
* | file | file | file | | TYPE | |
* | path | path | path | storage | storage | storage |
* | storage | storage | storage | | | |
* +-----------+-----------+-----------+-----------+-----------+----------+
*/
*pppcMemBlockStart = (char**)malloc((sizeof(char*) + sizeof(_stringlist*) + sizeof(_stringlist*) +
sizeof(_stringlist*) + sizeof(PID_TYPE) + sizeof(int)) *
iReqSize);
if (*pppcMemBlockStart == NULL) {
fprintf(stderr,
_("Could not allocate memory for schedule test "
"execution. Requested size: %d.\n"),
(int)((sizeof(char*) + sizeof(_stringlist*) + sizeof(_stringlist*) + sizeof(_stringlist*) +
sizeof(PID_TYPE) + sizeof(int)) *
iReqSize));
*pppcMemBlockStart = ppcOldMemBlockStart;
return REGR_ERRCODE_MALLOC_FAILURE;
}
/* Updating the output parameter to point to the result file storage area
* in the allocated memory block */
*pppstResultfiles = (_stringlist**)(void*)((char*)(void*)(*pppcMemBlockStart) + (sizeof(char*) * iReqSize));
/* Updating the output parameter to point to the expected file storage area
* in the allocated memory block */
*pppstExpectfiles = (_stringlist**)(void*)((char*)(void*)(*pppstResultfiles) + (sizeof(_stringlist*) * iReqSize));
/* Updating the output parameter to point to the Tags storage area
* in the allocated memory block */
*pppstTags = (_stringlist**)(void*)((char*)(void*)(*pppstExpectfiles) + (sizeof(_stringlist*) * iReqSize));
/* Updating the output parameter to point to the PID TYPE storage area
* in the allocated memory block */
*ppPids = (PID_TYPE*)(void*)((char*)(void*)(*pppstTags) + (sizeof(_stringlist*) * iReqSize));
/* Updating the output parameter to point to the STATUS storage area
* in the allocated memory block */
*ppiStatuses = (int*)(void*)((char*)(void*)(*ppPids) + (sizeof(PID_TYPE) * iReqSize));
/* Resetting the Result file, expected file and tags sections of the
* allocated memory block */
memset(*pppstResultfiles, 0, sizeof(_stringlist*) * iReqSize);
memset(*pppstExpectfiles, 0, sizeof(_stringlist*) * iReqSize);
memset(*pppstTags, 0, sizeof(_stringlist*) * iReqSize);
/* Case of Reallocating the memory. So copy the data from the old memory
* into the freshly allocated block
*/
if ((iOldSize != 0) && (ppcOldMemBlockStart != NULL)) {
/* Copying test file path pointers */
pcTmp = (char*)(void*)ppcOldMemBlockStart;
memcpy(*pppcMemBlockStart, pcTmp, (sizeof(char*) * iOldSize));
/* Copying result file path pointers */
pcTmp = pcTmp + (sizeof(char*) * iOldSize);
memcpy(*pppstResultfiles, pcTmp, (sizeof(_stringlist*) * iOldSize));
/* Copying expected file path pointers */
pcTmp = pcTmp + (sizeof(_stringlist*) * iOldSize);
memcpy(*pppstExpectfiles, pcTmp, (sizeof(_stringlist*) * iOldSize));
/* Copying tags pointers */
pcTmp = pcTmp + (sizeof(_stringlist*) * iOldSize);
memcpy(*pppstTags, pcTmp, (sizeof(_stringlist*) * iOldSize));
/* Copying PID TYPES */
pcTmp = pcTmp + (sizeof(_stringlist*) * iOldSize);
memcpy(*ppPids, pcTmp, (sizeof(PID_TYPE) * iOldSize));
/* Copying statuses */
pcTmp = pcTmp + (sizeof(PID_TYPE) * iOldSize);
memcpy(*ppiStatuses, pcTmp, (sizeof(int) * iOldSize));
/* Freeing the old memory block */
free(ppcOldMemBlockStart);
}
return REGR_SUCCESS;
}
/*
* Get the time difference between the start and end time of the test execution.
* Also updates the total time taken by the entire test suite.
*/
static void regrGetElapsedTime(void)
{
g_dTotalTime = (g_stRegrStopTime.tv_sec - g_stRegrStartTime.tv_sec) +
(g_stRegrStopTime.tv_usec - g_stRegrStartTime.tv_usec) * 0.000001;
g_dGroupTotalTime = g_dGroupTotalTime + g_dTotalTime;
}
/*
* Parse the line buffer read from the schedule file and populate the test
* details
*/
static int regrParseLineBuffer(char* pcLineBuf, char** ppcLastSpace, int* piNumTests, int* piMaxParallelTests,
char*** pppctests, _stringlist*** pppstResultfiles, _stringlist*** pppstExpectfiles, _stringlist*** pppstTags,
PID_TYPE** ppPids, int** ppiStatuses)
{
int iRet = REGR_SUCCESS;
char* pcTemp = NULL;
bool bInsideWord = false;
*ppcLastSpace = NULL;
for (pcTemp = pcLineBuf; *pcTemp; pcTemp++) {
if (isspace((unsigned char)*pcTemp)) {
*pcTemp = '\0';
bInsideWord = false;
*ppcLastSpace = pcTemp;
} else if (!bInsideWord) {
if (*piNumTests >= *piMaxParallelTests) {
/* Pre-allocated memory got exhausted. So extend it by
* reallocating and copying data from old block to the new
* block. Also make sure that the pointer are pointing to
* the appropriate positions in the new block */
iRet = regrMallocForTest(*piMaxParallelTests + REGR_MAX_PARALLEL_TESTS,
*piMaxParallelTests,
pppctests,
pppstResultfiles,
pppstExpectfiles,
pppstTags,
ppPids,
ppiStatuses);
if (iRet != REGR_SUCCESS) {
return iRet;
}
/* Memory has been extented for an additional
* REGR_MAX_PARALLEL_TESTS */
*piMaxParallelTests += REGR_MAX_PARALLEL_TESTS;
}
(*pppctests)[*piNumTests] = pcTemp;
(*piNumTests)++;
bInsideWord = true;
}
}
return REGR_SUCCESS;
}
/*
* Reload line buffer and pasrse the line read from the schedule file
*/
static int regrReloadAndParseLineBuffer(bool* pbBuffReloadReq, bool* pbHalfReadTest, char** ppcLastSpace, FILE* scf,
int* piNumTests, int* piMaxParallelTests, char*** pppctests, _stringlist*** pppstResultfiles,
_stringlist*** pppstExpectfiles, _stringlist*** pppstTags, PID_TYPE** ppPids, int** ppiStatuses)
{
int iRet = REGR_SUCCESS;
int j = 0;
char* pcLineBuf = NULL;
char* pcTemp = NULL;
g_uiCurLineBufIdx++;
if (g_uiCurLineBufIdx == REGR_MAX_NUM_OF_LINE_BUFF) {
fprintf(stderr,
_("\n Length of the line in schedule file is too long. "
"Maximum supported length is %d.\n"),
(REGR_MAX_NUM_OF_LINE_BUFF * REGR_MAX_SCH_LINE_SIZE));
return REGR_ERRCODE_MAX_NUM_OF_LINE_BUFF;
}
/* g_uiCurLineBufIdx is the index to the buffer and g_uiTotalBuf is the
* total number of buffers that are allocated */
if (g_uiCurLineBufIdx == g_uiTotalBuf) {
g_ppcBuf[g_uiCurLineBufIdx] = (char*)malloc(REGR_MAX_SCH_LINE_SIZE);
if (g_ppcBuf[g_uiCurLineBufIdx] == NULL) {
fprintf(stderr,
_("Could not allocate memory for reading line from "
"schedule file. Requested size: %d.\n"),
REGR_MAX_SCH_LINE_SIZE);
return REGR_ERRCODE_MALLOC_FAILURE;
}
g_uiTotalBuf++;
}
pcLineBuf = g_ppcBuf[g_uiCurLineBufIdx];
memset(pcLineBuf, 0, REGR_MAX_SCH_LINE_SIZE);
if (true == *pbHalfReadTest) {
/* Copy the 'half read' test file detail into the new buffer's start */
for (j = 0, pcTemp = *ppcLastSpace + 1; *pcTemp != '\0'; pcTemp++, j++)
pcLineBuf[j] = *pcTemp;
*pbHalfReadTest = false;
}
/* Loading the line buffer with the 'left out' characters of the current
* line in the schedule file */
if (NULL == fgets(&(pcLineBuf[j]), REGR_MAX_SCH_LINE_SIZE - j, scf))
return REGR_EOF_REACHED;
j = strlen(pcLineBuf);
if ((j > 0) && ('\n' != pcLineBuf[j - 1]) && (!feof(scf)))
*pbBuffReloadReq = true;
/* Parse the Line buffer to get the test file details */
iRet = regrParseLineBuffer(pcLineBuf,
ppcLastSpace,
piNumTests,
piMaxParallelTests,
pppctests,
pppstResultfiles,
pppstExpectfiles,
pppstTags,
ppPids,
ppiStatuses);
if (iRet != REGR_SUCCESS)
return iRet;
if ((true == *pbBuffReloadReq) && (NULL != *ppcLastSpace) && (0 != strlen(*ppcLastSpace + 1))) {
*pbHalfReadTest = true;
(*piNumTests)--;
}
return iRet;
}
/*
* Run all the tests specified in one schedule file
*/
static void run_schedule(const char* schedule, test_function tfunc, diag_function dfunc)
{
char** tests = NULL;
_stringlist** resultfiles;
_stringlist** expectfiles;
_stringlist** tags;
PID_TYPE* pids = NULL;
int* statuses = NULL;
int i;
int iRet = REGR_SUCCESS;
int iMaxParallelTests = REGR_MAX_PARALLEL_TESTS;
_stringlist* ignorelist = NULL;
FILE* scf = NULL;
int line_num = 0;
bool bRhsSpacePresent = false;
bool bscript = false;
bool bIgnoreLineOnReload = false;
bool isSystemTableDDL = false;
bool isPlanAndProto = false;
if (grayscale_upgrade != -1) {
g_uiTotalBuf = 0;
}
/* Allocating memory for holding the addresses of all line buffers.
* Each line buffer is used to hold a part (or full) of the line, read from
* the scheduler file */
g_ppcBuf = (char**)malloc(sizeof(char*) * REGR_MAX_NUM_OF_LINE_BUFF);
if (NULL == g_ppcBuf) {
fprintf(stderr,
_("Could not allocate memory for buffer to hold line "
"buffer addresses. Requested size: %lu.\n"),
(REGR_MAX_NUM_OF_LINE_BUFF * sizeof(char*)));
exit(2);
}
/* Allocating memory for holding a line to be read from the schedule file */
g_ppcBuf[g_uiCurLineBufIdx] = (char*)malloc(REGR_MAX_SCH_LINE_SIZE);
if (g_ppcBuf[g_uiCurLineBufIdx] == NULL) {
fprintf(stderr,
_("Could not allocate memory for reading line from "
"schedule file. Requested size: %d.\n"),
REGR_MAX_SCH_LINE_SIZE);
iRet = REGR_ERRCODE_MALLOC_FAILURE;
goto LB_ERR_HNDL_LVL_1;
}
g_uiTotalBuf++;
memset(g_ppcBuf[g_uiCurLineBufIdx], 0, REGR_MAX_SCH_LINE_SIZE);
/* Allocating memory for test file path storage, result file path storage,
* expected file path storage, tags (not used as of now) storage, PID TYPE
* storage and the status storage for the scheduler test execution */
iRet = regrMallocForTest(iMaxParallelTests, 0, &tests, &resultfiles, &expectfiles, &tags, &pids, &statuses);
if (iRet != REGR_SUCCESS) {
iRet = REGR_ERRCODE_MALLOC_FAILURE;
goto LB_ERR_HNDL_LVL_2;
}
/* Initializing the "total time taken by the test suite execution" */
g_dGroupTotalTime = 0;
scf = fopen(schedule, "r");
if (!scf) {
fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, schedule, gs_strerror(errno));
iRet = REGR_ERRCODE_FOPEN_FAILURE;
goto LB_ERR_HNDL_LVL_3;
}
while (fgets(g_ppcBuf[g_uiCurLineBufIdx], REGR_MAX_SCH_LINE_SIZE, scf)) {
char* test = NULL;
char* pcLastSpace = NULL;
char* c = NULL;
int num_tests;
bool bBuffReloadReq = false;
bool bHalfReadTest = false;
i = strlen(g_ppcBuf[g_uiCurLineBufIdx]);
if ((i > 0) && ('\n' != g_ppcBuf[g_uiCurLineBufIdx][i - 1]) && (!feof(scf))) {
bBuffReloadReq = true;
}
if (bIgnoreLineOnReload) {
if (!bBuffReloadReq)
bIgnoreLineOnReload = false;
continue;
}
line_num++;
for (i = 0; i < iMaxParallelTests; i++) {
if (resultfiles[i] == NULL)
break;
free_stringlist(&resultfiles[i]);
free_stringlist(&expectfiles[i]);
free_stringlist(&tags[i]);
}
/* strip trailing whitespace, especially the newline */
i = strlen(g_ppcBuf[g_uiCurLineBufIdx]);
while ((i > 0) && (isspace((unsigned char)g_ppcBuf[g_uiCurLineBufIdx][i - 1]))) {
i--;
if ((g_ppcBuf[g_uiCurLineBufIdx][i] == ' ') || (g_ppcBuf[g_uiCurLineBufIdx][i] == '\t'))
bRhsSpacePresent = true;
g_ppcBuf[g_uiCurLineBufIdx][i] = '\0';
}
if ((g_ppcBuf[g_uiCurLineBufIdx][0] == '\0') || (g_ppcBuf[g_uiCurLineBufIdx][0] == '#')) {
if (bBuffReloadReq)
bIgnoreLineOnReload = true;
continue;
}
if (strncmp(g_ppcBuf[g_uiCurLineBufIdx], "test: ", 6) == 0) {
test = g_ppcBuf[g_uiCurLineBufIdx] + 6;
} else if (strncmp(g_ppcBuf[g_uiCurLineBufIdx], "system_table_ddl_test: ", 23) == 0) {
isSystemTableDDL = true;
test = g_ppcBuf[g_uiCurLineBufIdx] + 23;
} else if (strncmp(g_ppcBuf[g_uiCurLineBufIdx], "plan_proto_test: ", 17) == 0) {
isPlanAndProto = true;
test = g_ppcBuf[g_uiCurLineBufIdx] + 17;
} else if (strncmp(g_ppcBuf[g_uiCurLineBufIdx], "script: ", 8) == 0) {
bscript = true;
test = g_ppcBuf[g_uiCurLineBufIdx] + 8;
} else if (strncmp(g_ppcBuf[g_uiCurLineBufIdx], "ignore: ", 8) == 0) {
c = g_ppcBuf[g_uiCurLineBufIdx] + 8;
while (*c && isspace((unsigned char)*c))
c++;
add_stringlist_item(&ignorelist, c);
/*
* Note: ignore: lines do not run the test, they just say that
* failure of this test when run later on is to be ignored. A bit
* odd but that's how the shell-script version did it.
*/
continue;
} else if (strncmp(g_ppcBuf[g_uiCurLineBufIdx], "testcon: ", 9) == 0) {
#ifdef ENABLED_DEBUG_SYNC
test = g_ppcBuf[g_uiCurLineBufIdx] + 9;
#else
c = g_ppcBuf[g_uiCurLineBufIdx] + 9;
while (*c && isspace((unsigned char)*c))
c++;
add_stringlist_item(&ignorelist, c);
continue;
#endif
} else {
fprintf(stderr,
_("Syntax error in ScheduleFile \"%s\" line %d: %s\n"),
schedule,
line_num,
g_ppcBuf[g_uiCurLineBufIdx]);
iRet = REGR_ERRCODE_SYNTAX_ERR_IN_SCH;
goto LB_ERR_HNDL_LVL_4;
}
num_tests = 0;
iRet = regrParseLineBuffer(test,
&pcLastSpace,
&num_tests,
&iMaxParallelTests,
&tests,
&resultfiles,
&expectfiles,
&tags,
&pids,
&statuses);
if (iRet != REGR_SUCCESS) {
goto LB_ERR_HNDL_LVL_4;
}
if ((!bRhsSpacePresent) && (true == bBuffReloadReq) && (NULL != pcLastSpace) &&
(0 != strlen(pcLastSpace + 1))) {
bHalfReadTest = true;
num_tests--;
}
/* Resetting the flag */
bRhsSpacePresent = false;
if (num_tests == 0) {
fprintf(stderr,
_("Syntax error in schedule file \"%s\" line %d: %s\n"),
schedule,
line_num,
g_ppcBuf[g_uiCurLineBufIdx]);
iRet = REGR_ERRCODE_SYNTAX_ERR_IN_SCH;
goto LB_ERR_HNDL_LVL_4;
}
if (bscript && (num_tests > 1)) {
fprintf(stderr,
_("Syntax error in schedule file \"%s\" line %d: %s\n"),
schedule,
line_num,
g_ppcBuf[g_uiCurLineBufIdx]);
iRet = REGR_ERRCODE_SYNTAX_ERR_IN_SCH;
goto LB_ERR_HNDL_LVL_4;
}
/* exception protect */
if (REG_MAX_NUM <= num_tests) {
fprintf(stderr,
_("The max test num:%d of parallel group is large than REG_MAX_NUM:%d. \n"
"Syntax error in schedule file \"%s\" line %d: %s\n"),
num_tests,
REG_MAX_NUM,
schedule,
line_num,
g_ppcBuf[g_uiCurLineBufIdx]);
iRet = REGR_ERRCODE_SYNTAX_ERR_IN_SCH;
goto LB_ERR_HNDL_LVL_4;
}
if (num_tests == 1) {
if (bscript) {
bscript = false;
status(_("script %-22s .... "), tests[0]);
REGR_START_TIMER_TEMP(0);
pids[0] = scriptExecute(tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
} else {
if (isSystemTableDDL) {
status(_("system_table_ddl_test %-24s .... "), tests[0]);
} else if (isPlanAndProto) {
status(_("plan_proto_test %-24s .... "), tests[0]);
} else if (use_jdbc_client) {
status(_("jdbc test %-24s .... "), tests[0]);
} else {
status(_("test %-24s .... "), tests[0]);
}
makeNestedDirectory(tests[0]);
REGR_START_TIMER_TEMP(0);
pids[0] = (tfunc)(tests[0], &resultfiles[0], &expectfiles[0], &tags[0], use_jdbc_client);
}
wait_for_tests(pids, statuses, NULL, 1);
REGR_STOP_TIMER_TEMP(0);
REGR_PRINT_ONEGROUP_ELAPSED_TIME;
/* status line is finished below */
} else if (max_connections > 0 && max_connections < num_tests) {
int oldest = 0;
i = 0;
do {
while (i < num_tests) {
if (i - oldest >= max_connections) {
wait_for_tests(pids + oldest, statuses + oldest, tests + oldest, i - oldest);
oldest = i;
}
makeNestedDirectory(tests[i]);
REGR_START_TIMER;
/* Invoke the single test file */
pids[i] = (tfunc)(tests[i], &resultfiles[i], &expectfiles[i], &tags[i], use_jdbc_client);
i++;
}
if (false == bBuffReloadReq)
break;
/* Resetting the flag */
bBuffReloadReq = false;
iRet = regrReloadAndParseLineBuffer(&bBuffReloadReq,
&bHalfReadTest,
&pcLastSpace,
scf,
&num_tests,
&iMaxParallelTests,
&tests,
&resultfiles,
&expectfiles,
&tags,
&pids,
&statuses);
if (iRet == REGR_EOF_REACHED) {
break;
}
if (iRet != REGR_SUCCESS) {
goto LB_ERR_HNDL_LVL_4;
}
} while (true);
if (isSystemTableDDL) {
status(_("parallel group (%d system_table_ddl_tests, in groups of %d): "), num_tests, max_connections);
} else if (isPlanAndProto) {
status(_("parallel group (%d plan_proto_tests, in groups of %d): "), num_tests, max_connections);
} else {
status(_("parallel group (%d tests, in groups of %d): "), num_tests, max_connections);
}
wait_for_tests(pids + oldest, statuses + oldest, tests + oldest, i - oldest);
status_end();
g_uiCurLineBufIdx = 0;
} else {
i = 0;
do {
while (i < num_tests) {
makeNestedDirectory(tests[i]);
REGR_START_TIMER_TEMP(i);
pids[i] = (tfunc)(tests[i], &resultfiles[i], &expectfiles[i], &tags[i], use_jdbc_client);
i++;
}
if (false == bBuffReloadReq)
break;
/* Resetting the flag */
bBuffReloadReq = false;
iRet = regrReloadAndParseLineBuffer(&bBuffReloadReq,
&bHalfReadTest,
&pcLastSpace,
scf,
&num_tests,
&iMaxParallelTests,
&tests,
&resultfiles,
&expectfiles,
&tags,
&pids,
&statuses);
if (iRet == REGR_EOF_REACHED) {
break;
}
if (iRet != REGR_SUCCESS) {
goto LB_ERR_HNDL_LVL_4;
}
} while (true);
if (isSystemTableDDL) {
status(_("parallel group (%d system_table_ddl_tests): "), num_tests);
} else if (isPlanAndProto) {
status(_("parallel group (%d plan_proto_tests): "), num_tests);
} else {
status(_("parallel group (%d tests): "), num_tests);
}
wait_for_tests(pids, statuses, tests, num_tests);
REGR_PRINT_ONEGROUP_ELAPSED_TIME;
status_end();
g_uiCurLineBufIdx = 0;
}
/* Check results for all tests */
for (i = 0; i < num_tests; i++) {
_stringlist *rl, *el, *tl;
bool differ = false;
if (num_tests > 1)
status(_(" %-24s .... "), tests[i]);
/*
* Advance over all three lists simultaneously.
*
* Compare resultfiles[j] with expectfiles[j] always. Tags are
* optional but if there are tags, the tag list has the same
* length as the other two lists.
*/
for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
rl != NULL; /* rl and el have the same length */
rl = rl->next, el = el->next) {
bool newdiff = false;
if (tl)
tl = tl->next; /* tl has the same length as rl and el
* if it exists */
newdiff = results_differ(tests[i], rl->str, el->str);
/* Check if diff has failed; if yes then collect the diagnosis report*/
if (newdiff && dfunc) {
pids[0] = dfunc((char*)tests[i]);
wait_for_tests(pids, statuses, NULL, 1);
}
if (newdiff && tl) {
printf("%s ", tl->str);
}
differ = differ || newdiff;
}
if (differ) {
bool ignore = false;
_stringlist* sl = NULL;
for (sl = ignorelist; sl != NULL; sl = sl->next) {
if (strcmp(tests[i], sl->str) == 0) {
ignore = true;
break;
}
}
if (ignore) {
status(_("%-18s"), "failed (ignored)");
fail_ignore_count++;
} else if (isSystemTableDDL) {
status(_("%-18s"), "ok (grayscale stage)");
success_count++;
} else if (isPlanAndProto) {
status(_("%-18s"), "FAILED (grayscale observe stage)");
fail_count++;
} else {
status(_("%-18s"), "FAILED");
fail_count++;
}
} else {
if (isSystemTableDDL) {
status(_("%-18s"), "FAILED (grayscale stage)");
fail_count++;
} else if (isPlanAndProto) {
status(_("%-18s"), "FAILED (grayscale stage)");
fail_count++;
} else {
status(_("%-18s"), "ok");
success_count++;
}
}
REGR_PRINT_ELAPSED_TIME_TEMP(i);
if (statuses[i] != 0) {
log_child_failure(statuses[i]);
}
/*
* Indicates the last test in the group in case of parallel test
* So end of status shall be done after the performation info
* printing
*/
}
}
free_stringlist(&ignorelist);
LB_ERR_HNDL_LVL_4:
fclose(scf);
LB_ERR_HNDL_LVL_3:
REGR_FREE(tests);
LB_ERR_HNDL_LVL_2:
for (i = 0; i < (int)g_uiTotalBuf; i++) {
REGR_FREE(g_ppcBuf[i]);
}
LB_ERR_HNDL_LVL_1:
REGR_FREE(g_ppcBuf);
if (iRet != REGR_SUCCESS)
exit_nicely(2);
return;
}
/*
* Run a single test
*/
static void run_single_test(const char* test, test_function tfunc, diag_function dfunc)
{
PID_TYPE pid;
int exit_status;
_stringlist* resultfiles = NULL;
_stringlist* expectfiles = NULL;
_stringlist* tags = NULL;
_stringlist *rl, *el, *tl;
bool differ = false;
if (!use_jdbc_client) {
status(_("test %-24s .... "), test);
} else {
status(_("jdbc test %-24s .... "), test);
}
makeNestedDirectory(test);
REGR_START_TIMER;
pid = (tfunc)(test, &resultfiles, &expectfiles, &tags, use_jdbc_client);
wait_for_tests(&pid, &exit_status, NULL, 1);
REGR_STOP_TIMER;
/*
* Advance over all three lists simultaneously.
*
* Compare resultfiles[j] with expectfiles[j] always. Tags are optional
* but if there are tags, the tag list has the same length as the other
* two lists.
*/
for (rl = resultfiles, el = expectfiles, tl = tags; rl != NULL; /* rl and el have the same length */
rl = rl->next, el = el->next) {
bool newdiff = false;
if (tl)
tl = tl->next; /* tl has the same length as rl and el if it
* exists */
newdiff = results_differ(test, rl->str, el->str);
/* Check if diff has failed; if yes then collect the diagnosis report*/
if (newdiff && dfunc) {
pid = dfunc((char*)test);
wait_for_tests(&pid, &exit_status, NULL, 1);
}
if (newdiff && tl) {
printf("%s ", tl->str);
}
differ = differ || newdiff;
}
if (differ) {
status(_("%-18s"), "FAILED");
fail_count++;
} else {
status(_("%-18s"), "ok");
success_count++;
}
if (exit_status != 0)
log_child_failure(exit_status);
if (false == g_bEnablePerfDataPrint)
status_end();
REGR_PRINT_ELAPSED_TIME;
}
#define GET_VARIABLE_NAME(name) (#name)
static void CheckCleanCodeWarningInfo(const int baseNum, const int currentNum,
const char *legacyMacrosName, const char *legacyMacrosNumName)
{
(void)fprintf(stdout, "======================== Clean Code Committee Warning ========================\n");
if (strcmp(legacyMacrosName, "THR_LOCAL") == 0) {
(void)fprintf(stdout, "The macro THR_LOCAL are strictly forbidden.\n");
} else {
(void)fprintf(stdout, "The only authoritative macro to isolate distribute and single node\n");
(void)fprintf(stdout, "logic is ENABLE_MULTIPLE_NODES, other legacy macros(PGXC/STREAMPLAN/IS_SINGLE_NODE)\n");
(void)fprintf(stdout, "are strictly forbidden.\n");
}
(void)fprintf(stdout, "The macro(%s) baseline are %d, current are %d.\n",
legacyMacrosName, baseNum, currentNum);
if (currentNum < baseNum) {
(void)fprintf(stdout, "Solution: Please adjust the baseline(%s:%s) to %d before run test.\n",
__FILE__, legacyMacrosNumName, currentNum);
} else {
(void)fprintf(stdout, "Solution: Please decrease the macros at least equal to baseline %d.\n",
baseNum);
}
(void)fprintf(stdout, "==============================================================================\n");
(void)fflush(stdout);
return;
}
#define BASE_GLOBAL_VARIABLE_NUM 222
#define CMAKE_CMD_BUF_LEN 1000
static void check_global_variables()
{
#ifdef BUILD_BY_CMAKE
char cmd_buf[CMAKE_CMD_BUF_LEN+1];
snprintf(cmd_buf, CMAKE_CMD_BUF_LEN,"find %s/common/backend/ -name \"*.cpp\" | \
xargs grep \"THR_LOCAL\" | grep -v \"extern THR_LOCAL\" | wc -l", code_base_src);
char* cmd = cmd_buf;
#else
char* cmd =
"find ../../common/backend/ -name \"*.cpp\" | xargs grep \"THR_LOCAL\" | \
grep -v \"extern THR_LOCAL\" | wc -l";
#endif
FILE* fstream = NULL;
char buf[50];
int globalnum = 0;
printf("cmd: %s\n", cmd);
memset(buf, 0, sizeof(buf));
fstream = popen(cmd, "r");
if (NULL != fgets(buf, sizeof(buf), fstream)) {
globalnum = atoi(buf);
}
pclose(fstream);
#ifdef BUILD_BY_CMAKE
snprintf(cmd_buf, CMAKE_CMD_BUF_LEN,"find %s/gausskernel/ -name \"*.cpp\" | \
xargs grep \"THR_LOCAL\" | grep -v \"extern THR_LOCAL\" | wc -l", code_base_src);
cmd = cmd_buf;
#else
cmd = "find ../../gausskernel/ -name \"*.cpp\" | xargs grep \"THR_LOCAL\" | \
grep -v \"extern THR_LOCAL\" | wc -l";
#endif
memset(buf, 0, sizeof(buf));
fstream = popen(cmd, "r");
if (NULL != fgets(buf, sizeof(buf), fstream)) {
globalnum += atoi(buf);
}
pclose(fstream);
if (globalnum != BASE_GLOBAL_VARIABLE_NUM) {
CheckCleanCodeWarningInfo(BASE_GLOBAL_VARIABLE_NUM, globalnum,
GET_VARIABLE_NAME(THR_LOCAL), GET_VARIABLE_NAME(BASE_GLOBAL_VARIABLE_NUM));
exit_nicely(2);
}
}
#define BASE_PGXC_LIKE_MACRO_NUM 1402
static void check_pgxc_like_macros()
{
#ifdef BUILD_BY_CMAKE
char cmd_buf[1001];
snprintf(cmd_buf, 1000,"find %s/common/backend/ %s/gausskernel/ -name \"*.cpp\" | \
xargs grep -e \"#ifdef STREAMPLAN\" -e \"#ifdef PGXC\" -e \"IS_SINGLE_NODE\" | \
wc -l", code_base_src, code_base_src);
printf("cmake..............i\n");
printf("cmd_buf:%s\n", cmd_buf);
char* cmd = cmd_buf;
#else
char* cmd =
"find ../../common/backend/ ../../gausskernel/ -name \"*.cpp\" |"
"xargs grep -e \"#ifdef STREAMPLAN\" -e \"#ifdef PGXC\" -e \"IS_SINGLE_NODE\" |wc -l";
#endif
FILE* fstream = NULL;
char buf[50];
int macros = 0;
memset(buf, 0, sizeof(buf));
fstream = popen(cmd, "r");
if (NULL != fgets(buf, sizeof(buf), fstream)) {
macros = atoi(buf);
if (macros != BASE_PGXC_LIKE_MACRO_NUM) {
CheckCleanCodeWarningInfo(BASE_PGXC_LIKE_MACRO_NUM, macros,
GET_VARIABLE_NAME(PGXC/STREAMPLAN/IS_SINGLE_NODE), GET_VARIABLE_NAME(BASE_PGXC_LIKE_MACRO_NUM));
exit_nicely(2);
}
}
pclose(fstream);
}
/*
* Create the summary-output files (making them empty if already existing)
*/
static void open_result_files(void)
{
char file[MAXPGPATH];
FILE* difffile = NULL;
errno_t rc = EOK;
/* create the log file (copy of running status output) */
(void)snprintf(file, sizeof(file), "%s/regression.out", outputdir);
logfilename = strdup(file);
logfile = fopen(logfilename, "w");
if (!logfile) {
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, logfilename, strerror(errno));
exit_nicely(2);
}
/* create the diffs file as empty */
if (to_create_jdbc_user) {
rc = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s/regression_jdbc.diffs", outputdir);
securec_check_ss_c(rc, "", "");
} else {
rc = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s/regression.diffs", outputdir);
securec_check_ss_c(rc, "", "");
}
difffilename = strdup(file);
difffile = fopen(difffilename, "w");
if (!difffile) {
fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"), progname, difffilename, strerror(errno));
exit_nicely(2);
}
/* we don't keep the diffs file open continuously */
fclose(difffile);
/* also create the output directory if not present */
if (!use_jdbc_client) {
rc = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s/results", outputdir);
securec_check_ss_c(rc, "", "");
} else {
rc = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s/results_jdbc", outputdir);
securec_check_ss_c(rc, "", "");
}
if (!directory_exists(file)) {
make_directory(file);
}
}
/* create jdbc_user & grant all database to it */
static void create_jdbc_user(const _stringlist* granted_dbs) {
const char *user_name = "jdbc_regress";
const char *user_password = "1q@W3e4r";
header(_("creating user for JDBC: \"%s\""), user_name);
psql_command("postgres", "CREATE USER %s WITH PASSWORD '%s' LOGIN", user_name, user_password);
psql_command("postgres", "ALTER USER %s sysadmin CREATEROLE createdb", user_name);
for (; granted_dbs != NULL; granted_dbs = granted_dbs->next) {
psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"", granted_dbs->str, user_name);
}
}
static void create_database(const char* dbname)
{
_stringlist* sl = NULL;
/*
* We use template0 so that any installation-local cruft in template1 will
* not mess up the tests.
*/
header(_("creating database \"%s\""), dbname);
if (encoding)
psql_command("postgres",
"CREATE DATABASE \"%s\" DBCOMPATIBILITY='A' TEMPLATE=TEMPLATE0 ENCODING='%s'%s",
dbname,
encoding,
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
else
psql_command("postgres",
"CREATE DATABASE \"%s\" DBCOMPATIBILITY='A' TEMPLATE=TEMPLATE0%s",
dbname,
(nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
/*
* Install any requested procedural languages. We use CREATE OR REPLACE
* so that this will work whether or not the language is preinstalled.
*/
for (sl = loadlanguage; sl != NULL; sl = sl->next) {
header(_("installing %s"), sl->str);
psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
}
/*
* Install any requested extensions. We use CREATE IF NOT EXISTS so that
* this will work whether or not the extension is preinstalled.
*/
for (sl = loadextension; sl != NULL; sl = sl->next) {
header(_("installing %s"), sl->str);
psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
}
}
static void create_role(const char* rolename, const _stringlist* granted_dbs)
{
header(_("creating role \"%s\""), rolename);
psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
for (; granted_dbs != NULL; granted_dbs = granted_dbs->next) {
psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"", granted_dbs->str, rolename);
}
}
static char* make_absolute_path(const char* in)
{
char* result = NULL;
if (is_absolute_path(in)) {
result = strdup(in);
} else {
static char cwdbuf[MAXPGPATH];
if (!cwdbuf[0]) {
if (!getcwd(cwdbuf, sizeof(cwdbuf))) {
fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
exit_nicely(2);
}
}
result = (char*)malloc(strlen(cwdbuf) + strlen(in) + 2);
sprintf(result, "%s/%s", cwdbuf, in);
}
canonicalize_path(result);
return result;
}
static void help(void)
{
printf(_("openGauss regression test driver\n"));
printf(_("\n"));
printf(_("Usage: %s [options...] [extra tests...]\n"), progname);
printf(_("\n"));
printf(_("Options:\n"));
printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
printf(_(" --debug turn on debug mode in programs that are run\n"));
printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n"));
printf(_(" --load-language=lang load the named language before running the\n"));
printf(_(" tests; can appear multiple times\n"));
printf(_(" --load-extension=ext load the named extension before running the\n"));
printf(_(" tests; can appear multiple times\n"));
printf(_(" --create-role=ROLE create the specified role before testing\n"));
printf(_(" --max-connections=N maximum number of concurrent connections\n"));
printf(_(" (default is 0 meaning unlimited)\n"));
printf(_(" --encoding=ENCODING use ENCODING as the encoding\n"));
printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n"));
printf(_(" --schedule=FILE use test ordering schedule from FILE\n"));
printf(_(" (can be used multiple times to concatenate)\n"));
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
printf(_(" --use-existing use an existing installation\n"));
printf(_(" --launcher=CMD use CMD as launcher of gsql\n"));
printf(_(" --skip_environment_cleanup do not clean generated sql scripts\n"));
printf(_("\n"));
printf(_("Options for \"temp-install\" mode:\n"));
printf(_(" --no-locale use C locale\n"));
printf(_(" --top-builddir=DIR (relative) path to top level build directory\n"));
printf(_(" --port=PORT start postmaster on PORT\n"));
printf(_(" --temp-config=PATH append contents of PATH to temporary config\n"));
printf(_(" --extra-install=DIR additional directory to install (e.g., contrib\n"));
printf(_(" --hdfshostname=IPAddress hdfs data IP address\n"));
printf(_(" --hdfsstoreplus=hdfsstoreplus hdfs data store path plus information\n"));
printf(_(" --hdfscfgpath=ConfigPath config path for principle and keytab file path\n"));
printf(_(" --hdfsport=N hdfs data port\n"));
printf(_(" --obshostname=IPAddress obs data IP address\n"));
printf(_(" --obsbucket=BucketName obs Bucket \n"));
printf(_(" --ak=access_key obs server access key \n"));
printf(_(" --sk=secret_access_key obs server secret access key \n"));
printf(_(" --g_aiehost=host_ip AI engine host IP \n"));
printf(_(" --g_aieport=port_number AI engine port number \n"));
printf(_("\n"));
printf(_("Options for using an existing installation:\n"));
printf(_(" --host=HOST use postmaster running on HOST\n"));
printf(_(" --port=PORT use postmaster running at PORT\n"));
printf(_(" --user=USER connect as USER\n"));
printf(_(" --psqldir=DIR use gsql in DIR (default: find in PATH)\n"));
printf(_(" --enable-segment create table default with segment=on"));
printf(_(" --jdbc enable jdbc regression test"));
printf(_("\n"));
printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
printf(_("if the tests could not be run for some reason.\n"));
}
static int initialize_myinfo(
int coordnode_num, int datanode_num, int init_port, bool keep_last_time_data, bool run_test_case)
{
int i;
int* coordnode_port = NULL;
int* coordnode_pooler_port = NULL;
int* coordnode_stream_ctl_port = NULL;
int* coordnode_sctp_port = NULL;
int* datanode_port = NULL;
int* datanode_pooler_port = NULL;
int* datanode_stream_ctl_port = NULL;
int* datanode_sctp_port = NULL;
/* if standby configed. */
int* dns_port = NULL;
int* dns_sctp_port = NULL;
int* dns_ctl_port = NULL;
int* dn_primary_port = NULL;
int* dn_standby_port = NULL;
int* dn_secondary_port = NULL;
if (coordnode_num) {
coordnode_port = (int*)malloc(sizeof(int) * coordnode_num);
coordnode_pooler_port = (int*)malloc(sizeof(int) * coordnode_num);
coordnode_stream_ctl_port = (int*)malloc(sizeof(int) * coordnode_num);
coordnode_sctp_port = (int*)malloc(sizeof(int) * coordnode_num);
}
datanode_pooler_port = (int*)malloc(sizeof(int) * datanode_num);
datanode_port = (int*)malloc(sizeof(int) * datanode_num);
datanode_stream_ctl_port = (int*)malloc(sizeof(int) * datanode_num);
datanode_sctp_port = (int*)malloc(sizeof(int) * datanode_num);
if (standby_defined) {
dns_port = (int*)malloc(sizeof(int) * datanode_num);
dns_ctl_port = (int*)malloc(sizeof(int) * datanode_num);
dns_sctp_port = (int*)malloc(sizeof(int) * datanode_num);
dn_primary_port = (int*)malloc(sizeof(int) * datanode_num);
dn_standby_port = (int*)malloc(sizeof(int) * datanode_num);
dn_secondary_port = (int*)malloc(sizeof(int) * datanode_num);
}
for (i = 0; i < coordnode_num; i++) {
coordnode_port[i] = init_port++;
coordnode_pooler_port[i] = init_port++;
coordnode_sctp_port[i] = init_port++;
coordnode_stream_ctl_port[i] = init_port++;
}
/* reserve port for datanode instance */
for (i = 0; i < datanode_num; i++) {
datanode_port[i] = init_port++;
datanode_pooler_port[i] = init_port++;
datanode_sctp_port[i] = init_port++; // Reserve sctp port for datanode.
datanode_stream_ctl_port[i] = init_port++;
if (standby_defined) {
dns_port[i] = init_port++;
init_port++;
dns_sctp_port[i] = init_port++;
init_port++;
dns_ctl_port[i] = init_port++;
dn_primary_port[i] = init_port++;
dn_standby_port[i] = init_port++;
dn_secondary_port[i] = init_port++;
}
}
myinfo.co_num = coordnode_num;
myinfo.dn_num = datanode_num;
myinfo.co_port = coordnode_port;
myinfo.co_ctl_port = coordnode_stream_ctl_port;
myinfo.co_sctp_port = coordnode_sctp_port;
myinfo.co_pool_port = coordnode_pooler_port;
myinfo.dn_port = datanode_port;
myinfo.dns_port = dns_port;
myinfo.dn_pool_port = datanode_pooler_port;
myinfo.dn_ctl_port = datanode_stream_ctl_port;
myinfo.dn_sctp_port = datanode_sctp_port;
myinfo.dns_ctl_port = dns_ctl_port;
myinfo.dns_sctp_port = dns_sctp_port;
myinfo.dn_primary_port = dn_primary_port;
myinfo.dn_standby_port = dn_standby_port;
myinfo.dn_secondary_port = dn_secondary_port;
myinfo.gtm_port = init_port;
myinfo.keep_data = keep_last_time_data;
myinfo.run_check = run_test_case;
myinfo.shell_count = 0;
return 0;
}
/*
* Compares two strings case insensitively. Returns 0 if the strings matched.
* A non zero value otherwise
*/
static int regressStrncasecmp(const char* rs1, const char* rs2, int iMaxLenToCmp)
{
int i;
int* piMaxLenCmp = &iMaxLenToCmp;
if (iMaxLenToCmp == -1)
piMaxLenCmp = &i;
for (i = 1; i <= *piMaxLenCmp; i++) {
unsigned char ch1 = (unsigned char)*rs1++;
unsigned char ch2 = (unsigned char)*rs2++;
if (ch1 != ch2) {
/* Convert all into lower case */
if ((ch1 >= 'A') && (ch1 <= 'Z'))
ch1 += 'a' - 'A';
if ((ch2 >= 'A') && (ch2 <= 'Z'))
ch2 += 'a' - 'A';
/* String did not match */
if (ch1 != ch2)
return (int)ch1 - (int)ch2;
}
if (ch1 == 0)
break;
}
return 0;
}
/*
* Validate the configuration item value of type: "on" or "off"
*/
static bool regrValidateBoolConfigVal(const char* pcConfigValue, int iConfId)
{
int len = strlen(pcConfigValue);
if (len == 0) {
fprintf(stderr,
_("\n Value not specified for configuration item: [%s]."
"Value should be \'off\' or \'on\'!\n"),
aacRegConfItemName[iConfId]);
return false;
}
if ((0 != regressStrncasecmp(pcConfigValue, "on", -1)) && (0 != regressStrncasecmp(pcConfigValue, "off", -1))) {
fprintf(stderr,
_("\n Value for the configuration item: [%s] should be"
" \'on\' or \'off\'. User has given: \'%s\'\n"),
aacRegConfItemName[iConfId],
pcConfigValue);
return false;
}
return true;
}
/* Set the value of the configuration item: "column_name_present" into the
* globals */
static void processTuplesOnlyConfItem(char* pcConfigValue)
{
if (false == regrValidateBoolConfigVal(pcConfigValue, REGR_TUPONLY_ID))
exit(2);
/* column_name_present = off ie tuples only */
if (0 == regressStrncasecmp(pcConfigValue, "off", -1))
(void)strncpy((char*)g_stRegrConfItems.acTuplesOnly, "-t", (MAX_TUPLE_ONLY_STRLEN - 1));
}
/* Set the value of the configuration item:"column_separator" into the globals*/
static void processColSepConfItem(const char* pcConfigValue)
{
int len;
char* pcTmp = (char*)pcConfigValue;
len = strlen(pcTmp);
if (len == 0) {
fprintf(stderr,
_("\n Value not specified for configuration item: [%s]!"
"Value should be a string, indicating the field separator. \n"),
aacRegConfItemName[REGR_COL_SEP_ID]);
exit(2);
}
if (*pcTmp != '\'') {
fprintf(stderr,
_("\n Syntax error in the regress "
"configuration item:%s! \' missing at the start of "
"the value! \n"),
aacRegConfItemName[REGR_COL_SEP_ID]);
exit(2);
}
/* Ignoring the start ' character */
pcTmp++;
len--;
if ((len == 0) || (pcTmp[len - 1] != '\'')) {
fprintf(stderr,
_("\n Syntax error in the regress "
"configuration item:%s! \' missing at the end of "
"the value! \n"),
aacRegConfItemName[REGR_COL_SEP_ID]);
exit(2);
}
/* Overwriting the end ' character */
pcTmp[len - 1] = '\0';
len = strlen(pcTmp);
/* User has given empty string. If no column separator is required,
* Space character shall be given but cannot be empty */
if (len == 0) {
fprintf(stderr,
_("\n User has given empty string. If no column "
"separator is required, Space character shall be given but it "
"cannot be empty. \n"));
exit(2);
}
if (len > MAX_COLUMN_SEP_STRLEN) {
fprintf(stderr,
_("\n Maximum allowed length of the column separator "
"string is %d!! Given length: %d. Given string: [%s].\n"),
MAX_COLUMN_SEP_STRLEN,
len,
pcTmp);
exit(2);
}
sprintf((char*)g_stRegrConfItems.acFieldSepForAllText, "-C\"%s\"", pcTmp);
}
/* Process the configuration item: performance_data_printing */
static void processPerfDataLoggingConf(const char* pcConfigValue)
{
if (false == regrValidateBoolConfigVal(pcConfigValue, REGR_PERF_DATA_LOGGING_ID))
exit(2);
if (0 == regressStrncasecmp(pcConfigValue, "on", -1))
g_bEnablePerfDataPrint = true;
}
static void processDiagCollectorConf(const char* pcConfigValue)
{
if (false == regrValidateBoolConfigVal(pcConfigValue, REGR_DIAG_COLLECT_ID))
exit(2);
if (0 == regressStrncasecmp(pcConfigValue, "on", -1))
g_bEnableDiagCollection = true;
}
/* Initialize of the module-global structure instance for storing the
* replacement pattern strings */
static void regrInitReplcPattStruct(void)
{
g_stRegrReplcPatt.iNumOfPatterns = 0;
g_stRegrReplcPatt.iRemainingPattBuffSize = 0;
g_stRegrReplcPatt.iMaxNumOfPattern = REGR_NUM_OF_REPLC_ITEMS_CHUNK;
g_stRegrReplcPatt.puiPatternOffset = NULL;
g_stRegrReplcPatt.puiPattReplValOffset = NULL;
g_stRegrReplcPatt.pcBuf = NULL;
}
/*
* This functions checks whether the line starts with a global variable name i.e. @....@.
* If yes, it returns true.
* ppcRegConfLine will be pointing to the character after the global
* variable name (maybe to the '=' character)
* piPattLen will be having the length of the global variable name.
*
* If no, it returns false.
*/
static bool regrIsStringPatternToBeReplaced(char** ppcRegConfLine, int* piPattLen)
{
char* pcCurLine = *ppcRegConfLine;
/* Format for specifying the replacement string is
* @VAR@ = 'VALUE'
* So the first character should be REGR_REPLC_PATT_START_CHAR */
if (*(pcCurLine++) != REGR_REPLC_PATT_START_CHAR)
return false;
/* Setting the length of the replacement pattern string as 1 on counting @*/
*piPattLen = 1;
while ((*pcCurLine != '\0') && (*pcCurLine != ' ') && (*pcCurLine != '\t') && (*pcCurLine != '=')) {
(*piPattLen)++;
pcCurLine++;
}
if ((*piPattLen == 1) || ((*(pcCurLine - 1)) != REGR_REPLC_PATT_END_CHAR)) {
fprintf(stderr,
_("\n Missing %c at the end of the pattern replacement "
"string! FORMAT is @PATTERN_NAME@ = \'VALUE\' . Line: [%s] \n"),
REGR_REPLC_PATT_END_CHAR,
*ppcRegConfLine);
return false;
}
/* LHS cant be @@ */
if (*piPattLen == 2) {
fprintf(stderr,
_("\n Replacement Pattern string should not be %c%c\n"),
REGR_REPLC_PATT_START_CHAR,
REGR_REPLC_PATT_END_CHAR);
return false;
}
/* Replacement pattern string should be immediately followed by space or =*/
if ((*pcCurLine != ' ') && (*pcCurLine != '\t') && (*pcCurLine != '=')) {
fprintf(stderr,
_("\n Replacement pattern string should be immediately "
"followed by \'space\' or \'Equal to\' character."
" Given line: [%s]\n"),
*ppcRegConfLine);
return false;
}
while ((*pcCurLine == ' ') || (*pcCurLine == '\t'))
pcCurLine++;
*ppcRegConfLine = pcCurLine;
return true;
}
/* Checks whether the current line from regress.conf is an entry for the
* replacement pattern string.
*
* If yes,
* piPattLen will be pointing to the length of the replacement pattern string
* ppcReplValue will be pointing to the string to be replaced with
* piValLen will be pointing to the length of the string to be replaced with
* Function returns true
*
* If no,
* Function returns false.
*/
static bool regrIsItemReplcPatt(const char* pcLine, int* piPattLen, char** ppcReplValue, int* piValLen)
{
int len;
char* pcCurLine = (char*)pcLine;
/* Checking whether the start of the line contains a GLOBAL VARIABLE, i.e.
* @....@ */
if (false == regrIsStringPatternToBeReplaced(&pcCurLine, piPattLen))
return false;
/* Skipping the pattern name - value separator character: '=' */
if (*pcCurLine == '=')
pcCurLine++;
regrTrimString(&pcCurLine);
if (*pcCurLine == '\0') {
fprintf(stderr,
_("\n Missing value for the replacement pattern"
"string! FORMAT is @PATTERN_NAME@ = \'VALUE\' . Line: [%s] \n"),
pcLine);
return false;
}
len = strlen(pcCurLine);
/* Validation to ensure that the value is given within ' or " characters */
if ((len == 1) || ((*pcCurLine != '\'') && (*pcCurLine != '\"')) ||
((pcCurLine[len - 1] != '\'') && (pcCurLine[len - 1] != '\"'))) {
fprintf(stderr,
_("\n Value should be enclosed within \' or \" "
"characters. Given Value: [%s]\n"),
pcCurLine);
return false;
}
/* Removing the ' or " at the end of the value */
pcCurLine[len - 1] = '\0';
/* Avoiding the ' or " at the beginning of the value and updating the
* output parameter */
*ppcReplValue = pcCurLine + 1;
*piValLen = strlen(*ppcReplValue);
return true;
}
/* Allocates memory for the storage of the replacement pattern strings, their
* corresponding replacement strings, and the index for the both. The offset to
* the location which is going to be used for the storage of the replacement
* pattern string, will be initialized to the index slot to be used next.
*
* Memory Layout:
*
* INDEX 1 INDEX 2 STORAGE SPACE
* +----------------+-------------------+-----------------------------+
* | Offsets to the | Offsets to | Actual storage for Patterns |
* | Patterns to be | the strings to be | to be replaced and their |
* | replaced | replaced for | corresponding values |
* | | the given patterns| They gets stored as pairs. |
* | | | \0 is explicitly stored. |
* +----------------+-------------------+-----------------------------+
* ^
* |
* Offset start
* i.e. g_stRegrReplcPatt.pcBuf
*/
static int regrAllocMemForReplcPatt()
{
int iBuffSize;
int iOldMaxNumOfPatt = 0;
int iSizeOfIdxSect;
unsigned int* puiPattBkup = NULL;
/* Case of reallocation. Back up the address of the old memory block */
if (g_stRegrReplcPatt.puiPatternOffset != NULL) {
puiPattBkup = g_stRegrReplcPatt.puiPatternOffset;
iOldMaxNumOfPatt = g_stRegrReplcPatt.iMaxNumOfPattern;
g_stRegrReplcPatt.iMaxNumOfPattern += REGR_NUM_OF_REPLC_ITEMS_CHUNK;
}
/* Size of the INDEX BLOCK for the easy access of the storage buffer */
iSizeOfIdxSect = REGR_SIZE_OF_INDEX_ELEM * g_stRegrReplcPatt.iMaxNumOfPattern;
/* Size of the buffer excluding the offset sections */
iBuffSize = (REGR_AVG_REPLC_PATT_LEN + REGR_AVG_REPLC_PATT_VAL_LEN) * g_stRegrReplcPatt.iMaxNumOfPattern;
/* Allocating memory for storing the pattern replacement strings
* from the regress.conf file */
g_stRegrReplcPatt.puiPatternOffset = (unsigned int*)malloc((iSizeOfIdxSect * 2) + iBuffSize);
if (g_stRegrReplcPatt.puiPatternOffset == NULL) {
fprintf(stderr,
_("Could not allocate memory for parsing "
"regress.conf file! Requested Size: %d. \n"),
(int)((iSizeOfIdxSect * 2) + iBuffSize));
return REGR_ERRCODE_MALLOC_FAILURE;
}
memset(g_stRegrReplcPatt.puiPatternOffset, '\0', (iSizeOfIdxSect * 2) + iBuffSize);
/* Initialize the remaining size of the storage buffer as the storage buffer
* size of the newly allocated memory */
g_stRegrReplcPatt.iRemainingPattBuffSize = iBuffSize;
g_stRegrReplcPatt.puiPattReplValOffset =
(unsigned int*)(((char*)(void*)g_stRegrReplcPatt.puiPatternOffset) + iSizeOfIdxSect);
g_stRegrReplcPatt.pcBuf = ((char*)(void*)g_stRegrReplcPatt.puiPattReplValOffset) + iSizeOfIdxSect;
/* Copy contents of old memory block into newly allocated memory */
if (puiPattBkup != NULL) {
/* Copying the offsets of replacement pattern strings */
memcpy(g_stRegrReplcPatt.puiPatternOffset, puiPattBkup, REGR_SIZE_OF_INDEX_ELEM * iOldMaxNumOfPatt);
/* Copying offsets of 'values' of the 'replacement pattern strings' */
memcpy(g_stRegrReplcPatt.puiPattReplValOffset,
(char*)(void*)puiPattBkup + (REGR_SIZE_OF_INDEX_ELEM * iOldMaxNumOfPatt),
REGR_SIZE_OF_INDEX_ELEM * iOldMaxNumOfPatt);
/* Copying the core contents of the old memory block excluding the index
* area */
memcpy(g_stRegrReplcPatt.pcBuf,
(char*)(void*)puiPattBkup + (REGR_SIZE_OF_INDEX_ELEM * iOldMaxNumOfPatt * 2),
((REGR_AVG_REPLC_PATT_LEN + REGR_AVG_REPLC_PATT_VAL_LEN) * iOldMaxNumOfPatt));
if (g_stRegrReplcPatt.iNumOfPatterns > 0) {
int iOffIdx = g_stRegrReplcPatt.iNumOfPatterns;
int iPrevOffset = g_stRegrReplcPatt.puiPatternOffset[iOffIdx - 1];
int iNewOffset = iPrevOffset + strlen(&(g_stRegrReplcPatt.pcBuf[iPrevOffset])) + 1;
/* Updating the offset of the next string's location, which is going
* to be used */
g_stRegrReplcPatt.puiPatternOffset[iOffIdx] = iNewOffset;
/* Subtracting the size of the copied contents from the
* 'new total buffer size' */
g_stRegrReplcPatt.iRemainingPattBuffSize -= (iBuffSize - iNewOffset);
}
/* Freeing the old memory block */
REGR_FREE(puiPattBkup);
}
return REGR_SUCCESS;
}
/* Load the replacement pattern strings and their values into global memory */
static void regrProcessReplcPattLine(const char* pcLine, int iPattLen, const char* pcReplcPattValue, int iPattValueLen)
{
int iIdx = g_stRegrReplcPatt.iNumOfPatterns;
unsigned int* puiPattOff = g_stRegrReplcPatt.puiPatternOffset;
unsigned int* puiValOff = g_stRegrReplcPatt.puiPattReplValOffset;
char* pcBuf = g_stRegrReplcPatt.pcBuf;
unsigned int uiCurrOff = puiPattOff[iIdx];
/* Load the pattern string to be replaced into global memory */
(void)strncpy((char*)&(pcBuf[puiPattOff[iIdx]]), pcLine, iPattLen);
/* Update the offset of the replacement pattern string's value in the
* corresponding index section. + 1 is considering the \0. Also note that
* we are not keeping \0 explicitly because the entire buffer is memset with
* 0 */
puiValOff[iIdx] = uiCurrOff + iPattLen + 1;
/* Load the value to be replaced for the pattern string, into global
* memory */
errno_t rc = memcpy_s((char*)&(pcBuf[puiValOff[iIdx]]), iPattValueLen, pcReplcPattValue, iPattValueLen);
securec_check_c(rc, "\0", "\0");
/* One pattern and its values is now loaded. So increment the loaded
* pattern count */
g_stRegrReplcPatt.iNumOfPatterns++;
/* If this IF CHECK is false, then we have filled the last pattern and its
* value. If next pair has to be loaded then memory need to be extented.
* Next offset will be updated during that time and here nothing is to be
* done */
if (g_stRegrReplcPatt.iNumOfPatterns < g_stRegrReplcPatt.iMaxNumOfPattern)
/* Updating the offset to the next usable location */
puiPattOff[g_stRegrReplcPatt.iNumOfPatterns] = puiValOff[iIdx] + (unsigned int)iPattValueLen + (unsigned int)1;
/* +2 is considering the \0 */
g_stRegrReplcPatt.iRemainingPattBuffSize -= (iPattLen + iPattValueLen + 2);
}
/* Load the regress configuration item values into the globals */
static void loadRegressConf(char* pcRegConfigFile)
{
int len;
int iRegConfInd;
int iPattLen = 0;
int iPattValueLen;
bool bLineValid = false;
char buf[MAX_LINE_LEN];
char* pcLine = NULL;
char* pcTemp = NULL;
char* pcReplcPattValue = NULL;
FILE* pfRegConf = NULL;
/* Open the regress.conf file */
pfRegConf = fopen(pcRegConfigFile, "r");
if (NULL == pfRegConf) {
fprintf(stderr,
_("\n Could not open the regression configuration "
"file: [%s]. Continuing with default values.\n"),
pcRegConfigFile);
return;
}
/* Process each line in the regress.conf file */
while (fgets(buf, sizeof(buf), pfRegConf) != NULL) {
pcLine = buf;
/* Remove white space on LHS and white space/CarriageReturn on RHS */
regrTrimString(&pcLine);
/* # at the start of the line indicates a full comment line. Entire line
* can be ignored */
if (*pcLine == '#')
continue;
/* Empty lines can be ignored */
if (*pcLine == '\0')
continue;
/* Comments can be started any where in the line. RHS of #, in a line,
* can be ignored */
pcTemp = strstr(pcLine, "#");
if (pcTemp != NULL)
*pcTemp = '\0';
bLineValid = false;
for (iRegConfInd = 0; iRegConfInd < MAX_REG_CONF_ITEMS; iRegConfInd++) {
len = strlen(aacRegConfItemName[iRegConfInd]);
if (0 == regressStrncasecmp(pcLine, aacRegConfItemName[iRegConfInd], len)) {
pcTemp = pcLine + len;
/* Configuration Item name should be immediately followed by */
if ((*pcTemp != ' ') && (*pcTemp != '\t') && (*pcTemp != '='))
continue;
while ((*pcTemp == ' ') || (*pcTemp == '\t'))
pcTemp++;
if (*pcTemp == '=')
pcTemp++;
regrTrimString(&pcTemp);
/* pcTemp will be now containing the value */
switch (iRegConfInd) {
case REGR_TUPONLY_ID:
processTuplesOnlyConfItem(pcTemp);
break;
case REGR_COL_SEP_ID:
processColSepConfItem(pcTemp);
break;
case REGR_PERF_DATA_LOGGING_ID:
processPerfDataLoggingConf(pcTemp);
break;
case REGR_DIAG_COLLECT_ID:
processDiagCollectorConf(pcTemp);
break;
}
/* Marking the line regress.conf as valid */
bLineValid = true;
/* Line matches with one of the config item. So process and get
* next line */
break;
}
}
if (bLineValid == false) {
regrTrimString(&pcLine);
if (regrIsItemReplcPatt(pcLine, &iPattLen, &pcReplcPattValue, &iPattValueLen)) {
if ((g_stRegrReplcPatt.iNumOfPatterns == 0) ||
(g_stRegrReplcPatt.iNumOfPatterns == g_stRegrReplcPatt.iMaxNumOfPattern) ||
((iPattLen + iPattValueLen + 2) > g_stRegrReplcPatt.iRemainingPattBuffSize)) {
do {
if (regrAllocMemForReplcPatt() != REGR_SUCCESS)
goto LB_MALLOC_REPLC_PATT_ERR;
} while ((iPattLen + iPattValueLen + 2) > g_stRegrReplcPatt.iRemainingPattBuffSize);
}
regrProcessReplcPattLine(pcLine, iPattLen, pcReplcPattValue, iPattValueLen);
} else
fprintf(stderr,
_("\n Unknown Configuration item. Line: [%s] "
"ignored. \n"),
pcLine);
}
} /* while (fgets ... */
LB_MALLOC_REPLC_PATT_ERR:
fclose(pfRegConf);
}
static void drop_role_if_exists(const char* rolename)
{
header(_("dropping role \"%s\""), rolename);
psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
}
static void drop_database_if_exists(const char* dbname)
{
header(_("dropping database \"%s\""), dbname);
psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
}
static void check_upgrade_options()
{
if ((inplace_upgrade || grayscale_upgrade != -1) && !upgrade_from) {
fprintf(stderr, _("Must provide upgrade_from parameter when doing upgrade check\n"));
exit_nicely(2);
}
if (!(inplace_upgrade || grayscale_upgrade != -1) && upgrade_from) {
fprintf(stderr, _("Please do not provide upgrade_from parameter when not doing upgrade check\n"));
exit_nicely(2);
}
if ((inplace_upgrade || grayscale_upgrade != -1) && myinfo.keep_data) {
fprintf(stderr, _("At present, upgrade check do no support keep data test.\n"));
exit_nicely(2);
}
}
static void setup_super_user()
{
header(_("setting super user for upgrade check"));
header(_("sleeping"));
sleep(3);
int i, j;
struct passwd* pwd;
pwd = getpwuid(getuid());
if (NULL == pwd || NULL == pwd->pw_name) {
fprintf(stderr, _("Can not get current user name.\n"));
exit_nicely(2);
}
for (i = 0; i < myinfo.co_num; i++) {
psql_command_node("postgres", i, COORD, "update pg_authid set rolname='%s' where rolsuper=true;", pwd->pw_name);
}
for (j = 0; j < myinfo.dn_num; j++) {
psql_command_node(
"postgres", j, DATANODE, "update pg_authid set rolname='%s' where rolsuper=true;", pwd->pw_name);
}
}
/*
* Start the temp postmaster
*/
static void start_postmaster(void)
{
int i;
int j = 0;
header(_("starting postmaster"));
if (!test_single_node) {
/* Start GTM */
start_gtm();
start_my_node(0, COORD, true, false, upgrade_from);
for (i = 1; i < myinfo.co_num; i++) {
start_my_node(i, COORD, false, false, upgrade_from);
}
for (i = 0; i < myinfo.dn_num; i++) {
start_my_node(i, DATANODE, false, standby_defined, upgrade_from);
}
} else {
assert(0 == myinfo.co_num && 1 == myinfo.dn_num);
start_single_node();
}
pg_usleep(10000000L);
/*
* Wait till postmaster is able to accept connections (normally only a
* second or so, but Cygwin is reportedly *much* slower). Don't wait
* forever, however.
*/
/* Check node failure */
for (i = 0; i < myinfo.co_num; i++) {
check_node_fail(i, COORD);
}
for (i = 0; i < myinfo.dn_num; i++) {
check_node_fail(i, DATANODE);
}
if (j >= 60) {
/* If one node fails, all fail */
for (i = 0; i < myinfo.co_num; i++) {
kill_node(i, COORD);
}
for (i = 0; i < myinfo.dn_num; i++) {
kill_node(i, DATANODE);
}
for (i = 0; i < myinfo.shell_count; i++) {
kill_node(i, SHELL);
}
exit_nicely(2);
}
if (standby_defined) {
for (i = 0; i < myinfo.dn_num; i++) {
setup_datanode_replication(i);
}
}
postmaster_running = true;
#ifdef WIN64
/* need a series of two casts to convert HANDLE without compiler warning */
#define ULONGPID(x) (unsigned long)(unsigned long long)(x)
#else
#define ULONGPID(x) (unsigned long)(x)
#endif
/* Print info for each node */
for (i = 0; i < myinfo.co_num; i++)
printf(_("running on port %d with PID %lu for Coordinator %d\n"),
get_port_number(i, COORD),
ULONGPID(get_node_pid(i, COORD)),
i + 1);
for (i = 0; i < myinfo.dn_num; i++)
printf(_("running on port %d with PID %lu for Datanode %d\n"),
get_port_number(i, DATANODE),
ULONGPID(get_node_pid(i, DATANODE)),
i + 1);
}
int regression_main(int argc, char* argv[], init_function ifunc, test_function tfunc, diag_function dfunc)
{
#ifdef BUILD_BY_CMAKE
get_value_from_env(&code_base_src, "CODE_BASE_SRC");
get_value_from_cwd(&current_exe_dir);
get_value_from_env(&CMAKE_PGBINDIR, "PREFIX_HOME");
get_value_from_env(&CMAKE_LIBDIR, "PREFIX_HOME");
get_value_from_env(&CMAKE_PGSHAREDIR, "PREFIX_HOME");
printf("--------------------------------------------------------------------------------------");
printf("\ncode_base_src:%s\n", code_base_src);
printf("\ncurrent_exe_dir:%s\n", current_exe_dir);
printf("\nCMAKE_PGBINDIR:%s\n", CMAKE_PGBINDIR);
#endif
_stringlist* ssl = NULL;
int c;
int i;
int option_index;
char buf[MAXPGPATH * 4];
int coordnode_num = 3;
int datanode_num = 12;
int init_port = 25632;
bool keep_last_time_data = false;
bool run_test_case = true;
bool run_qunit = false;
char* qunit_module = "all";
char* qunit_level = "all";
bool override_installdir = false;
bool only_install = false;
int kk = 0;
struct timeval start_time;
struct timeval end_time;
double total_time;
struct timeval start_time_total;
struct timeval end_time_total;
(void)gettimeofday(&start_time_total, NULL);
static struct option long_options[] = {{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"dbname", required_argument, NULL, 1},
{"debug", no_argument, NULL, 2},
{"inputdir", required_argument, NULL, 3},
{"load-language", required_argument, NULL, 4},
{"max-connections", required_argument, NULL, 5},
{"encoding", required_argument, NULL, 6},
{"outputdir", required_argument, NULL, 7},
{"schedule", required_argument, NULL, 8},
{"temp-install", required_argument, NULL, 9},
{"no-locale", no_argument, NULL, 10},
{"top-builddir", required_argument, NULL, 11},
{"host", required_argument, NULL, 13},
{"port", required_argument, NULL, 14},
{"user", required_argument, NULL, 15},
{"psqldir", required_argument, NULL, 16},
{"dlpath", required_argument, NULL, 17},
{"create-role", required_argument, NULL, 18},
{"temp-config", required_argument, NULL, 19},
{"use-existing", no_argument, NULL, 20},
{"launcher", required_argument, NULL, 21},
{"load-extension", required_argument, NULL, 22},
{"extra-install", required_argument, NULL, 23},
{"noclean", no_argument, NULL, 24},
{"regconf", required_argument, NULL, 25},
{"hdfshostname", required_argument, NULL, 26},
{"hdfsport", required_argument, NULL, 27},
{"hdfscfgpath", required_argument, NULL, 28},
{"hdfsstoreplus", required_argument, NULL, 29},
{"obshostname", required_argument, NULL, 30},
{"obsbucket", required_argument, NULL, 31},
{"keep_last_data", required_argument, NULL, 32},
{"securitymode", no_argument, NULL, 33},
{"abs_gausshome", required_argument, NULL, 34},
{"ak", required_argument, NULL, 35},
{"sk", required_argument, NULL, 36},
{"inplace_upgrade", no_argument, NULL, 37},
{"init_database", no_argument, NULL, 38},
{"data_base_dir", required_argument, NULL, 39},
{"upgrade_from", required_argument, NULL, 40},
{"upgrade_script_dir", required_argument, NULL, 41},
{"ignore-exitcode", no_argument, NULL, 42},
{"keep-run", no_argument, NULL, 43},
{"parallel_initdb", no_argument, NULL, 44},
{"single_node", no_argument, NULL, 45},
{"qunit", no_argument, NULL, 46},
{"qunit_module", required_argument, NULL, 47},
{"qunit_level", required_argument, NULL, 48},
{"old_bin_dir", required_argument, NULL, 49},
{"grayscale_mode", no_argument, NULL, 50},
{"grayscale_full_mode", no_argument, NULL, 51},
{"uc", required_argument, NULL, 52},
{"ud", required_argument, NULL, 53},
{"upgrade_schedule", required_argument, NULL, 54},
{"platform", required_argument, NULL, 55},
{"g_aiehost", required_argument, NULL, 56},
{"g_aieport", required_argument, NULL, 57},
{"enable-segment", no_argument, NULL, 58},
{"client_logic_hook", required_argument, NULL, 59},
{"jdbc", no_argument, NULL, 60},
{"skip_environment_cleanup", no_argument, NULL, 61},
{NULL, 0, NULL, 0}
};
progname = get_progname(argv[0]);
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
#ifndef HAVE_UNIX_SOCKETS
/* no unix domain sockets available, so change default */
hostname = "localhost";
#endif
/*
* We call the initialization function here because that way we can set
* default parameters and let them be overwritten by the commandline.
*/
ifunc();
while ((c = getopt_long(argc, argv, "b:c:d:hp:r:s:V:n:w", long_options, &option_index)) != -1) {
switch (c) {
case 'h':
help();
exit_nicely(0);
/* fall through */
case 'V':
puts("pg_regress (PostgreSQL) " PG_VERSION);
exit_nicely(0);
/* fall through */
case 'd':
datanode_num = atoi(optarg);
break;
case 'c':
coordnode_num = atoi(optarg);
break;
case 'p':
init_port = atoi(optarg);
break;
case 'r':
kk = atoi(optarg);
if (kk == 2) {
only_install = true;
run_test_case = false;
} else if (kk == 0)
run_test_case = false;
else
run_test_case = true;
break;
case 'b':
temp_install = make_absolute_path(optarg);
override_installdir = true;
break;
case 'm':
if (strncmp(optarg, "tcp", 3) != 0)
comm_tcp_mode = false;
break;
case 's':
standby_defined = atoi(optarg);
break;
case 'w':
change_password = true;
break;
case 'n':
dop = atoi(optarg);
break;
case 1:
/*
* If a default database was specified, we need to remove it
* before we add the specified one.
*/
free_stringlist(&dblist);
split_to_stringlist(strdup(optarg), ", ", &dblist);
break;
case 2:
debug = true;
break;
case 3:
inputdir = strdup(optarg);
break;
case 4:
add_stringlist_item(&loadlanguage, optarg);
break;
case 5:
max_connections = atoi(optarg);
break;
case 6:
encoding = strdup(optarg);
break;
case 7:
outputdir = strdup(optarg);
break;
case 8:
add_stringlist_item(&schedulelist, optarg);
break;
case 9:
if (override_installdir == false)
temp_install = make_absolute_path(optarg);
break;
case 10:
nolocale = true;
break;
case 11:
top_builddir = strdup(optarg);
break;
case 13:
hostname = strdup(optarg);
break;
case 14:
port = atoi(optarg);
port_specified_by_user = true;
break;
case 15:
user = strdup(optarg);
break;
case 16:
/* "--psqldir=" should mean to use PATH */
if (strlen(optarg)) {
psqldir = strdup(optarg);
}
break;
case 17:
dlpath = strdup(optarg);
break;
case 18:
split_to_stringlist(strdup(optarg), ", ", &extraroles);
break;
case 19:
temp_config = strdup(optarg);
break;
case 20:
use_existing = true;
break;
case 21:
launcher = strdup(optarg);
break;
case 22:
add_stringlist_item(&loadextension, optarg);
break;
case 23:
add_stringlist_item(&extra_install, optarg);
break;
case 24:
clean = false;
break;
case 25:
pcRegConfFile = strdup(optarg);
break;
case 26:
hdfshostname = strdup(optarg);
break;
case 27:
hdfsport = strdup(optarg);
break;
case 28:
hdfscfgpath = strdup(optarg);
break;
case 29:
hdfsstoreplus = strdup(optarg);
break;
case 30:
obshostname = strdup(optarg);
break;
case 31:
obsbucket = strdup(optarg);
break;
case 32: {
char* tmp_str = NULL;
tmp_str = strdup(optarg);
if (pg_strncasecmp(tmp_str, "true", 4) == 0) {
keep_last_time_data = true;
} else {
keep_last_time_data = false;
}
free(tmp_str);
break;
}
case 33:
securitymode = true;
break;
case 34:
gausshomedir = strdup(optarg);
break;
case 35:
ak = strdup(optarg);
break;
case 36:
sk = strdup(optarg);
break;
case 37:
inplace_upgrade = true;
super_user_altered = false;
passwd_altered = false;
break;
case 38:
init_database = true;
break;
case 39:
data_base_dir = strdup(optarg);
break;
case 40:
upgrade_from = atoi(optarg);
break;
case 41:
upgrade_script_dir = strdup(optarg);
break;
case 42:
ignore_exitcode = true;
break;
case 43:
keep_run = true;
break;
case 44:
parallel_initdb = true;
break;
case 45: {
/* test single datanode mode */
test_single_node = true;
break;
}
case 46:
run_qunit = true;
break;
case 47:
qunit_module = strdup(optarg);
break;
case 48:
qunit_level = strdup(optarg);
break;
case 49:
old_bin_dir = strdup(optarg);
break;
case 50:
grayscale_upgrade = 0;
inplace_upgrade = true;
super_user_altered = false;
passwd_altered = false;
break;
case 51:
grayscale_upgrade = 1;
inplace_upgrade = true;
super_user_altered = false;
passwd_altered = false;
break;
case 52:
upgrade_cn_num = atoi(optarg);
if (upgrade_cn_num > 2 || upgrade_cn_num < 1) {
upgrade_cn_num = 1;
}
break;
case 53:
upgrade_dn_num = atoi(optarg);
if (upgrade_dn_num > 11 || upgrade_dn_num < 1) {
upgrade_dn_num = 4;
}
break;
case 54:
add_stringlist_item(&upgrade_schedulelist, optarg);
break;
case 55:
platform = strdup(optarg);
break;
case 56:
g_aiehost = strdup(optarg);
break;
case 57:
g_aieport = strdup(optarg);
break;
case 58:
g_enable_segment = true;
break;
case 59:
client_logic_hook = make_absolute_path(optarg);
break;
case 60:
printf("\n starting with jdbc\n");
use_jdbc_client = true;
to_create_jdbc_user = true;
break;
case 61:
is_skip_environment_cleanup = true;
break;
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), progname);
exit_nicely(2);
}
}
if (strcmp(platform, "openeuler_aarch64") == 0) {
grayscale_upgrade = -1;
inplace_upgrade = false;
}
if (run_test_case && run_qunit) {
fprintf(stderr, _("Can not run qunit and other check simultaneously\n"));
exit_nicely(2);
}
memset(g_stRegrConfItems.acFieldSepForAllText, '\0', sizeof(g_stRegrConfItems.acFieldSepForAllText));
memset(g_stRegrConfItems.acTuplesOnly, '\0', sizeof(g_stRegrConfItems.acTuplesOnly));
regrInitReplcPattStruct();
if (pcRegConfFile)
loadRegressConf(pcRegConfFile);
int result = initialize_myinfo(coordnode_num, datanode_num, init_port, keep_last_time_data, run_test_case);
if (result != 0) {
return -1;
}
// Upgrade check option
(void)check_upgrade_options();
/*
* if we still have arguments, they are extra tests to run
*/
while (argc - optind >= 1) {
add_stringlist_item(&extra_tests, argv[optind]);
optind++;
}
if (temp_install && !port_specified_by_user)
/*
* To reduce chances of interference with parallel installations, use
* a port number starting in the private range (49152-65535)
* calculated from the version number.
*/
{
port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
}
inputdir = make_absolute_path(inputdir);
outputdir = make_absolute_path(outputdir);
dlpath = make_absolute_path(dlpath);
data_base_dir = make_absolute_path(data_base_dir);
old_bin_dir = make_absolute_path(old_bin_dir);
upgrade_script_dir = make_absolute_path(upgrade_script_dir);
loginuser = get_id();
#if defined (__x86_64__)
(void)snprintf(pgbenchdir, MAXPGPATH, "%s/%s", ".", "data/pgbench/x86_64");
#elif defined (__aarch64__)
(void)snprintf(pgbenchdir, MAXPGPATH, "%s/%s", ".", "data/pgbench/aarch64");
#endif
/* Check thread local varieble's num */
check_global_variables();
/* Check macros like STREAMLAN/PGXC/IS_SINGLE_NODE */
check_pgxc_like_macros();
// Do not stop postmaster if we are told not to run test cases.
if (run_test_case)
(void)atexit(atexit_cleanup);
/*
* create regression.out regression.diff result
*/
open_result_files();
// Initialize environment variables, including but not limited to LD_LIBRARY_PATH, PATH
initialize_environment();
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
unlimit_core_size();
#endif
// temp_install != NULL; Whether to specify the installation directory
if (temp_install) {
/*
* Prepare the temp installation
*/
if (!top_builddir) {
fprintf(stderr, _("--top-builddir must be specified when using --temp-install\n"));
exit_nicely(2);
}
if (!directory_exists(temp_install)) {
myinfo.keep_data = false;
}
#ifndef ENABLE_LLT
if (myinfo.keep_data) {
/* "make install" */
#ifndef WIN32_ONLY_COMPILER
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE
"\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install -sj > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
makeprog,
top_builddir,
temp_install,
outputdir);
#else
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE
"perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
top_builddir,
temp_install,
outputdir);
#endif
if (system(buf)) {
fprintf(stderr,
_("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
}
#endif
if (myinfo.keep_data == false) {
if (only_install == false) {
#ifndef ENABLE_LLT
if (directory_exists(temp_install)) {
header(_("removing existing temp installation"));
(void)rmtree(temp_install, true);
}
header(_("creating temporary installation"));
/* make the temp install top directory */
make_directory(temp_install);
#endif
/* and a directory for log files */
(void)snprintf(buf, sizeof(buf), "%s/log", outputdir);
if (!directory_exists(buf)) {
make_directory(buf);
}
#ifdef ENABLE_LLT
if (directory_exists(temp_install)) {
snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "rm -rf %s/coordinator* > %s/log/install.log 2>&1" SYSTEMQUOTE,
temp_install,
outputdir);
header("%s", buf);
if (system(buf)) {
fprintf(stderr,
_("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: "
"%s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "rm -rf %s/data* > %s/log/install.log 2>&1" SYSTEMQUOTE,
temp_install,
outputdir);
header("%s", buf);
if (system(buf)) {
fprintf(stderr,
_("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: "
"%s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
}
#endif
}
#ifndef ENABLE_LLT
/* "make install" */
#ifdef BUILD_BY_CMAKE
(void)snprintf_s(buf, sizeof(buf), sizeof(buf) - 1,
SYSTEMQUOTE "cd %s && \"%s\" DESTDIR=\"%s/install\" install -j >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
current_exe_dir, makeprog, temp_install, outputdir);
printf("cd %s && \"%s\" DESTDIR=\"%s/install\" install -j >> \"%s/log/install.log\" 2>&1\n", \
current_exe_dir, makeprog, temp_install, outputdir);
#elif !defined(WIN32_ONLY_COMPILER)
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE
"\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install -sj > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
makeprog,
top_builddir,
temp_install,
outputdir);
#else
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE
"perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
top_builddir,
temp_install,
outputdir);
#endif
if (system(buf)) {
fprintf(stderr,
_("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
} else if (only_install)
exit_nicely(2);
for (ssl = extra_install; ssl != NULL; ssl = ssl->next) {
#ifndef WIN32_ONLY_COMPILER
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE
"\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
makeprog,
top_builddir,
ssl->str,
temp_install,
outputdir);
#else
fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n"), progname);
exit_nicely(2);
#endif
if (system(buf)) {
fprintf(stderr,
_("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"),
progname,
outputdir,
buf);
exit_nicely(2);
}
}
#endif
if ((!inplace_upgrade && grayscale_upgrade == -1) || init_database) {
/* initdb */
header(_("initializing database system"));
init_gtm();
/* Initialize nodes */
if (parallel_initdb)
initdb_node_info_parallel(standby_defined);
else
initdb_node_info(standby_defined);
} else {
/* copy base data and binary for upgrade */
(void)snprintf(buf,
sizeof(buf),
SYSTEMQUOTE "rm -rf %s/data* && rm -rf %s/coordinator* &&"
"rm -rf %s/data_base && rm -rf %s/coordinator* &&"
" rm -rf %s/bin/ && rm -rf %s/etc/ && rm -rf %s/include/ &&"
" rm -rf %s/jdk/ && rm -rf %s/lib/ && rm -rf %s/share/ &&"
" tar -xpf %s/%s/data_base.tar.gz -C %s &&"
" tar -xpf %s/%s/bin_base.tar.gz -C %s " SYSTEMQUOTE,
temp_install,
temp_install,
data_base_dir,
data_base_dir,
temp_install,
temp_install,
temp_install,
temp_install,
temp_install,
temp_install,
data_base_dir,
platform,
temp_install,
data_base_dir,
platform,
temp_install);
header("%s", buf);
if (system(buf)) {
fprintf(stderr, _("Failed to copy base data for upgrade.\nCommand was: %s\n"), buf);
exit_nicely(2);
}
// The old binary installation is complete, switch to the old binary environment variable
setBinAndLibPath(true);
}
/* If only init database for inplace upgrade, we are done. */
if (init_database) {
cleanup_environment();
return 0;
}
/*
* Adjust the default postgresql.conf as needed for regression
* testing. The user can specify a file to be appended; in any case we
* set max_prepared_transactions to enable testing of prepared xacts.
* (Note: to reduce the probability of unexpected shmmax failures,
* don't set max_prepared_transactions any higher than actually needed
* by the prepared_xacts regression test.)
* Update configuration file of each node with user-defined options
* and 2PC related information.
* PGXCTODO: calculate port of GTM before setting configuration files
*/
if (!(inplace_upgrade || grayscale_upgrade != -1)) {
initdb_node_config_file(standby_defined);
}
}
/*Execute shell cmds in source files*/
exec_cmds_from_inputfiles();
/*
* Start the temp postmaster: 3C12D
*/
(void)start_postmaster();
if (inplace_upgrade || grayscale_upgrade != -1) {
checkProcInsert();
setup_super_user();
super_user_altered = true;
// Execute the upgrade script in the old bin
(void)snprintf_s(buf,
sizeof(buf),
sizeof(buf) - 1,
SYSTEMQUOTE "python %s/upgradeCheck.py -u -p %d -f %d -s %s%sgsql" SYSTEMQUOTE,
upgrade_script_dir,
get_port_number(0, DATANODE),
upgrade_from,
psqldir ? psqldir : "",
psqldir ? "/" : "");
header("%s", buf);
(void)gettimeofday(&end_time, NULL);
if (regr_system(buf)) {
fprintf(stderr, _("Failed to exec upgrade.\nCommand was: %s\n"), buf);
exit_nicely(2);
}
// start with new bin
(void)gettimeofday(&start_time, NULL);
header(_("shutting down postmaster"));
(void)stop_postmaster();
setBinAndLibPath(false);
if (grayscale_upgrade == 1) {
(void)start_postmaster();
header(_("sleeping"));
pg_usleep(10000000L);
(void)gettimeofday(&end_time, NULL);
total_time = (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_usec - start_time.tv_usec) * 0.000001;
printf("It takes %fs to restart cluster.\n", total_time);
/* Execute the post upgrade script */
(void)snprintf_s(buf,
sizeof(buf),
sizeof(buf) - 1,
SYSTEMQUOTE "python %s/upgradeCheck.py --post -p %d -f %d -s %s%sgsql" SYSTEMQUOTE,
upgrade_script_dir,
get_port_number(0, DATANODE),
upgrade_from,
psqldir ? psqldir : "",
psqldir ? "/" : "");
header("%s", buf);
(void)gettimeofday(&end_time, NULL);
if (regr_system(buf)) {
fprintf(stderr, _("Failed to exec post-upgrade.\nCommand was: %s\n"), buf);
exit_nicely(2);
}
/* Execute the post rollback script */
(void)snprintf_s(buf,
sizeof(buf),
sizeof(buf) - 1,
SYSTEMQUOTE "python %s/upgradeCheck.py --rollback -p %d -f %d -s %s%sgsql" SYSTEMQUOTE,
upgrade_script_dir,
get_port_number(0, DATANODE),
upgrade_from,
psqldir ? psqldir : "",
psqldir ? "/" : "");
header("%s", buf);
(void)gettimeofday(&end_time, NULL);
if (regr_system(buf)) {
fprintf(stderr, _("Failed to exec post-rollback.\nCommand was: %s\n"), buf);
exit_nicely(2);
}
// start with old bin
restartPostmaster(true);
/* Execute the rollback script */
(void)snprintf_s(buf,
sizeof(buf),
sizeof(buf) - 1,
SYSTEMQUOTE "python %s/upgradeCheck.py -o -p %d -f %d -s %s%sgsql" SYSTEMQUOTE,
upgrade_script_dir,
get_port_number(0, DATANODE),
upgrade_from,
psqldir ? psqldir : "",
psqldir ? "/" : "");
header("%s", buf);
(void)gettimeofday(&end_time, NULL);
if (regr_system(buf)) {
fprintf(stderr, _("Failed to exec post-rollback.\nCommand was: %s\n"), buf);
exit_nicely(2);
}
// Execute the upgrade script in the old bin
(void)snprintf_s(buf,
sizeof(buf),
sizeof(buf) - 1,
SYSTEMQUOTE "python %s/upgradeCheck.py -u -p %d -f %d -s %s%sgsql" SYSTEMQUOTE,
upgrade_script_dir,
get_port_number(0, DATANODE),
upgrade_from,
psqldir ? psqldir : "",
psqldir ? "/" : "");
header("%s", buf);
(void)gettimeofday(&end_time, NULL);
if (regr_system(buf)) {
fprintf(stderr, _("Failed to exec upgrade.\nCommand was: %s\n"), buf);
exit_nicely(2);
}
// start with new bin
(void)gettimeofday(&start_time, NULL);
header(_("shutting down postmaster"));
(void)stop_postmaster();
setBinAndLibPath(false);
}
initdb_node_config_file(standby_defined);
(void)start_postmaster();
header(_("sleeping"));
pg_usleep(10000000L);
(void)gettimeofday(&end_time, NULL);
total_time = (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_usec - start_time.tv_usec) * 0.000001;
printf("It takes %fs to restart cluster.\n", total_time);
/* Execute the post upgrade script */
(void)snprintf_s(buf,
sizeof(buf),
sizeof(buf) - 1,
SYSTEMQUOTE "python %s/upgradeCheck.py --post -p %d -f %d -s %s%sgsql" SYSTEMQUOTE,
upgrade_script_dir,
get_port_number(0, DATANODE),
upgrade_from,
psqldir ? psqldir : "",
psqldir ? "/" : "");
header("%s", buf);
(void)gettimeofday(&end_time, NULL);
if (regr_system(buf)) {
fprintf(stderr, _("Failed to exec post-upgrade.\nCommand was: %s\n"), buf);
exit_nicely(2);
}
header(_("shutting down postmaster"));
(void)stop_postmaster();
upgrade_from = 0;
// switch to the new binary
setBinAndLibPath(false);
(void)start_postmaster();
header(_("sleeping"));
pg_usleep(10000000L);
}
if (!test_single_node) {
/* Postmaster is finally running, so set up connection information on Coordinators */
if (myinfo.keep_data == false) {
setup_connection_information(standby_defined);
} else {
pg_usleep(2000000L);
rebuild_node_group();
for (ssl = dblist; ssl; ssl = ssl->next)
drop_database_if_exists(ssl->str);
for (ssl = extraroles; ssl; ssl = ssl->next)
drop_role_if_exists(ssl->str);
_stringlist* tmpdblist = NULL;
_stringlist* tmprolelist = NULL;
add_stringlist_item(&tmpdblist, "cstore_vacuum_full_db");
add_stringlist_item(&tmpdblist, "test_sort");
add_stringlist_item(&tmpdblist, "td_db");
add_stringlist_item(&tmpdblist, "my_db1");
add_stringlist_item(&tmpdblist, "td_db_char");
add_stringlist_item(&tmpdblist, "td_db_char_cast");
add_stringlist_item(&tmpdblist, "tdtest");
add_stringlist_item(&tmpdblist, "td_db_char_bulkload");
add_stringlist_item(&tmpdblist, "testaes1");
add_stringlist_item(&tmpdblist, "db_gin_utf8");
add_stringlist_item(&tmpdblist, "music");
add_stringlist_item(&tmpdblist, "db_ascii_bulkload_compatibility_test");
add_stringlist_item(&tmpdblist, "db_latin1_bulkload_compatibility_test");
add_stringlist_item(&tmpdblist, "db_gbk_bulkload_compatibility_test");
add_stringlist_item(&tmpdblist, "db_eucjis2004_bulkload_compatibility_test");
add_stringlist_item(&tmpdblist, "td_format_db");
add_stringlist_item(&tmpdblist, "test_parallel_db");
for (ssl = tmpdblist; ssl; ssl = ssl->next)
drop_database_if_exists(ssl->str);
add_stringlist_item(&tmprolelist, "temp_reset_user");
add_stringlist_item(&tmprolelist, "cstore_role");
add_stringlist_item(&tmprolelist, "test_llt");
add_stringlist_item(&tmprolelist, "alter_llt2");
add_stringlist_item(&tmprolelist, "llt_1");
add_stringlist_item(&tmprolelist, "llt_3");
add_stringlist_item(&tmprolelist, "llt_5");
add_stringlist_item(&tmprolelist, "role_pwd_complex");
add_stringlist_item(&tmprolelist, "dfm");
add_stringlist_item(&tmprolelist, "ad1");
add_stringlist_item(&tmprolelist, "ad2");
add_stringlist_item(&tmprolelist, "hs");
add_stringlist_item(&tmprolelist, "testdb_new");
for (ssl = tmprolelist; ssl; ssl = ssl->next)
drop_role_if_exists(ssl->str);
}
}
gen_startstop_script();
} else {
/*
* Using an existing installation, so may need to get rid of
* pre-existing database(s) and role(s)
*/
if (myinfo.run_check == true && !use_existing) {
for (ssl = dblist; ssl; ssl = ssl->next)
drop_database_if_exists(ssl->str);
for (ssl = extraroles; ssl; ssl = ssl->next)
drop_role_if_exists(ssl->str);
}
}
if (myinfo.run_check == true) {
(void)gettimeofday(&end_time_total, NULL);
total_time = (end_time_total.tv_sec - start_time_total.tv_sec) + (end_time_total.tv_usec - start_time_total.tv_usec) * 0.000001;
printf("Preparation before the test case execution takes %fs..\n", total_time);
if (!use_existing) {
for (ssl = dblist; ssl; ssl = ssl->next) {
create_database(ssl->str);
}
if (to_create_jdbc_user) {
create_jdbc_user(dblist);
}
for (ssl = extraroles; ssl; ssl = ssl->next) {
create_role(ssl->str, dblist);
}
}
/*
* Ready to run the tests
*/
header(_("running regression test queries"));
char* old_gausshome = NULL;
char env_path[MAXPGPATH + sizeof("GAUSSHOME=")];
// Query retry need env 'GAUSSHOME'.
old_gausshome = gs_getenv_r("GAUSSHOME");
/* keep the old shell GAUSSHOME evn */
(void)snprintf(env_path, sizeof(env_path), "OLDGAUSSHOME=%s", old_gausshome);
char* old_gausshome_env = strdup(env_path);
gs_putenv_r(old_gausshome_env);
/* pg regress env */
(void)snprintf(env_path, sizeof(env_path), "GAUSSHOME=%s/../", psqldir);
gs_putenv_r(env_path);
(void)gettimeofday(&start_time, NULL);
if (!g_bEnableDiagCollection)
dfunc = NULL;
for (ssl = schedulelist; ssl != NULL; ssl = ssl->next) {
run_schedule(ssl->str, tfunc, dfunc);
}
for (ssl = extra_tests; ssl != NULL; ssl = ssl->next) {
run_single_test(ssl->str, tfunc, dfunc);
}
(void)gettimeofday(&end_time, NULL);
// Restore the env 'GAUSSHOME'
//
if (old_gausshome) {
(void)snprintf(env_path, sizeof(env_path), "GAUSSHOME=%s", old_gausshome);
gs_putenv_r(env_path);
}
total_time = (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_usec - start_time.tv_usec) * 0.000001;
/*
* Shut down temp installation's postmaster
*/
if (temp_install && clean) {
header(_("shutting down postmaster"));
stop_postmaster();
}
fclose(logfile);
/*
* Emit nice-looking summary message
*/
if (fail_count == 0 && fail_ignore_count == 0)
(void)snprintf(buf, sizeof(buf), _(" All %d tests passed. "), success_count);
else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */
(void)snprintf(buf,
sizeof(buf),
_(" %d of %d tests passed, %d failed test(s) ignored. "),
success_count,
success_count + fail_ignore_count,
fail_ignore_count);
else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */
(void)snprintf(buf, sizeof(buf), _(" %d of %d tests failed. "), fail_count, success_count + fail_count);
else
/* fail_count>0 && fail_ignore_count>0 */
(void)snprintf(buf,
sizeof(buf),
_(" %d of %d tests failed, %d of these failures ignored. "),
fail_count + fail_ignore_count,
success_count + fail_count + fail_ignore_count,
fail_ignore_count);
(void)putchar('\n');
for (i = strlen(buf); i > 0; i--) {
(void)putchar('=');
}
printf("\n%s\n", buf);
printf(" Total Time: %fs\n", total_time);
for (i = strlen(buf); i > 0; i--) {
(void)putchar('=');
}
(void)putchar('\n');
(void)putchar('\n');
if (file_size(difffilename) > 0) {
printf(_("The differences that caused some tests to fail can be viewed in the\n"
"file \"%s\". A copy of the test summary that you see\n"
"above is saved in the file \"%s\".\n\n"),
difffilename,
logfilename);
} else {
unlink(difffilename);
unlink(logfilename);
}
if (!keep_run && fail_count != 0) {
exit_nicely(1);
}
}
if (run_qunit == true) {
char buf[MAXPGPATH * 2];
int r;
/* On Windows, system() seems not to force fflush, so... */
fflush(stdout);
fflush(stderr);
int ret = snprintf_s(buf,
sizeof(buf),
sizeof(buf),
"../QUnit/src/qunit -logdir ../QUnit/test/log -cp %d -dp %d -m %s -l %s",
init_port,
myinfo.dn_port[0],
qunit_module,
qunit_level);
securec_check_ss_c(ret, "", "");
fprintf(stderr, _("\n%s\n"), buf);
r = regr_system(buf);
if (r != 0) {
fprintf(stderr, _("\n%s: Failed to Run QUnit : exit code was %d\n"), progname, r);
exit(2);
}
if (temp_install && clean) {
header(_("shutting down postmaster"));
stop_postmaster();
}
}
cleanup_environment();
return 0;
}
void checkProcInsert()
{
int result = 0;
char cmd[MAXPGPATH] = {'\0'};
#ifdef BUILD_BY_CMAKE
(void)snprintf(
cmd, sizeof(cmd), "grep -E \"DATA.*\\(.*insert\" %s/include/catalog/pg_proc.h | wc -l", code_base_src);
#else
(void)snprintf(
cmd, sizeof(cmd), "grep -E \"DATA.*\\(.*insert\" %s/../../../include/catalog/pg_proc.h | wc -l", temp_install);
#endif
FILE* fstream = NULL;
char buf[50];
memset(buf, 0, sizeof(buf));
fstream = popen(cmd, "r");
if (NULL != fgets(buf, sizeof(buf), fstream)) {
result = atoi(buf);
}
if (result != 0) {
fprintf(stderr,
_("\n%s: can not add buildin function in the pg_proc.h;\n"
"please add it in the Code/src/backend/catalog/pg_builtin_proc.cpp\n"),
progname);
exit(2);
}
if (NULL != fstream) {
pclose(fstream);
}
}
// only for restart during grayscale upgrade
void restartPostmaster(bool isOld)
{
struct timeval start_time;
struct timeval end_time;
double total_time;
(void)gettimeofday(&start_time, NULL);
header(_("shutting down postmaster"));
(void)stop_postmaster();
setBinAndLibPath(isOld);
(void)start_postmaster();
header(_("sleeping"));
pg_usleep(10000000L);
(void)gettimeofday(&end_time, NULL);
total_time = (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_usec - start_time.tv_usec) * 0.000001;
printf("It takes %fs to restart cluster.\n", total_time);
}