Files
email-tracker/external/duckdb/test/sqlite/sqllogic_parser.cpp
2025-10-24 19:21:19 -05:00

276 lines
8.4 KiB
C++

#include "sqllogic_parser.hpp"
#include "catch.hpp"
#include <fstream>
namespace duckdb {
bool SQLLogicParser::OpenFile(const string &path) {
this->file_name = path;
std::ifstream infile(file_name);
if (infile.bad() || infile.fail()) {
return false;
}
string line;
while (std::getline(infile, line)) {
lines.push_back(StringUtil::Replace(line, "\r", ""));
}
return !infile.bad();
}
bool SQLLogicParser::EmptyOrComment(const string &line) {
return line.empty() || StringUtil::StartsWith(line, "#");
}
bool SQLLogicParser::NextLineEmptyOrComment() {
if (current_line + 1 >= lines.size()) {
return true;
} else {
return EmptyOrComment(lines[current_line + 1]);
}
}
bool SQLLogicParser::NextStatement() {
if (seen_statement) {
// skip the current statement
// but only if we have already seen a statement in the file
while (current_line < lines.size() && !EmptyOrComment(lines[current_line])) {
current_line++;
}
}
seen_statement = true;
// now look for the first non-empty line
while (current_line < lines.size() && EmptyOrComment(lines[current_line])) {
current_line++;
}
// return whether or not we reached the end of the file
return current_line < lines.size();
}
void SQLLogicParser::NextLine() {
current_line++;
}
string SQLLogicParser::ExtractStatement() {
string statement;
bool first_line = true;
while (current_line < lines.size() && !EmptyOrComment(lines[current_line])) {
if (lines[current_line] == "----") {
break;
}
if (!first_line) {
statement += "\n";
}
statement += lines[current_line];
first_line = false;
current_line++;
}
return statement;
}
vector<string> SQLLogicParser::ExtractExpectedResult() {
vector<string> result;
// skip the result line (----) if we are still reading that
if (current_line < lines.size() && lines[current_line] == "----") {
current_line++;
}
// read the expected result until we encounter a new line
while (current_line < lines.size() && !lines[current_line].empty()) {
result.push_back(lines[current_line]);
current_line++;
}
return result;
}
string SQLLogicParser::ExtractExpectedError(bool expect_ok, bool original_sqlite_test) {
// check if there is an expected error at all
if (current_line >= lines.size() || lines[current_line] != "----") {
if (!expect_ok && !original_sqlite_test) {
Fail("Failed to parse statement: statement error needs to have an expected error message");
}
return string();
}
if (expect_ok) {
Fail("Failed to parse statement: only statement error can have an expected error message, not statement ok");
}
current_line++;
string error;
vector<string> error_lines;
while (current_line < lines.size() && !lines[current_line].empty()) {
error_lines.push_back(lines[current_line]);
current_line++;
}
error = StringUtil::Join(error_lines, "\n");
return error;
}
void SQLLogicParser::FailRecursive(const string &msg, vector<ExceptionFormatValue> &values) {
auto error_message =
file_name + ":" + to_string(current_line + 1) + ": " + ExceptionFormatValue::Format(msg, values);
FAIL(error_message.c_str());
}
SQLLogicToken SQLLogicParser::Tokenize() {
SQLLogicToken result;
if (current_line >= lines.size()) {
result.type = SQLLogicTokenType::SQLLOGIC_INVALID;
return result;
}
vector<string> argument_list;
auto &line = lines[current_line];
idx_t last_pos = 0;
for (idx_t i = 0; i < line.size(); i++) {
if (StringUtil::CharacterIsSpace(line[i])) {
if (i == last_pos) {
last_pos++;
} else {
argument_list.push_back(line.substr(last_pos, i - last_pos));
last_pos = i + 1;
}
}
}
if (last_pos != line.size()) {
argument_list.push_back(line.substr(last_pos, line.size() - last_pos));
}
if (argument_list.empty()) {
Fail("Empty line!?");
}
result.type = CommandToToken(argument_list[0]);
for (idx_t i = 1; i < argument_list.size(); i++) {
result.parameters.push_back(std::move(argument_list[i]));
}
return result;
}
// Single line statements should throw a parser error if the next line is not a comment or a newline
bool SQLLogicParser::IsSingleLineStatement(SQLLogicToken &token) {
switch (token.type) {
case SQLLogicTokenType::SQLLOGIC_HASH_THRESHOLD:
case SQLLogicTokenType::SQLLOGIC_HALT:
case SQLLogicTokenType::SQLLOGIC_MODE:
case SQLLogicTokenType::SQLLOGIC_SET:
case SQLLogicTokenType::SQLLOGIC_RESET:
case SQLLogicTokenType::SQLLOGIC_LOOP:
case SQLLogicTokenType::SQLLOGIC_FOREACH:
case SQLLogicTokenType::SQLLOGIC_CONCURRENT_LOOP:
case SQLLogicTokenType::SQLLOGIC_CONCURRENT_FOREACH:
case SQLLogicTokenType::SQLLOGIC_ENDLOOP:
case SQLLogicTokenType::SQLLOGIC_REQUIRE:
case SQLLogicTokenType::SQLLOGIC_REQUIRE_ENV:
case SQLLogicTokenType::SQLLOGIC_TEST_ENV:
case SQLLogicTokenType::SQLLOGIC_LOAD:
case SQLLogicTokenType::SQLLOGIC_RESTART:
case SQLLogicTokenType::SQLLOGIC_RECONNECT:
case SQLLogicTokenType::SQLLOGIC_SLEEP:
case SQLLogicTokenType::SQLLOGIC_UNZIP:
case SQLLogicTokenType::SQLLOGIC_TAGS:
return true;
case SQLLogicTokenType::SQLLOGIC_SKIP_IF:
case SQLLogicTokenType::SQLLOGIC_ONLY_IF:
case SQLLogicTokenType::SQLLOGIC_INVALID:
case SQLLogicTokenType::SQLLOGIC_STATEMENT:
case SQLLogicTokenType::SQLLOGIC_QUERY:
return false;
default:
throw std::runtime_error("Unknown SQLLogic token found!");
}
}
// (All) Context statements must precede all non-header statements
bool SQLLogicParser::IsTestCommand(SQLLogicTokenType &type) {
switch (type) {
case SQLLogicTokenType::SQLLOGIC_QUERY:
case SQLLogicTokenType::SQLLOGIC_STATEMENT:
return true;
case SQLLogicTokenType::SQLLOGIC_CONCURRENT_FOREACH:
case SQLLogicTokenType::SQLLOGIC_CONCURRENT_LOOP:
case SQLLogicTokenType::SQLLOGIC_ENDLOOP:
case SQLLogicTokenType::SQLLOGIC_FOREACH:
case SQLLogicTokenType::SQLLOGIC_HALT:
case SQLLogicTokenType::SQLLOGIC_HASH_THRESHOLD:
case SQLLogicTokenType::SQLLOGIC_INVALID:
case SQLLogicTokenType::SQLLOGIC_LOAD:
case SQLLogicTokenType::SQLLOGIC_LOOP:
case SQLLogicTokenType::SQLLOGIC_MODE:
case SQLLogicTokenType::SQLLOGIC_ONLY_IF:
case SQLLogicTokenType::SQLLOGIC_RECONNECT:
case SQLLogicTokenType::SQLLOGIC_REQUIRE:
case SQLLogicTokenType::SQLLOGIC_REQUIRE_ENV:
case SQLLogicTokenType::SQLLOGIC_RESET:
case SQLLogicTokenType::SQLLOGIC_RESTART:
case SQLLogicTokenType::SQLLOGIC_SET:
case SQLLogicTokenType::SQLLOGIC_SKIP_IF:
case SQLLogicTokenType::SQLLOGIC_SLEEP:
case SQLLogicTokenType::SQLLOGIC_TAGS:
case SQLLogicTokenType::SQLLOGIC_TEST_ENV:
case SQLLogicTokenType::SQLLOGIC_UNZIP:
return false;
default:
throw std::runtime_error("Unknown SQLLogic token found!");
}
}
SQLLogicTokenType SQLLogicParser::CommandToToken(const string &token) {
if (token == "skipif") {
return SQLLogicTokenType::SQLLOGIC_SKIP_IF;
} else if (token == "onlyif") {
return SQLLogicTokenType::SQLLOGIC_ONLY_IF;
} else if (token == "statement") {
return SQLLogicTokenType::SQLLOGIC_STATEMENT;
} else if (token == "query") {
return SQLLogicTokenType::SQLLOGIC_QUERY;
} else if (token == "hash-threshold") {
return SQLLogicTokenType::SQLLOGIC_HASH_THRESHOLD;
} else if (token == "halt") {
return SQLLogicTokenType::SQLLOGIC_HALT;
} else if (token == "mode") {
return SQLLogicTokenType::SQLLOGIC_MODE;
} else if (token == "set") {
return SQLLogicTokenType::SQLLOGIC_SET;
} else if (token == "reset") {
return SQLLogicTokenType::SQLLOGIC_RESET;
} else if (token == "loop") {
return SQLLogicTokenType::SQLLOGIC_LOOP;
} else if (token == "concurrentloop") {
return SQLLogicTokenType::SQLLOGIC_CONCURRENT_LOOP;
} else if (token == "foreach") {
return SQLLogicTokenType::SQLLOGIC_FOREACH;
} else if (token == "concurrentforeach") {
return SQLLogicTokenType::SQLLOGIC_CONCURRENT_FOREACH;
} else if (token == "endloop") {
return SQLLogicTokenType::SQLLOGIC_ENDLOOP;
} else if (token == "require") {
return SQLLogicTokenType::SQLLOGIC_REQUIRE;
} else if (token == "require-env") {
return SQLLogicTokenType::SQLLOGIC_REQUIRE_ENV;
} else if (token == "test-env") {
return SQLLogicTokenType::SQLLOGIC_TEST_ENV;
} else if (token == "load") {
return SQLLogicTokenType::SQLLOGIC_LOAD;
} else if (token == "restart") {
return SQLLogicTokenType::SQLLOGIC_RESTART;
} else if (token == "reconnect") {
return SQLLogicTokenType::SQLLOGIC_RECONNECT;
} else if (token == "sleep") {
return SQLLogicTokenType::SQLLOGIC_SLEEP;
} else if (token == "unzip") {
return SQLLogicTokenType::SQLLOGIC_UNZIP;
} else if (token == "tags") {
return SQLLogicTokenType::SQLLOGIC_TAGS;
}
Fail("Unrecognized parameter %s", token);
return SQLLogicTokenType::SQLLOGIC_INVALID;
}
} // namespace duckdb