blob: 07071f4d5dc34eb6611c34231749f49eba8bc79a [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 <vector>
#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) {
schemaName = string(test_info->test_case_name()) + "_" + test_info->name();
databaseName = HAWQ_DB;
exec("DROP SCHEMA IF EXISTS " + schemaName + " CASCADE");
exec("CREATE SCHEMA " + schemaName);
sql_util_mode = MODE_SCHEMA;
} 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() {
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) {
exec("DROP SCHEMA " + schemaName + " CASCADE");
}
if (sql_util_mode == MODE_DATABASE) {
exec("DROP DATABASE " + databaseName);
}
}
}
std::string SQLUtility::getDbName() {
return databaseName;
}
std::string SQLUtility::getSchemaName() {
return schemaName;
}
void SQLUtility::exec(const string &sql) {
EXPECT_EQ(0, (conn->runSQLCommand(sql)).getLastStatus())
<< conn->getLastResult();
}
string SQLUtility::execute(const string &sql, bool check) {
conn->runSQLCommand("SET SEARCH_PATH=" + schemaName + ";" + sql);
EXPECT_NE(conn.get(), nullptr);
if (check) {
EXPECT_EQ(0, conn->getLastStatus()) << conn->getLastResult();
return "";
}
return conn.get()->getLastResult();
}
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);
}
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
string initFileAbsPath;
if (!initFile.empty()) {
initFileAbsPath = testRootPath + "/" + initFile;
if (!std::ifstream(initFileAbsPath))
ASSERT_TRUE(false) << initFileAbsPath << " doesn't exist";
fp = splitFilePath(initFileAbsPath);
// double check to avoid empty fileBaseName
if (fp.fileBaseName.empty())
ASSERT_TRUE(false) << initFileAbsPath << " is invalid";
} else {
initFileAbsPath = "";
}
string globalInitFileAbsPath;
globalInitFileAbsPath = testRootPath + "/lib/global_init_file";
bool is_sql_ans_diff = conn->checkDiff(ansFileAbsPath, outFileAbsPath, true, globalInitFileAbsPath, initFileAbsPath);
EXPECT_FALSE(is_sql_ans_diff);
if (is_sql_ans_diff == false) {
// no diff, continue to delete the generated sql file
if (remove(newSqlFile.c_str()))
ASSERT_TRUE(false) << "Error deleting file " << newSqlFile;
} else {
EXPECT_FALSE(true);
}
}
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;
}
const string SQLUtility::generateSQLFile(const string &sqlFile, bool usingDefaultSchema) {
const string originSqlFile = testRootPath + "/" + sqlFile;
const string newSqlFile = "/tmp/" + string(test_info->test_case_name()) + "_" + test_info->name() + ".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 (!usingDefaultSchema) {
out << "SET SEARCH_PATH=" + schemaName + ";" << std::endl;
}
if (sql_util_mode == MODE_DATABASE) {
out << "\\c " << databaseName << std::endl;
}
out << "-- end_ignore" << std::endl;
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;
}
const hawq::test::PSQL *SQLUtility::getPSQL() const { return conn.get(); }
string SQLUtility::getTestRootPath() const {
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;
execute(sql, true);
}
std::string SQLUtility::getGUCValue(const std::string &guc) {
string sql = "show " + guc;
const hawq::test::PSQLQueryResult &result = executeQuery(sql);
EXPECT_EQ(result.rowCount(), 1);
std::vector<std::string> row = result.getRows()[0];
return row[0];
}
std::string SQLUtility::getQueryResult(const std::string &query) {
const hawq::test::PSQLQueryResult &result = executeQuery(query);
EXPECT_LE(result.rowCount(), 1);
std::string value;
if (result.rowCount() == 1)
{
value = result.getRows()[0][0];
}
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) const {
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;
}
} // namespace test
} // namespace hawq