blob: 3ad027c87ae4c3ecfeda5b8d290317ad972d0730 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <iomanip>
#include <regex>
#include <vector>
#include "hawq_config.h"
#include "sql_util.h"
#include "string_util.h"
#ifdef __linux__
#include <limits.h>
#include <unistd.h>
#elif __APPLE__
#include <libproc.h>
#endif
using std::string;
namespace hawq {
namespace test {
SQLUtility::SQLUtility(SQLUtilityMode mode)
: testRootPath(getTestRootPath()),
test_info(::testing::UnitTest::GetInstance()->current_test_info()) {
auto getConnection = [&]() {
string user = HAWQ_USER;
if (user.empty()) {
struct passwd *pw;
uid_t uid = geteuid();
pw = getpwuid(uid);
user.assign(pw->pw_name);
}
conn.reset(new hawq::test::PSQL(HAWQ_DB, HAWQ_HOST, HAWQ_PORT, user,
HAWQ_PASSWORD));
};
getConnection();
if (mode == MODE_SCHEMA || mode == MODE_SCHEMA_NODROP ||
mode == MODE_MADLIB) {
schemaName = string(test_info->test_case_name()) + "_" + test_info->name();
databaseName = HAWQ_DB;
sql_util_mode = mode;
if (mode == MODE_SCHEMA || mode == MODE_MADLIB) {
exec("DROP SCHEMA IF EXISTS " + schemaName + " CASCADE");
exec("CREATE SCHEMA " + schemaName);
} else {
execIgnore("CREATE SCHEMA " + schemaName);
}
} else if (mode == MODE_MADLIB) {
schemaName = string(test_info->test_case_name()) + "_" + test_info->name();
databaseName = HAWQ_DB;
exec("DROP SCHEMA IF EXISTS " + schemaName + " CASCADE");
exec("CREATE SCHEMA " + schemaName);
} else {
schemaName = HAWQ_DEFAULT_SCHEMA;
databaseName =
"db_" + string(test_info->test_case_name()) + "_" + test_info->name();
std::transform(databaseName.begin(), databaseName.end(),
databaseName.begin(), ::tolower);
exec("DROP DATABASE IF EXISTS " + databaseName);
exec("CREATE DATABASE " + databaseName);
sql_util_mode = MODE_DATABASE;
}
}
SQLUtility::SQLUtility(const std::string &db, const std::string &host,
const std::string &port, const std::string &user)
: testRootPath(getTestRootPath()),
test_info(::testing::UnitTest::GetInstance()->current_test_info()) {
conn.reset(new hawq::test::PSQL(db, host, port, user, HAWQ_PASSWORD));
schemaName = HAWQ_DEFAULT_SCHEMA;
databaseName = db;
sql_util_mode = MODE_SCHEMA_NODROP;
}
SQLUtility::~SQLUtility() {
if (!test_info->result()->Failed()) {
//--------------------------------------------------------------------------
// This is a temporary work around to sleep a short time window in order to
// wait for the quit of query dispatcher processes. Because each query
// dispatcher has one resource heart-beat thread to be joined before the
// exit, in worst case, that thread will sleep 100ms and consequently check
// the switch variable to complete the exiting logic. This may causes the
// error reporting that the database is still accessed by other users, when
// user drops database once finished using database.
//
// When we have that exit logic improved, we can remove this logic.
//--------------------------------------------------------------------------
usleep(200000);
if (schemaName != HAWQ_DEFAULT_SCHEMA &&
sql_util_mode != MODE_SCHEMA_NODROP) {
exec("DROP SCHEMA " + schemaName + " CASCADE");
}
if (sql_util_mode == MODE_DATABASE) {
exec("DROP DATABASE " + databaseName);
}
}
}
std::string SQLUtility::getStrCurTime() {
time_t secs = time(NULL);
struct tm *tm_cur = localtime(&secs);
std::string str_time = std::to_string(tm_cur->tm_year + 1900) + "-" +
std::to_string(tm_cur->tm_mon + 1) + "-" +
std::to_string(tm_cur->tm_mday) + " " +
std::to_string(tm_cur->tm_hour) + ":" +
std::to_string(tm_cur->tm_min) + ":" +
std::to_string(tm_cur->tm_sec);
return str_time;
}
std::ostream &SQLUtility::time_log() {
std::cout.flags(std::ios::left);
std::cout << std::setw(18) << SQLUtility::getStrCurTime() << " => ";
return std::cout;
}
std::string SQLUtility::getHost() { return conn->getHost(); }
std::string SQLUtility::getDbName() { return databaseName; }
std::string SQLUtility::getSchemaName() { return schemaName; }
void SQLUtility::setSchemaName(const std::string &name, bool isSwitch) {
schemaName = name;
if (!isSwitch) {
assert(name != HAWQ_DEFAULT_SCHEMA);
if (sql_util_mode != MODE_SCHEMA_NODROP) {
exec("DROP SCHEMA IF EXISTS " + schemaName + " CASCADE");
exec("CREATE SCHEMA " + schemaName);
} else {
execIgnore("CREATE SCHEMA " + schemaName);
}
}
}
std::string SQLUtility::getVersion() {
return getQueryResult("select version();");
}
// Set Host of connection value
void SQLUtility::setHost(const std::string &host) { conn->setHost(host); }
void SQLUtility::setDatabase(const std::string &db) { conn->setDatabase(db); }
void SQLUtility::exec(const string &sql) {
EXPECT_EQ(0, (conn->runSQLCommand(sql)).getLastStatus())
<< conn->getLastResult();
}
void SQLUtility::execIgnore(const string &sql) { conn->runSQLCommand(sql); }
string SQLUtility::execute(const string &sql, bool check) {
conn->runSQLCommand("SET SEARCH_PATH=" + schemaName + ";" + savedGUCValue + sql);
EXPECT_NE(conn.get(), nullptr);
if (check) {
EXPECT_EQ(0, conn->getLastStatus()) << sql << " " << conn->getLastResult();
return "";
}
return conn.get()->getLastResult();
}
bool SQLUtility::executeSql(const std::string &sql) {
conn->runSQLCommand("SET SEARCH_PATH=" + schemaName + ";" + savedGUCValue + sql);
EXPECT_NE(conn.get(), nullptr);
return !conn->getLastStatus();
}
void SQLUtility::executeExpectErrorMsgStartWith(const std::string &sql,
const std::string &errmsg) {
std::string errout = execute(sql, false);
EXPECT_STREQ(errmsg.c_str(), errout.substr(0, errmsg.size()).c_str());
}
void SQLUtility::executeIgnore(const string &sql) {
conn->runSQLCommand("SET SEARCH_PATH=" + schemaName + ";" + sql);
EXPECT_NE(conn.get(), nullptr);
}
void SQLUtility::query(const string &sql, int expectNum) {
const hawq::test::PSQLQueryResult &result = executeQuery(sql);
ASSERT_FALSE(result.isError()) << result.getErrorMessage();
EXPECT_EQ(expectNum, result.rowCount());
}
void SQLUtility::query(const string &sql, const string &expectStr) {
const hawq::test::PSQLQueryResult &result = executeQuery(sql);
ASSERT_FALSE(result.isError()) << result.getErrorMessage();
std::vector<std::vector<string> > resultString = result.getRows();
string resultStr;
for (auto row : result.getRows()) {
for (auto column : row) resultStr += column + "|";
resultStr += "\n";
}
EXPECT_EQ(expectStr, resultStr);
}
bool SQLUtility::checkAnsFile(const string &ansFileAbsPath,
const string &outFileAbsPath,
const string &initFile, const string &newSqlFile,
FilePath &fp) {
// initFile if any
string initFileAbsPath;
if (!initFile.empty()) {
initFileAbsPath = testRootPath + "/" + initFile;
if (!std::ifstream(initFileAbsPath)) {
EXPECT_TRUE(false) << initFileAbsPath << " doesn't exist";
return false;
}
fp = splitFilePath(initFileAbsPath);
// double check to avoid empty fileBaseName
if (fp.fileBaseName.empty()) {
EXPECT_TRUE(false) << initFileAbsPath << " is invalid";
return false;
}
} else {
initFileAbsPath = "";
}
string globalInitFileAbsPath;
globalInitFileAbsPath = testRootPath + "/lib/global_init_file";
bool is_sql_ans_diff =
conn->checkDiff(ansFileAbsPath, outFileAbsPath, true,
globalInitFileAbsPath, initFileAbsPath);
if (is_sql_ans_diff == false) {
// no diff, continue to delete the generated sql file
if (!newSqlFile.empty() && remove(newSqlFile.c_str())) {
EXPECT_TRUE(false) << "Error deleting file " << newSqlFile;
return false;
}
return true;
} else {
EXPECT_FALSE(is_sql_ans_diff) << conn->getDifFilePath(outFileAbsPath);
return false;
}
}
void SQLUtility::execSQLFile(const string &sqlFile, const string &ansFile,
const string &initFile, bool usingDefaultSchema,
bool printTupleOnly) {
FilePath fp;
// do precheck for sqlFile & ansFile
if (hawq::test::startsWith(sqlFile, "/") ||
hawq::test::startsWith(ansFile, "/"))
ASSERT_TRUE(false) << "For sqlFile and ansFile, relative path to feature "
"test root dir is needed";
string ansFileAbsPath = testRootPath + "/" + ansFile;
if (!std::ifstream(ansFileAbsPath))
ASSERT_TRUE(false) << ansFileAbsPath << " doesn't exist";
fp = splitFilePath(ansFileAbsPath);
// double check to avoid empty fileBaseName
if (fp.fileBaseName.empty())
ASSERT_TRUE(false) << ansFileAbsPath << " is invalid";
// generate new sql file with set search_path added at the begining
const string newSqlFile = generateSQLFile(sqlFile, usingDefaultSchema);
// outFile is located in the same folder with ansFile
string outFileAbsPath = fp.path + "/" + fp.fileBaseName + ".out";
conn->setOutputFile(outFileAbsPath);
EXPECT_EQ(0, conn->runSQLFile(newSqlFile, printTupleOnly).getLastStatus());
conn->resetOutput();
// initFile if any
checkAnsFile(ansFileAbsPath, outFileAbsPath, initFile, newSqlFile, fp);
}
void SQLUtility::execSQLFileandCheck(const string &sqlFile,
const string &outFile,
const std::string &pattern,
const std::string &sqlOptions) {
// do precheck for sqlFile
if (hawq::test::startsWith(sqlFile, "/")) {
ASSERT_TRUE(false)
<< "For sqlFile, relative path to feature test root dir is needed";
}
string sqlFileAbsPath = testRootPath + "/" + sqlFile;
FilePath fp = splitFilePath(sqlFileAbsPath);
string outFileAbsPath = testRootPath + "/" + outFile;
if (fp.fileBaseName.empty()) ASSERT_TRUE(false) << sqlFile << " is invalid";
// generate new sql file with set search_path added at the begining
const string newSqlFile = generateSQLFile(sqlFile, false);
conn->setOutputFile(outFileAbsPath);
EXPECT_EQ(0, conn->runSQLFile(newSqlFile, sqlOptions).getLastStatus());
conn->resetOutput();
bool result = this->checkPatternInFile(outFile, pattern);
ASSERT_TRUE(result) << outFile << " find " << pattern;
}
bool SQLUtility::checkPatternInFile(const std::string &outFile,
const std::string &pattern) {
if (hawq::test::startsWith(outFile, "/")) {
std::cout << "ERROR : outfile format is error ";
return false;
}
string outFileAbsPath = getTestRootPath() + "/" + outFile;
std::ifstream fin(outFileAbsPath);
std::string line;
bool result = true;
while (getline(fin, line) && result) {
std::regex re(pattern, std::regex_constants::icase);
if (std::regex_search(line, re)) {
result = false;
break;
}
}
fin.close();
return result;
}
bool SQLUtility::execAbsSQLFile(const string &sqlFile) {
// double check to avoid empty fileBaseName
FilePath fp = splitFilePath(sqlFile);
if (fp.fileBaseName.empty()) return false;
// outFile is located in the same folder with ansFile
string outFileAbsPath = "/tmp/" + fp.fileBaseName + ".out";
// run sql file and store its result in output file
conn->setOutputFile(outFileAbsPath);
return conn->runSQLFile(sqlFile).getLastStatus() == 0 ? true : false;
}
bool SQLUtility::execSQLFile(const string &sqlFile) {
// do precheck for sqlFile
if (hawq::test::startsWith(sqlFile, "/")) return false;
// double check to avoid empty fileBaseName
FilePath fp = splitFilePath(sqlFile);
if (fp.fileBaseName.empty()) return false;
// outFile is located in the same folder with ansFile
string outFileAbsPath = "/tmp/" + fp.fileBaseName + ".out";
// generate new sql file with set search_path added at the begining
const string newSqlFile = generateSQLFile(sqlFile, false);
// run sql file and store its result in output file
conn->setOutputFile(outFileAbsPath);
return conn->runSQLFile(newSqlFile).getLastStatus() == 0 ? true : false;
}
int32_t SQLUtility::getErrorNumber(const string &outputFile) {
// do precheck for sqlFile
string outputAbspath;
if (hawq::test::startsWith(outputFile, "/"))
outputAbspath = outputFile;
else
outputAbspath = testRootPath + "/" + outputFile;
string cmdstr = "grep -i ERROR " + outputAbspath + " | wc -l";
string output = Command::getCommandOutput(cmdstr);
try {
return std::stoi(output);
} catch (...) {
return -1;
}
}
bool SQLUtility::__beforeExecSpecificSQLFile(const string &sqlFile,
const string &outputFile,
const string &sqlFilePrefix,
string &newSqlFile,
string &outputAbsPath) {
// do precheck for sqlFile
if (hawq::test::startsWith(sqlFile, "/")) return false;
// double check to avoid empty fileBaseName
FilePath fp = splitFilePath(sqlFile);
if (fp.fileBaseName.empty()) return false;
// generate new sql file with set search_path added at the begining
newSqlFile = generateSQLFile(sqlFile, false, sqlFilePrefix);
// outFile is located in the same folder with ansFile
outputAbsPath = testRootPath + "/" + outputFile;
return true;
}
bool SQLUtility::execSpecificSQLFile(const string &sqlFile,
const string &outputFile,
const string &psqlOptions,
const string &sqlFilePrefix) {
string newSqlFile, outputAbsPath;
if (!__beforeExecSpecificSQLFile(sqlFile, outputFile, sqlFilePrefix,
newSqlFile, outputAbsPath))
return false;
conn->setOutputFile(outputAbsPath);
bool result = conn->runSQLFile(newSqlFile, psqlOptions).getLastStatus() == 0
? true
: false;
conn->resetOutput();
return result;
}
bool SQLUtility::__beforeBoolExecAppendSQLFile(const string &sqlFile,
const string &outputFile,
const string &sqlFilePrefix,
const string &appendString,
string &newSqlFile) {
// do precheck for sqlFile
if (hawq::test::startsWith(sqlFile, "/")) return false;
// double check to avoid empty fileBaseName
FilePath fp = splitFilePath(sqlFile);
if (fp.fileBaseName.empty()) return false;
// generate new sql file with set search_path added at the begining
newSqlFile = generateSQLFile(sqlFile, false, sqlFilePrefix, appendString);
// outFile is located in the same folder with ansFile
std::string outputAbsPath = testRootPath + "/" + outputFile;
conn->setOutputFile(outputAbsPath);
return true;
}
bool SQLUtility::execAppendSQLFile(const string &sqlFile,
const string &outputFile,
const string &psqlOptions,
const string &sqlFilePrefix,
const string &appendString) {
std::string newSqlFile;
if (!__beforeBoolExecAppendSQLFile(sqlFile, outputFile, sqlFilePrefix,
appendString, newSqlFile))
return false;
bool result = conn->runSQLFile(newSqlFile, psqlOptions).getLastStatus() == 0
? true
: false;
conn->resetOutput();
return result;
}
void SQLUtility::__beforeRunSqlfile(
const string &sqlFile, const string &outputFile, const string &ansFile,
const string &sqlFilePrefix, const string &appendString,
bool usingDefaultSchema, FilePath &fp, std::string &ansFileAbsPath,
std::string &newSqlFile, std::string &outputAbsPath) {
// do precheck for sqlFile & ansFile
if (hawq::test::startsWith(sqlFile, "/") ||
hawq::test::startsWith(ansFile, "/"))
ASSERT_TRUE(false) << "For sqlFile and ansFile, relative path to feature "
"test root dir is needed";
ansFileAbsPath = testRootPath + "/" + ansFile;
if (!std::ifstream(ansFileAbsPath))
ASSERT_TRUE(false) << ansFileAbsPath << " doesn't exist";
fp = splitFilePath(ansFileAbsPath);
// double check to avoid empty fileBaseName
if (fp.fileBaseName.empty())
ASSERT_TRUE(false) << ansFileAbsPath << " is invalid";
// generate new sql file with set search_path added at the begining
newSqlFile =
generateSQLFile(sqlFile, usingDefaultSchema, sqlFilePrefix, appendString);
// outFile is located in the same folder with ansFile
outputAbsPath = testRootPath + "/" + outputFile;
}
bool SQLUtility::__afterRunSqlfile(const std::string &initFile,
bool sortoutfile, FilePath &fp,
std::string &ansFileAbsPath,
std::string &outputAbsPath,
std::string &newSqlFile) {
if (sortoutfile == false)
return checkAnsFile(ansFileAbsPath, outputAbsPath, initFile, newSqlFile,
fp);
else {
hawq::test::Command comm;
std::string command;
fp = splitFilePath(outputAbsPath);
if (fp.fileBaseName.empty()) {
EXPECT_TRUE(false) << outputAbsPath << " is invalid";
return false;
}
std::string sortoutFileAbsPath =
fp.path + "/" + fp.fileBaseName + "_sort.out";
command = "sort " + outputAbsPath + " > " + sortoutFileAbsPath;
int result = comm.getCommandStatus(command);
EXPECT_TRUE(result != -1);
return checkAnsFile(ansFileAbsPath, sortoutFileAbsPath, initFile, "", fp);
}
}
void SQLUtility::execAppendSQLFile(
const string &sqlFile, const string &outputFile, const string &ansFile,
const string &psqlOptions, const string &sqlFilePrefix,
const string &appendString, const string &initFile, bool usingDefaultSchema,
bool printTupleOnly, bool sortoutfile) {
FilePath fp;
std::string ansFileAbsPath, newSqlFile, outputAbsPath;
__beforeRunSqlfile(sqlFile, outputFile, ansFile, sqlFilePrefix, appendString,
usingDefaultSchema, fp, ansFileAbsPath, newSqlFile,
outputAbsPath);
conn->setOutputFile(outputAbsPath);
EXPECT_EQ(0, conn->runSQLFile(newSqlFile, psqlOptions, printTupleOnly)
.getLastStatus());
conn->resetOutput();
__afterRunSqlfile(initFile, sortoutfile, fp, ansFileAbsPath, outputAbsPath,
newSqlFile);
}
void SQLUtility::runParallelSQLFile(const string sqlFile,
const string outputFile, int processnum,
const string ansFile,
bool usingDefaultSchema) {
// do precheck for sqlFile & ansFile
if (hawq::test::startsWith(sqlFile, "/") ||
hawq::test::startsWith(ansFile, "/"))
ASSERT_TRUE(false) << "For sqlFile and ansFile, relative path to feature "
"test root dir is needed";
// generate new sql file with set search_path added at the begining
const string newSqlFile = generateSQLFile(sqlFile, usingDefaultSchema);
pid_t childpid;
std::vector<pid_t> childprocess;
for (int i = 0; i < processnum; i++) {
childpid = fork();
if (childpid == -1) ASSERT_TRUE(false) << "Fork child process error";
if (childpid == 0) {
auto filename = hawq::test::split(outputFile, '.');
string outFileAbsPath = testRootPath + "/" + filename[0] + "_process_" +
std::to_string(i) + ".out";
conn->setOutputFile(outFileAbsPath);
EXPECT_TRUE(conn->runSQLFile(newSqlFile).getLastStatus() == 0 ? true
: false);
exit(0);
} else {
childprocess.push_back(childpid);
}
}
while (childprocess.size() > 0) {
int length = childprocess.size();
pid_t pid = waitpid(childprocess[length - 1], NULL, 0);
if (pid == childprocess[length - 1]) childprocess.pop_back();
}
}
void SQLUtility::runParallelSQLFile(const string sqlFile,
const string outputFile, int processnum,
std::vector<std::string> &options,
const string ansFile,
bool usingDefaultSchema) {
// do precheck for sqlFile & ansFile
if (hawq::test::startsWith(sqlFile, "/") ||
hawq::test::startsWith(ansFile, "/"))
ASSERT_TRUE(false) << "For sqlFile and ansFile, relative path to feature "
"test root dir is needed";
// generate new sql file with set search_path added at the begining
const string newSqlFile = generateSQLFile(sqlFile, usingDefaultSchema);
pid_t childpid;
std::vector<pid_t> childprocess;
for (int i = 0; i < processnum; i++) {
childpid = fork();
if (childpid == -1) ASSERT_TRUE(false) << "Fork child process error";
if (childpid == 0) {
auto filename = hawq::test::split(outputFile, '.');
string outFileAbsPath = testRootPath + "/" + filename[0] + "_process_" +
std::to_string(i) + ".out";
conn->setOutputFile(outFileAbsPath);
EXPECT_TRUE(conn->runSQLFile(newSqlFile, options[i]).getLastStatus() == 0
? true
: false);
exit(0);
} else {
childprocess.push_back(childpid);
}
}
while (childprocess.size() > 0) {
int length = childprocess.size();
pid_t pid = waitpid(childprocess[length - 1], NULL, 0);
if (pid == childprocess[length - 1]) childprocess.pop_back();
}
}
void SQLUtility::__beforeExecSpecificSQLFile(
const string &sqlFile, const string &outputFile, const string &ansFile,
const string &sqlFilePrefix, bool usingDefaultSchema, FilePath &fp,
string &ansFileAbsPath, string &newSqlFile, string &outputAbsPath) {
// do precheck for sqlFile & ansFile
if (hawq::test::startsWith(sqlFile, "/") ||
hawq::test::startsWith(ansFile, "/"))
ASSERT_TRUE(false) << "For sqlFile and ansFile, relative path to feature "
"test root dir is needed";
ansFileAbsPath = testRootPath + "/" + ansFile;
if (!std::ifstream(ansFileAbsPath))
ASSERT_TRUE(false) << ansFileAbsPath << " doesn't exist";
fp = splitFilePath(ansFileAbsPath);
// double check to avoid empty fileBaseName
if (fp.fileBaseName.empty())
ASSERT_TRUE(false) << ansFileAbsPath << " is invalid";
// generate new sql file with set search_path added at the begining
newSqlFile = generateSQLFile(sqlFile, usingDefaultSchema, sqlFilePrefix);
// outFile is located in the same folder with ansFile
outputAbsPath = testRootPath + "/" + outputFile;
}
void SQLUtility::execSpecificSQLFile(
const string &sqlFile, const string &outputFile, const string &ansFile,
const string &psqlOptions, const string &sqlFilePrefix,
const string &initFile, bool usingDefaultSchema, bool printTupleOnly) {
FilePath fp;
std::string ansFileAbsPath, newSqlFile, outputAbsPath;
__beforeExecSpecificSQLFile(sqlFile, outputFile, ansFile, sqlFilePrefix,
usingDefaultSchema, fp, ansFileAbsPath,
newSqlFile, outputAbsPath);
conn->setOutputFile(outputAbsPath);
EXPECT_EQ(0, conn->runSQLFile(newSqlFile, psqlOptions, printTupleOnly)
.getLastStatus());
conn->resetOutput();
checkAnsFile(ansFileAbsPath, outputAbsPath, initFile, newSqlFile, fp);
}
const string SQLUtility::generateSQLFile(const string &sqlFile,
bool usingDefaultSchema,
const string &sqlFileSurfix,
const string &appendString) {
const string originSqlFile = testRootPath + "/" + sqlFile;
const string newSqlFile = "/tmp/" + string(test_info->test_case_name()) +
"_" + test_info->name() + sqlFileSurfix + ".sql";
std::fstream in;
in.open(originSqlFile, std::ios::in);
if (!in.is_open()) {
EXPECT_TRUE(false) << "Error opening file " << originSqlFile;
}
std::fstream out;
out.open(newSqlFile, std::ios::out);
if (!out.is_open()) {
EXPECT_TRUE(false) << "Error opening file " << newSqlFile;
}
out << "-- start_ignore" << std::endl;
if (sql_util_mode == MODE_MADLIB) {
out << "SET SEARCH_PATH=" << schemaName << ",madlib;" << std::endl;
} else if (!usingDefaultSchema) {
out << "SET SEARCH_PATH=" + schemaName + ";" << std::endl;
}
if (sql_util_mode == MODE_DATABASE) {
out << "\\c " << databaseName << std::endl;
}
out << "-- end_ignore" << std::endl;
if (appendString.size() > 0) out << appendString << "\n";
string line;
while (getline(in, line)) {
out << line << std::endl;
}
in.close();
out.close();
return newSqlFile;
}
const hawq::test::PSQLQueryResult &SQLUtility::executeQuery(const string &sql) {
const hawq::test::PSQLQueryResult &result =
conn->getQueryResult("SET SEARCH_PATH=" + schemaName + ";" + sql);
return result;
}
std::string SQLUtility::getHawqDfsURL() {
std::string url = this->getGUCValue("hawq_dfs_url");
if (url[url.size() - 1] != '/') {
url += "/";
}
return url;
}
bool SQLUtility::isDarwin() {
std::string output = Command::getCommandOutput("uname | grep Darwin | wc -l");
int num = atoi(output.c_str());
return num != 0;
}
std::string SQLUtility::getHawqMagmaURL() {
hawq::test::HawqConfig hc;
std::vector<std::string> segs;
hc.getSlaves(segs);
if (segs.empty()) {
return "";
} else {
// use magma service on first segment host specified in $GPHOME/etc/slaves
return segs[0] + ":" + this->getGUCValue("hawq_magma_port_segment");
}
}
std::string SQLUtility::getHawqDfsHost() {
string url = this->getGUCValue("hawq_dfs_url");
std::size_t pos = url.find("/");
std::string result = url.substr(0, pos);
return result;
}
std::string SQLUtility::getHdfsPath() {
string url = this->getGUCValue("hawq_dfs_url");
std::size_t found = url.find("/");
std::string result = url.substr(found);
if (result[result.size() - 1] != '/') {
result += "/";
}
return result;
}
std::string SQLUtility::getHdfsNamenode() {
string url = this->getGUCValue("hawq_dfs_url");
std::size_t found = url.find("/");
std::string result = url.substr(0, found + 1);
return result;
}
const hawq::test::PSQL *SQLUtility::getPSQL() const { return conn.get(); }
string SQLUtility::getTestRootPath() {
string result;
#ifdef __linux__
char pathbuf[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", pathbuf, PATH_MAX);
if (count <= 0)
EXPECT_TRUE(false) << "readlink /proc/self/exe error: " << strerror(errno);
result = string(pathbuf, count);
#elif __APPLE__
int ret;
pid_t pid;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
pid = getpid();
ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
if (ret <= 0)
EXPECT_TRUE(false) << "PID " << pid << ": proc_pidpath () "
<< strerror(errno);
result = string(pathbuf);
#endif
return splitFilePath(result).path;
}
void SQLUtility::setGUCValue(const std::string &guc, const std::string &value) {
string sql = "set " + guc + " = " + value;
savedGUCValue += sql + ';';
}
std::string SQLUtility::getGUCValue(const std::string &guc) {
string sql = "show " + guc;
const hawq::test::PSQLQueryResult &result = executeQuery(sql);
EXPECT_EQ(result.rowCount(), 1);
if (result.rowCount() == 1) {
std::vector<std::string> row = result.getRows()[0];
return row[0];
}
return "";
}
std::string SQLUtility::getQueryResult(const std::string &query, bool check) {
const hawq::test::PSQLQueryResult &result = executeQuery(query);
std::string value;
if (check) EXPECT_LE(result.rowCount(), 1);
if (result.rowCount() == 1) {
value = result.getRows()[0][0];
} else {
value = "";
}
return value;
}
std::string SQLUtility::getQueryResultSetString(const std::string &query) {
const hawq::test::PSQLQueryResult &result = executeQuery(query);
std::vector<std::vector<string> > resultString = result.getRows();
string resultStr;
for (auto row : result.getRows()) {
for (auto column : row) resultStr += column + "|";
resultStr += "\n";
}
return resultStr;
}
FilePath SQLUtility::splitFilePath(const string &filePath) {
FilePath fp;
size_t found1 = filePath.find_last_of("/");
size_t found2 = filePath.find_last_of(".");
fp.path = filePath.substr(0, found1);
fp.fileBaseName = filePath.substr(found1 + 1, found2 - found1 - 1);
fp.fileSuffix = filePath.substr(found2 + 1, filePath.length() - found2 - 1);
return fp;
}
void SQLUtility::runParallelSQLFile_sqllist(
std::vector<std::string> &sqlfiles, std::vector<std::string> &ansfiles) {
int len = sqlfiles.size();
int anslen = ansfiles.size();
int i;
FilePath fp;
std::string sqlFileAbsPath, ansFileAbsPath, outFileAbsPath;
// do precheck for sqlFile & ansFile
if (len != anslen)
ASSERT_TRUE(false)
<< "The number of sqlfile is not equal to the number of ansfile";
for (i = 0; i < len; i++) {
if (hawq::test::startsWith(sqlfiles[i], "/") ||
hawq::test::startsWith(ansfiles[i], "/"))
ASSERT_TRUE(false) << "For sqlFile and ansFile, relative path to feature "
"test root dir is needed";
ansFileAbsPath = testRootPath + "/" + ansfiles[i];
if (!std::ifstream(ansFileAbsPath))
ASSERT_TRUE(false) << ansFileAbsPath << " doesn't exist";
}
pid_t childpid;
std::vector<pid_t> childprocess;
for (i = 0; i < len; i++) {
childpid = fork();
if (childpid == -1) ASSERT_TRUE(false) << "Fork child process error";
if (childpid == 0) {
sqlFileAbsPath = testRootPath + "/" + sqlfiles[i];
ansFileAbsPath = testRootPath + "/" + ansfiles[i];
fp = splitFilePath(ansFileAbsPath);
outFileAbsPath = fp.path + "/" + fp.fileBaseName + ".out";
conn->setOutputFile(outFileAbsPath);
EXPECT_TRUE(
conn->runSQLFile(sqlFileAbsPath).getLastStatus() == 0 ? true : false);
conn->resetOutput();
checkAnsFile(ansFileAbsPath, outFileAbsPath, "", "", fp);
exit(0);
} else
childprocess.push_back(childpid);
}
while (childprocess.size() > 0) {
int length = childprocess.size();
pid_t pid = waitpid(childprocess[length - 1], NULL, 0);
if (pid == childprocess[length - 1]) childprocess.pop_back();
}
}
} // namespace test
} // namespace hawq