should be it
This commit is contained in:
275
external/duckdb/test/sqlite/sqllogic_parser.cpp
vendored
Normal file
275
external/duckdb/test/sqlite/sqllogic_parser.cpp
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user