/*-------------------------------------------------------------------------
 *
 * 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 = 10;
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;

/* for shared storage test use */
static bool enable_ss = false;
static bool ss_standby_read = 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_sp5_x86_64";

/* client logic jdbc run regression tests */
static bool use_jdbc_client = false;
static bool use_ecpg = 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 char *g_db_compatibility = "A";

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"));
        free((char*)data_folder);
        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));
            free((char*)data_folder);
            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;
        if (enable_ss) {
            printf("stop DSS now!\n");
            int ret = system("ps ux | grep dssserver | grep -v grep | awk '{print $2}' | xargs kill -9");
            if (ret != 0) {
                fprintf(stderr, _("\n could not stop dss: exit code was %d\n"), ret);
                exit(2); /* not exit(), that would be recursive */
            }
        }
    }
}

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;
    free((char*)data_folder);
}

/* 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 single datanode for test */
static void start_ss_node(int i)
{
    char buf[MAXPGPATH * 4];
    int port_number = myinfo.dn_port[i];
    char* data_folder = get_node_info_name(i, DATANODE, false);

    (void)snprintf(buf,
                   sizeof(buf),
                   SYSTEMQUOTE "\"%s/gaussdb\" -p %d -D \"%s/%s\" -c comm_sctp_port=%d -c comm_control_port=%d -c log_statement=all -c logging_collector=true -c "
                               "\"listen_addresses=%s\" & > \"%s/log/postmaster_%s.log\" 2>&1" SYSTEMQUOTE,
                               bindir,
                               port_number,
                               temp_install,
                               data_folder,
                               port_number + 30,
                               port_number + 40,
                               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("start %dth node: node_pid %d.\n", i, datanode_pid);

    myinfo.dn_pid[i] = 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* node_name = get_node_info_name(i, is_cn ? COORD : DATANODE, true);
    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,
        node_name,
        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);
    free(node_name);
}

/*
 * 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];

    char* data_folder = get_node_info_name(0, DATANODE, false);
    errno_t rc = snprintf_s(dstDatadir, sizeof(dstDatadir), sizeof(dstDatadir) - 1, "%s/%s/pg_copydir",
        temp_install, data_folder);
    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, data_folder);
    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);
    }
    free(data_folder);
}

/*
 * 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);
        char* data_folder2 = get_node_info_name(i, COORD, true);
        (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,
            data_folder2,
            init_database ? "-U upcheck" : "",
            temp_install,
            data_folder,
            datadir,
            debug ? " --debug" : "",
            nolocale ? " --no-locale" : "",
            outputdir);
        free(data_folder);
        free(data_folder2);
        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 ss_extra_args[MAXPGPATH] = {0};

        (void)snprintf(ss_extra_args,
            sizeof(ss_extra_args),
            SYSTEMQUOTE "--vgname=\"+data,+log\" --enable-dss --dms_url=\"%s\" -I %d "
                "--socketpath=\"UDS:%s/dss_home%d/.dss_unix_d_socket\"" SYSTEMQUOTE,
            ss_standby_read ? "0:127.0.0.1:1611,1:127.0.0.1:1711" : "0:127.0.0.1:1611",
            i,
            temp_install,
            i);

        char* data_folder = get_node_info_name(i, DATANODE, false);
        char* data_folder2 = get_node_info_name(i, DATANODE, true);
        (void)snprintf(buf,
            sizeof(buf),
            SYSTEMQUOTE "\"%s/gs_initdb\" --nodename %s %s -w \"gauss@123\" -D \"%s/%s\" -L \"%s\" %s --noclean%s%s > "
                        "\"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
            bindir,
            data_folder2,
            init_database ? "-U upcheck" : "",
            temp_install,
            data_folder,
            datadir,
            enable_ss ? ss_extra_args : "",
            debug ? " --debug" : "",
            nolocale ? " --no-locale" : "",
            outputdir);
        free(data_folder);
        free(data_folder2);
        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);
        }
    }

    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);
        char* node_name   = get_node_info_name(i, DATANODE, true);
        (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,
            node_name,
            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);
        free(node_name);
    }
}

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 (ss_standby_read) {
            if (i == 0) {
                (void)snprintf(buf, sizeof(buf),
                    "replconninfo1 = 'localhost=127.0.0.1 localport=%d localheartbeatport=%d remotehost=127.0.0.1 "
                    "remoteport=%d remoteheartbeatport=%d'\n",
                    myinfo.dn_primary_port[i], myinfo.dn_primary_port[i] + 1, myinfo.dn_primary_port[i + 1],
                    myinfo.dn_primary_port[i + 1] + 1);
                fputs(buf, pg_conf);
            } else {
                (void)snprintf(buf, sizeof(buf),
                    "replconninfo1 = 'localhost=127.0.0.1 localport=%d localheartbeatport=%d remotehost=127.0.0.1 "
                    "remoteport=%d remoteheartbeatport=%d'\n",
                    myinfo.dn_primary_port[i], myinfo.dn_primary_port[i] + 1, myinfo.dn_primary_port[0],
                    myinfo.dn_primary_port[0] + 1);
                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\" -d \"%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);
                char* node_name = get_node_info_name(0, DATANODE, true);
                replace_string(line, "@datanode1@", node_name);
                free(node_name);
                node_name = get_node_info_name(1, DATANODE, true); 
                replace_string(line, "@datanode2@", node_name);
                free(node_name);
                node_name=get_node_info_name(0, COORD, true);
                replace_string(line, "@coordinator1@", node_name);
                free(node_name);
                node_name = get_node_info_name(1, COORD, true);
                replace_string(line, "@coordinator2@", node_name);
                free(node_name);
                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;
}

static int countTestLines(const char* filename){
    char command[1024];
    snprintf(command, sizeof(command), "grep -c '^test:' %s", filename);

    FILE *f = popen(command, "r");
    if(f == NULL){
       fprintf(stderr, "Failed to execute command.\n");
       return -1;
    }

    char result[10];
    fgets(result, sizeof(result), f);
    pclose(f);

    int count = atoi((result));
    return count;
}

/*
 * 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;
    int all_test_lines = 0;
    int done_test_lines = 0;
    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;

    all_test_lines = countTestLines(schedule);
    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(%d/%d) %-24s .... "), ++done_test_lines, all_test_lines, tests[0]);
                }
                makeNestedDirectory(tests[0]);

                REGR_START_TIMER_TEMP(0);

                if (!ss_standby_read) {
                    pids[0] = (tfunc)(tests[0], &resultfiles[0], &expectfiles[0], &tags[0], use_jdbc_client);
                } else {
                    pids[0] = (tfunc)(tests[0], &resultfiles[0], &expectfiles[0], &tags[0], false);
                }
            }

            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 */
                    if (!ss_standby_read) {
                        pids[i] = (tfunc)(tests[i], &resultfiles[i], &expectfiles[i], &tags[i], use_jdbc_client);
                    } else {
                        pids[i] = (tfunc)(tests[i], &resultfiles[i], &expectfiles[i], &tags[i], false);
                    }
                    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);

                    if (!ss_standby_read) {
                        pids[i] = (tfunc)(tests[i], &resultfiles[i], &expectfiles[i], &tags[i], use_jdbc_client);
                    } else {
                        pids[i] = (tfunc)(tests[i], &resultfiles[i], &expectfiles[i], &tags[i], false);
                    }

                    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)(%d/%d): "), num_tests, ++done_test_lines, all_test_lines);
            }

            wait_for_tests(pids, statuses, tests, num_tests);
            REGR_PRINT_ONEGROUP_ELAPSED_TIME;
            status_end();

            g_uiCurLineBufIdx = 0;
        }
        if (ss_standby_read) {
            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], true);
                }

                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], true);
                        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], true);

                        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;
            }
            num_tests *= 2;
        }

        /* 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 237

#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_buf[CMAKE_CMD_BUF_LEN+1];
    snprintf(cmd_buf, CMAKE_CMD_BUF_LEN,"find %s/src/common/backend/ -name \"*.cpp\" | \
        xargs grep \"THR_LOCAL\" | grep -v \"extern THR_LOCAL\" | wc -l", top_builddir);
    char* cmd = cmd_buf;
#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
    snprintf(cmd_buf, CMAKE_CMD_BUF_LEN,"find  %s/src/gausskernel/ -name \"*.cpp\" | \
    xargs grep \"THR_LOCAL\" | grep -v \"extern THR_LOCAL\" | wc -l", top_builddir);
    cmd = cmd_buf;
#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 1393
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_buf[1001];
    snprintf(cmd_buf, 1000,"find %s/src/common/backend/ %s/src/gausskernel/ -name \"*.cpp\" | \
    xargs grep -e \"#ifdef STREAMPLAN\" -e \"#ifdef PGXC\"  -e \"IS_SINGLE_NODE\" | \
    wc -l", top_builddir, top_builddir);
    char* cmd = cmd_buf;
#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);
    }
    if (ss_standby_read) {
        rc = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s/results/ss_wr", outputdir);
        securec_check_ss_c(rc, "", "");
        if (!directory_exists(file)) {
            make_directory(file);
        }
        rc = snprintf_s(file, sizeof(file), sizeof(file) - 1, "%s/results/ss_r", 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='%s' TEMPLATE=TEMPLATE0 ENCODING='%s'%s",
            dbname,
            g_db_compatibility,
            encoding,
            (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
    else
        psql_command("postgres",
            "CREATE DATABASE \"%s\" DBCOMPATIBILITY='%s' TEMPLATE=TEMPLATE0%s",
            dbname,
            g_db_compatibility,
            (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_ecpg(const char* rolename, const _stringlist* granted_dbs)
{
    header(_("creating role \"%s\""), rolename);
    psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN PASSWORD 'connectpw@123'", 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 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(_("  --enable_ss               test shared storage mode\n"));
    printf(_("  --ss_standby_read         test standby read in shared storage mode\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\n"));
    printf(_("  --jdbc          enable jdbc regression test\n"));
    printf(_("  --ecpg          enable ecpg regression test\n"));
    printf(_("  --dbcmpt=DBCMPT           create regression database with DBCMPT(default \"A\")\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 || ss_standby_read) {
        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 || ss_standby_read) {
            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 {
        if (!enable_ss) {
            assert(0 == myinfo.co_num && 1 == myinfo.dn_num);
            start_single_node();
        } else {
            start_ss_node(0);
            if (ss_standby_read) {
                start_ss_node(1);
            }
        }
    }

    if (!enable_ss) {
        pg_usleep(10000000L);
    } else {
        pg_usleep(100000000L);
    }

    /*
     * 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},
        {"ecpg", no_argument, NULL, 62},
        {"dbcmpt", required_argument, NULL, 63},
        {"enable_ss", no_argument, NULL, 64},
        {"ss_standby_read", no_argument, NULL, 65},
        {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(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(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;
            case 62:
                printf("\n starting with ecpg\n");
                use_ecpg = true;
                break;
            case 63:
                g_db_compatibility = strdup(optarg);
                break;
            case 64:
                enable_ss = true;
                break;
            case 65:
                ss_standby_read = true;
                enable_ss = 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 (!enable_ss) {
                    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) {
                if (use_ecpg)
                    create_role_ecpg(ssl->str, dblist);
                else
                    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;

        test_function tmpfunc = tfunc;
        if (ss_standby_read) {
            tmpfunc = psql_ss_start_test;
        }
        for (ssl = schedulelist; ssl != NULL; ssl = ssl->next) {
            run_schedule(ssl->str, tmpfunc, dfunc);
        }

        for (ssl = extra_tests; ssl != NULL; ssl = ssl->next) {
            run_single_test(ssl->str, tmpfunc, 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);
}