unittest/utils_mysql_parsing_t.cc (1,189 lines of code) (raw):
/*
* Copyright (c) 2014, 2024, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms,
* as designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <array>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <stack>
#include <string>
#include <string_view>
#include <vector>
#include "unittest/gtest_clean.h"
#include "unittest/test_utils/shell_test_env.h"
#include "utils/utils_mysql_parsing.h"
namespace mysqlshdk {
namespace utils {
// Legacy tests with adaptations
class TestMySQLSplitter : public ::testing::Test {
protected:
bool debug = false;
std::vector<std::tuple<std::string, std::string, size_t>> results;
std::string sql;
std::string delim = ";";
std::string context;
std::string buffer;
std::unique_ptr<Sql_splitter> splitter;
void SetUp() override {
debug = g_test_trace_scripts;
splitter.reset(new Sql_splitter(
[this](std::string_view s, bool bol,
size_t lnum) -> std::pair<size_t, bool> {
if (!bol) s = s.substr(0, 2);
assert(s.size() >= 2);
if (s[1] != 'g' && s[1] != 'G') {
results.emplace_back(std::string{s}, "", lnum);
return std::make_pair(s.size(), false);
}
return std::make_pair(2U, true);
},
[this](std::string_view err) {
results.emplace_back(std::string{err}, "", 0);
}));
}
void send_sql(const std::string &data, bool dont_flush = false) {
// Sends the received sql and sets the list of command results denoted
// by their position in the original query and a delimiter.
results.clear();
splitter->reset();
buffer.clear();
delim = ";";
sql = data;
std::stringstream str(data);
split_incremental(&str, data.size(), dont_flush);
}
void send_sql_incr(const std::string &data, bool dont_flush = true) {
// Sends the received sql and sets the list of command results denoted
// by their position in the original query and a delimiter.
sql = data;
std::stringstream str(data);
if (debug) std::cout << "\n" << data << "\n";
split_incremental(&str, data.size(), dont_flush);
}
void split_incremental(std::iostream *stream, size_t chunk_size,
bool dont_flush) {
assert(chunk_size > 0);
splitter->set_delimiter(delim);
size_t offset = buffer.size();
buffer.resize(offset + chunk_size);
stream->read(&buffer[offset], chunk_size);
buffer.resize(offset + stream->gcount());
splitter->feed_chunk(&buffer[0], buffer.size());
while (!splitter->eof()) {
Sql_splitter::Range range;
std::string delimiter;
if (splitter->next_range(&range, &delimiter)) {
if (debug)
std::cout << buffer.substr(range.offset, range.length) << delimiter
<< "\n";
results.emplace_back(buffer.substr(range.offset, range.length),
delimiter, range.line_num);
} else {
splitter->pack_buffer(&buffer, range);
size_t osize = buffer.size();
buffer.resize(osize + chunk_size);
stream->read(&buffer[osize], chunk_size);
size_t gcount = stream->gcount();
buffer.resize(osize + gcount);
if (gcount < chunk_size && !dont_flush) {
// this is the last chunk
splitter->feed(&buffer[0], buffer.size());
} else {
if (gcount == 0) break;
splitter->feed_chunk(&buffer[0], buffer.size());
}
}
}
delim = splitter->delimiter();
context = to_string(splitter->context());
}
};
TEST_F(TestMySQLSplitter, full_statement) {
send_sql("show databases;");
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, full_statement_misc_delimiter) {
send_sql("show databases\\G");
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("\\G", std::get<1>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, quoted) {
send_sql("'a';'b';");
EXPECT_EQ(2U, results.size());
EXPECT_EQ("'a'", std::get<0>(results[0]));
EXPECT_EQ("'b'", std::get<0>(results[1]));
send_sql("'\n';`\n`;");
EXPECT_EQ(2U, results.size());
EXPECT_EQ("'\n'", std::get<0>(results[0]));
EXPECT_EQ("`\n`", std::get<0>(results[1]));
send_sql("````;b;");
EXPECT_EQ(2U, results.size());
EXPECT_EQ("````", std::get<0>(results[0]));
EXPECT_EQ("b", std::get<0>(results[1]));
send_sql("`\n--`;b;");
EXPECT_EQ(2U, results.size());
EXPECT_EQ("`\n--`", std::get<0>(results[0]));
EXPECT_EQ("b", std::get<0>(results[1]));
send_sql("'\n--';b;");
EXPECT_EQ(2U, results.size());
EXPECT_EQ("'\n--'", std::get<0>(results[0]));
EXPECT_EQ("b", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, by_line_continued_statement) {
send_sql_incr("show ");
ASSERT_EQ(0, results.size());
EXPECT_EQ("-", context);
send_sql_incr("databases");
ASSERT_EQ(0, results.size());
EXPECT_EQ("-", context);
send_sql_incr(";");
ASSERT_EQ(1, results.size());
EXPECT_EQ(";", std::get<1>(results[0]));
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, by_line_continued_statement_misc_delimiter) {
send_sql_incr("show ");
ASSERT_EQ(0, results.size());
EXPECT_EQ("-", context);
send_sql_incr("databases");
ASSERT_EQ(0, results.size());
EXPECT_EQ("-", context);
send_sql_incr("\\G");
ASSERT_EQ(1, results.size());
EXPECT_EQ("\\G", std::get<1>(results[0]));
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, script_continued_statement) {
send_sql("show\ndatabases\n;\n");
ASSERT_EQ(1, results.size());
EXPECT_FALSE(std::get<1>(results[0]).empty());
EXPECT_EQ("", context);
EXPECT_EQ("show\ndatabases\n", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, script_continued_statement_misc_delimiter) {
send_sql("show\ndatabases\n\\G\n");
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("\\G", std::get<1>(results[0]));
EXPECT_EQ("show\ndatabases\n", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, no_ignore_empty_statements) {
send_sql("show databases;\n;");
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("", std::get<0>(results[1]));
send_sql(";");
EXPECT_EQ(1, results.size());
EXPECT_EQ("", context);
send_sql("show databases\\G;\\G");
EXPECT_EQ(3, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
send_sql("\\G#");
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
send_sql("\\G #");
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
send_sql(";;");
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
send_sql(";;;");
EXPECT_EQ(3, results.size());
EXPECT_EQ("", context);
send_sql("show databases;;");
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
send_sql("show databases;;;");
EXPECT_EQ(3, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
send_sql("show databases;\\G;");
EXPECT_EQ(3, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
send_sql(";#");
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
send_sql("; #");
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
}
TEST_F(TestMySQLSplitter, single_line_comments) {
sql =
"/* shows the database list */\n"
"show databases;";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ(sql.substr(0, sql.length() - 1), std::get<0>(results[0]));
sql =
"-- shows the database list\n"
"show databases;";
send_sql(sql);
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("-- shows the database list", std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
sql =
"--\tshows the database list\n"
"show databases;";
send_sql(sql);
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("--\tshows the database list", std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
sql =
"--\n"
"show databases;";
send_sql(sql);
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("--", std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
sql = "--";
send_sql(sql, true);
ASSERT_EQ(0, results.size());
EXPECT_EQ("-", context); // -- comments are terminated by a newline
sql =
"--this is an invalid comment, should be considered part of statement\n"
"show databases;";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ(sql.substr(0, sql.length() - 1), std::get<0>(results[0]));
sql =
"#this is an valid comment\n"
"show databases;";
send_sql(sql);
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("#this is an valid comment", std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
sql = "show databases; #this is an valid comment\n";
send_sql(sql);
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("#this is an valid comment", std::get<0>(results[1]));
sql = "show databases\\G #this is an valid comment\n";
send_sql(sql);
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("\\G", std::get<1>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("#this is an valid comment", std::get<0>(results[1]));
sql = "show databases\\G -- this is an valid comment\n";
send_sql(sql);
EXPECT_EQ(2, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("\\G", std::get<1>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("-- this is an valid comment", std::get<0>(results[1]));
sql =
"--this is an invalid comment, state should indicate a continued "
"statement";
send_sql(sql, true);
ASSERT_EQ(0, results.size());
EXPECT_EQ("-", context);
}
TEST_F(TestMySQLSplitter, multi_line_comments_in_batch) {
sql =
"/*\n"
"this is a comment\n"
"and should be ignored\n"
"and ends here */\n"
"show databases;";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ(";", std::get<1>(results[0]));
EXPECT_EQ(sql.substr(0, sql.length() - 1), std::get<0>(results[0]));
sql =
"/*\n"
"this is a comment;\n"
"and should be ignored;\n"
"* foo; bar\n"
"and ends here */\n"
"show databases;";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ(";", std::get<1>(results[0]));
EXPECT_EQ(sql.substr(0, sql.length() - 1), std::get<0>(results[0]));
sql =
"/*\n"
"this is a comment\n"
"and should be ignored\n"
"and ends here */\n"
"show databases\\G";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("", context);
EXPECT_EQ("\\G", std::get<1>(results[0]));
EXPECT_EQ(sql.substr(0, sql.length() - 2), std::get<0>(results[0]));
sql =
"/*\n"
"this is a comment\n"
"and should be ignored\n"
"and ends on next line\n"
"*/\n"
"show databases;";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ(sql.substr(0, sql.size() - 1), std::get<0>(results[0]));
// should continue until delimiter since we're not trimming comments
// EXPECT_EQ("", context); // Multiline comment flag is cleared
send_sql_incr("show databases;");
ASSERT_EQ(2, results.size());
EXPECT_FALSE(std::get<1>(results[0]).empty());
EXPECT_EQ("", context);
EXPECT_EQ(
"/*\nthis is a comment\nand should be ignored\nand ends on next "
"line\n*/\nshow databases",
std::get<0>(results[0]));
sql =
"/* comment */# comment\n"
"show databases;";
send_sql(sql);
ASSERT_EQ(2, results.size());
EXPECT_FALSE(std::get<1>(results[1]).empty());
EXPECT_EQ("/* comment */# comment", std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
sql =
"/* comment */ # comment\n"
"show databases;";
send_sql(sql);
ASSERT_EQ(2, results.size());
EXPECT_FALSE(std::get<1>(results[1]).empty());
EXPECT_EQ("/* comment */ # comment", std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, continued_backtick_string) {
send_sql_incr("select * from `t1");
ASSERT_EQ(0, results.size());
// EXPECT_TRUE(std::get<1>(results[0]).empty());
EXPECT_EQ("`", context); // Multiline backtick flag is set
// EXPECT_EQ(sql, std::get<0>(results[0]));
send_sql_incr("`;");
ASSERT_EQ(1, results.size());
EXPECT_FALSE(std::get<1>(results[0]).empty());
EXPECT_EQ("", context); // Multiline backtick flag is cleared
EXPECT_EQ("select * from `t1`", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, continued_single_quote_string) {
send_sql_incr("select * from t1 where name = 'this");
ASSERT_EQ(0, results.size());
// EXPECT_TRUE(std::get<1>(results[0]).empty());
EXPECT_EQ("'", context); // Multiline backtick flag is set
// EXPECT_EQ(sql, std::get<0>(results[0]));
send_sql_incr("thing';");
ASSERT_EQ(1, results.size());
EXPECT_FALSE(std::get<1>(results[0]).empty());
EXPECT_EQ("", context); // Multiline backtick flag is cleared
EXPECT_EQ("select * from t1 where name = 'thisthing'",
std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, continued_double_quote_string) {
send_sql_incr("select * from t1 where name = \"this");
ASSERT_EQ(0, results.size());
// EXPECT_TRUE(std::get<1>(results[0]).empty());
EXPECT_EQ("\"", context); // Multiline backtick flag is set
// EXPECT_EQ(sql, std::get<0>(results[0]));
send_sql_incr("\";");
ASSERT_EQ(1, results.size());
EXPECT_FALSE(std::get<1>(results[0]).empty());
EXPECT_EQ("", context); // Multiline backtick flag is cleared
EXPECT_EQ("select * from t1 where name = \"this\"", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, delimiter_ignored_contexts) {
sql = "#this is an valid comment, delimiter ; inside\n";
send_sql(sql);
ASSERT_EQ(1, results.size());
sql = "-- this is an valid comment, delimiter ; inside\n";
send_sql(sql);
ASSERT_EQ(1, results.size());
auto prev_context = context;
sql = "/* this is an valid comment, delimiter ; inside */\n";
send_sql(sql, true);
ASSERT_EQ(0, results.size());
EXPECT_EQ(prev_context, context);
prev_context = context;
sql = "/* this is an valid comment, delimiter ; inside */\n";
send_sql(sql, false);
ASSERT_EQ(1, results.size());
EXPECT_EQ(prev_context, context);
sql = "select ';' as a;";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("select ';' as a", std::get<0>(results[0]));
sql = "select `;` from x;";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("select `;` from x", std::get<0>(results[0]));
sql = "#this is an valid comment, delimiter \\G inside\n";
send_sql(sql);
ASSERT_EQ(1, results.size());
sql = "-- this is an valid comment, delimiter \\G inside\n";
send_sql(sql);
ASSERT_EQ(1, results.size());
sql = "/* this is an valid comment, delimiter \\G inside */\n";
send_sql(sql, true);
ASSERT_EQ(0, results.size());
sql = "select '\\G' as a\\G";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("select '\\G' as a", std::get<0>(results[0]));
sql = "select `\\G` from x\\G";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_EQ("select `\\G` from x", std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, multiple_statements) {
sql =
"show databases;\n"
"select * from whatever;\n"
"drop database whatever;\n";
send_sql(sql);
EXPECT_EQ(3, results.size());
for (int i = 0; i < 3; i++) EXPECT_FALSE(std::get<1>(results[i]).empty());
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("select * from whatever", std::get<0>(results[1]));
EXPECT_EQ("drop database whatever", std::get<0>(results[2]));
}
TEST_F(TestMySQLSplitter, multiple_statements_misc_delimiter) {
sql =
"show databases\\G\n"
"select * from whatever\\G\n"
"drop database whatever\\G\n";
send_sql(sql);
EXPECT_EQ(3, results.size());
for (int i = 0; i < 3; i++) EXPECT_EQ("\\G", std::get<1>(results[i]));
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("select * from whatever", std::get<0>(results[1]));
EXPECT_EQ("drop database whatever", std::get<0>(results[2]));
}
TEST_F(TestMySQLSplitter, multiple_statements_one_line_misc_delimiter) {
sql = "show databases\\Gselect * from whatever\\Gdrop database whatever\\G\n";
send_sql(sql);
EXPECT_EQ(3, results.size());
for (int i = 0; i < 3; i++) EXPECT_EQ("\\G", std::get<1>(results[i]));
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("select * from whatever", std::get<0>(results[1]));
EXPECT_EQ("drop database whatever", std::get<0>(results[2]));
}
TEST_F(TestMySQLSplitter, multiple_statements_mixed_delimiters) {
sql =
"show databases;\n"
"select * from whatever\\G\n"
"drop database whatever;\n";
send_sql(sql);
EXPECT_EQ(3, results.size());
EXPECT_EQ(";", std::get<1>(results[0]));
EXPECT_EQ("\\G", std::get<1>(results[1]));
EXPECT_EQ(";", std::get<1>(results[2]));
EXPECT_EQ("", context);
EXPECT_EQ("show databases", std::get<0>(results[0]));
EXPECT_EQ("select * from whatever", std::get<0>(results[1]));
EXPECT_EQ("drop database whatever", std::get<0>(results[2]));
}
TEST_F(TestMySQLSplitter, delimiter_change) {
send_sql("delimiter \\\n");
ASSERT_EQ(1, results.size());
EXPECT_EQ("DELIMITER cannot contain a backslash character",
std::get<0>(results[0]));
EXPECT_EQ(";", delim);
send_sql("delimiter ;");
ASSERT_EQ(0, results.size());
EXPECT_EQ(";", delim);
sql =
"delimiter $\n"
"show databases;\n"
"select * from whatever;\n"
"drop database whatever;\n"
"$\n"
"delimiter $\n";
std::string statement =
"show databases;\n"
"select * from whatever;\n"
"drop database whatever;\n";
send_sql(sql);
ASSERT_EQ(1, results.size());
EXPECT_FALSE(std::get<1>(results[0]).empty());
EXPECT_EQ("", context);
EXPECT_EQ(statement, std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, delimiter_change_misc_delimiter) {
send_sql("show databases;", true);
ASSERT_EQ(1, results.size());
EXPECT_EQ(";", std::get<1>(results[0]));
EXPECT_EQ(";", delim);
send_sql("show databases\\G", true);
ASSERT_EQ(1, results.size());
EXPECT_EQ("\\G", std::get<1>(results[0]));
EXPECT_EQ(";", delim);
send_sql("delimiter //\n", true);
ASSERT_EQ(0, results.size());
EXPECT_EQ("//", delim);
send_sql_incr("show databases//");
ASSERT_EQ(1, results.size());
EXPECT_EQ("//", std::get<1>(results[0]));
EXPECT_EQ("//", delim);
results.clear();
send_sql_incr("show databases\\G");
ASSERT_EQ(1, results.size());
EXPECT_EQ("//", delim);
EXPECT_EQ("\\G", std::get<1>(results[0]));
results.clear();
send_sql_incr("show databases; select 11 as a\\G select 12 as b//");
EXPECT_EQ(2, results.size());
EXPECT_EQ("show databases; select 11 as a", std::get<0>(results[0]));
EXPECT_EQ("select 12 as b", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, single_line_mysql_extension) {
sql =
"/*! SET TIME_ZONE='+00:00' */;\n"
"show databases;";
send_sql(sql);
EXPECT_EQ("", context);
EXPECT_EQ(2, results.size());
EXPECT_EQ("/*! SET TIME_ZONE='+00:00' */", std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, multi_line_mysql_extension_in_batch) {
std::string view =
"/*!50001 CREATE VIEW `ActorLimit10` AS SELECT "
"1 AS `actor_id`,\n"
"1 AS `first_name`,\n"
"1 AS `last_name`,\n"
"1 AS `last_update`*/";
sql = view + ";\nshow databases;";
send_sql(sql);
EXPECT_EQ("", context);
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ(view, std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, multi_line_mysql_extension_line_by_line) {
send_sql_incr("/*!50001 CREATE VIEW `ActorLimit10`");
EXPECT_EQ("-", context); // Multiline comment flag is set
EXPECT_EQ(0, static_cast<int>(results.size()));
std::string expected = "/*!50001 CREATE VIEW `ActorLimit10`";
// EXPECT_EQ(expected, std::get<0>(results[0]));
send_sql_incr("1 AS `actor_id`,");
EXPECT_EQ("-", context); // Multiline comment flag continues
EXPECT_EQ(0, static_cast<int>(results.size()));
expected += "1 AS `actor_id`,";
// EXPECT_EQ(expected, std::get<0>(results[0]));
send_sql_incr("1 AS `last_name`,");
EXPECT_EQ("-", context); // Multiline comment flag continues
EXPECT_EQ(0, static_cast<int>(results.size()));
expected += "1 AS `last_name`,";
// EXPECT_EQ(expected, std::get<0>(results[0]));
send_sql_incr("1 AS `last_update`*/;");
EXPECT_EQ("", context); // Multiline comment flag is done
EXPECT_EQ(1, static_cast<int>(results.size()));
expected += "1 AS `last_update`*/";
EXPECT_EQ(expected, std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, single_line_optimizer_hint) {
sql =
"/*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */;\n"
"show databases;";
send_sql(sql);
EXPECT_EQ("", context);
EXPECT_EQ(2, results.size());
EXPECT_EQ("/*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */",
std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, multi_line_optimizer_hint_in_batch) {
std::string optimizer =
"/*+ BKA(t1) "
"NO_BKA(t2)*/";
sql = optimizer + ";\nshow databases;";
send_sql(sql);
EXPECT_EQ("", context);
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ(optimizer, std::get<0>(results[0]));
EXPECT_EQ("show databases", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, multi_line_optimizer_hint_line_by_line) {
send_sql_incr("/*+ BKA(t1) ");
EXPECT_EQ("-", context); // Multiline comment flag is set
EXPECT_EQ(0, static_cast<int>(results.size()));
std::string expected = "/*+ BKA(t1) ";
// EXPECT_EQ(expected, std::get<0>(results[0]));
send_sql_incr("NO_BKA(t2)*/;");
EXPECT_EQ("", context); // Multiline comment flag is done
EXPECT_EQ(1, static_cast<int>(results.size()));
expected += "NO_BKA(t2)*/";
EXPECT_EQ(expected, std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, continued_stmt_multiline_comment) {
send_sql_incr("SELECT 1 AS _one /*");
EXPECT_EQ("-", context); // Continued statement flag
EXPECT_EQ(0, static_cast<int>(results.size()));
// The first range is the incomplete statement
std::string expected = "SELECT 1 AS _one /*";
// EXPECT_EQ(expected, std::get<0>(results[0]));
send_sql_incr("comment text */;");
EXPECT_EQ("", context);
EXPECT_EQ(1, static_cast<int>(results.size()));
// The first range is the incomplete statement
expected += "comment text */";
EXPECT_EQ(expected, std::get<0>(results[0]));
}
TEST_F(TestMySQLSplitter, continued_stmt_dash_dash_comment) {
send_sql_incr("select 1 as one -- sample text\n");
EXPECT_EQ("-", context); // Continued statement flag
EXPECT_EQ(0, static_cast<int>(results.size()));
// The first range is the incomplete statement
std::string expected = "select 1 as one ";
// EXPECT_EQ(expected, std::get<0>(results[0]));
send_sql_incr(";select 2 as two;");
EXPECT_EQ("", context);
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ("select 1 as one -- sample text\n", std::get<0>(results[0]));
// The second range is the second statement
expected = "select 2 as two";
EXPECT_EQ(expected, std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, continued_stmt_dash_dash_comment_batch) {
send_sql_incr("select 1 as one -- sample text\n;select 2 as two;");
EXPECT_EQ("", context);
EXPECT_EQ(2, static_cast<int>(results.size()));
// The first range is the incomplete statement
std::string expected = "select 1 as one -- sample text\n";
EXPECT_EQ(expected, std::get<0>(results[0]));
// The 2nd range is the second statement
expected = "select 2 as two";
EXPECT_EQ(expected, std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, continued_stmt_hash_comment) {
send_sql_incr("select 1 as one #sample text\n");
EXPECT_EQ("-", context); // Continued statement flag
EXPECT_EQ(0, static_cast<int>(results.size()));
// The first range is the incomplete statement
std::string expected = "select 1 as one ";
// EXPECT_EQ(expected, std::get<0>(results[0]));
send_sql_incr(";select 2 as two;");
EXPECT_EQ("", context);
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ("select 1 as one #sample text\n", std::get<0>(results[0]));
// The second range is the second statement
expected = "select 2 as two";
EXPECT_EQ(expected, std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, continued_stmt_hash_comment_batch) {
send_sql_incr("select 1 as one #sample text\n;select 2 as two;");
EXPECT_EQ("", context);
EXPECT_EQ(2, static_cast<int>(results.size()));
// The first range is the incomplete statement
std::string expected = "select 1 as one #sample text\n";
EXPECT_EQ(expected, std::get<0>(results[0]));
// The 2nd range is the second statement
expected = "select 2 as two";
EXPECT_EQ(expected, std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, multiline_comment_bug) {
send_sql("/* * */ select 1;"); // This loops forever before bugfix
EXPECT_EQ(1, static_cast<int>(results.size()));
EXPECT_EQ("/* * */ select 1", std::get<0>(results[0]));
send_sql("select 0; /* / */ select 1;");
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ("select 0", std::get<0>(results[0]));
EXPECT_EQ("/* / */ select 1", std::get<0>(results[1]));
send_sql("/*/ */ select 1;");
EXPECT_EQ(1, static_cast<int>(results.size()));
EXPECT_EQ("/*/ */ select 1", std::get<0>(results[0]));
send_sql("/* /*/ select 1;");
EXPECT_EQ(1, static_cast<int>(results.size()));
EXPECT_EQ("/* /*/ select 1", std::get<0>(results[0]));
send_sql("/**/ select 1;");
EXPECT_EQ(1, static_cast<int>(results.size()));
EXPECT_EQ("/**/ select 1", std::get<0>(results[0]));
send_sql("select 1 /*\n-- */;");
EXPECT_EQ(1, static_cast<int>(results.size()));
EXPECT_EQ("select 1 /*\n-- */", std::get<0>(results[0]));
send_sql("/;");
EXPECT_EQ(1, static_cast<int>(results.size()));
EXPECT_EQ("/", std::get<0>(results[0]));
send_sql("select */;");
EXPECT_EQ(1, static_cast<int>(results.size()));
EXPECT_EQ("select */", std::get<0>(results[0]));
// single line comments and quotes are not supported inside /* */
send_sql("select 1 /*\n-- */;\n*/;");
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ("select 1 /*\n-- */", std::get<0>(results[0]));
EXPECT_EQ("*/", std::get<0>(results[1]));
send_sql("select 1 /*+\n-- */;\n*/;");
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ("select 1 /*+\n-- */", std::get<0>(results[0]));
EXPECT_EQ("*/", std::get<0>(results[1]));
send_sql("select 1 /* '*/;' */;'");
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ("select 1 /* '*/", std::get<0>(results[0]));
EXPECT_EQ("' */;'", std::get<0>(results[1]));
send_sql("select 1 /*+ '*/;' */;'");
EXPECT_EQ(2, static_cast<int>(results.size()));
EXPECT_EQ("select 1 /*+ '*/", std::get<0>(results[0]));
EXPECT_EQ("' */;'", std::get<0>(results[1]));
}
TEST_F(TestMySQLSplitter, conditional_comments) {
send_sql("/*! select 1 */;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select 1 */", std::get<0>(results[0]));
send_sql("/*! select 1 /* */ */;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select 1 /* */ */", std::get<0>(results[0]));
send_sql("/*! select 1 /*+ */ */;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select 1 /*+ */ */", std::get<0>(results[0]));
// single line comments and quotes ARE supported inside /* */
send_sql("/*! select 1 /*! */ */;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select 1 /*! */ */", std::get<0>(results[0]));
send_sql("/*! select 1 \n-- */;\n/* */ */;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select 1 \n-- */;\n/* */ */", std::get<0>(results[0]));
send_sql("/*! select '*/;' */ select 1;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select '*/;' */ select 1", std::get<0>(results[0]));
send_sql("/*! select '*/;' */;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select '*/;' */", std::get<0>(results[0]));
send_sql("/*! select \n-- */;\n */;");
EXPECT_EQ(1U, results.size());
EXPECT_EQ("/*! select \n-- */;\n */", std::get<0>(results[0]));
send_sql("/*! select '\n-- */2,3'*/ xyz; select `;`;");
EXPECT_EQ(2U, results.size());
EXPECT_EQ("/*! select '\n-- */2,3'*/ xyz", std::get<0>(results[0]));
EXPECT_EQ("select `;`", std::get<0>(results[1]));
send_sql("/*! select '\n-- */2,3'*/; select `;`;");
EXPECT_EQ(2U, results.size());
EXPECT_EQ("/*! select '\n-- */2,3'*/", std::get<0>(results[0]));
EXPECT_EQ("select `;`", std::get<0>(results[1]));
}
#if 0
// this is not a bug, old client will only accept delimiter if its at
// the beginning of statement
TEST_F(TestMySQLSplitter, multiline_comment_bug_delim) {
send_sql("select 1; /* * */ delimiter //\nselect 2//\nfoo;bar//\n");
EXPECT_EQ(3, static_cast<int>(results.size()));
EXPECT_EQ("select 1", std::get<0>(results[0]));
EXPECT_EQ("select 2", std::get<0>(results[1]));
EXPECT_EQ("foo;bar", std::get<0>(results[2]));
}
#endif
// New tests
class Statement_splitter : public ::testing::TestWithParam<int> {
protected:
std::vector<std::string> split_batch(std::string_view sql,
bool ansi_quotes = false,
bool no_backslash_escapes = false) {
std::stringstream ss(std::string{sql});
std::vector<std::tuple<std::string, std::string, size_t>> r;
if (GetParam() == 0)
r = split_sql_stream(
&ss, sql.empty() ? 1 : sql.length(),
[](std::string_view err) {
throw std::runtime_error(std::string{err});
},
ansi_quotes, no_backslash_escapes, &delimiter);
else
r = split_sql_stream(
&ss, GetParam(),
[](std::string_view err) {
throw std::runtime_error(std::string{err});
},
ansi_quotes, no_backslash_escapes, &delimiter);
std::vector<std::string> stmts;
stmts.reserve(r.size());
for (const auto &i : r) stmts.push_back(std::get<0>(i) + std::get<1>(i));
return stmts;
}
void check_lnum(const std::vector<std::string> &stmts) {
std::string sql;
for (const auto &s : stmts) {
sql.append(s);
}
std::stringstream ss(sql);
std::vector<std::tuple<std::string, std::string, size_t>> r;
if (GetParam() == 0)
r = split_sql_stream(
&ss, sql.empty() ? 1 : sql.length(),
[](std::string_view err) {
throw std::runtime_error(std::string{err});
},
false, false, &delimiter);
else
r = split_sql_stream(
&ss, GetParam(),
[](std::string_view err) {
throw std::runtime_error(std::string{err});
},
false, false, &delimiter);
size_t lnum = 1;
auto s = stmts.begin();
for (const auto &i : r) {
ASSERT_TRUE(s != stmts.end());
if (s->compare(0, strlen("delimiter"), "delimiter") == 0) {
// delimiter lines are skipped
lnum += std::count(s->begin(), s->end(), '\n');
++s;
}
SCOPED_TRACE(std::to_string(lnum) + ") " + *s);
EXPECT_EQ(lnum, std::get<2>(i));
EXPECT_EQ(shcore::str_rstrip(*s, "\n"), std::get<0>(i) + std::get<1>(i));
lnum += std::count(s->begin(), s->end(), '\n');
++s;
}
}
std::string delimiter = ";";
};
using strv = std::vector<std::string>;
TEST_P(Statement_splitter, basic) {
EXPECT_EQ(strv({}), split_batch(""));
EXPECT_EQ(strv({";"}), split_batch(";"));
EXPECT_EQ(strv({"'foo\\'b;a''r';"}), split_batch("'foo\\'b;a''r';"));
EXPECT_EQ(strv({"select 1;", "select 2;"}),
split_batch("select 1; select 2;"));
EXPECT_EQ(strv({"select 1;", "select 2"}), split_batch("select 1; select 2"));
EXPECT_EQ(strv({"select 1;", "select 2;", "select\n3;"}),
split_batch(R"*(select 1;
select 2;select
3;)*"));
}
TEST_P(Statement_splitter, comments) {
EXPECT_EQ(strv({"/* one\n*/"}), split_batch(R"*(/* one
*/)*"));
EXPECT_EQ(strv({"/* one **/"}), split_batch(R"*(/* one **/)*"));
EXPECT_EQ(strv({"/** one */"}), split_batch(R"*(/** one */)*"));
EXPECT_EQ(strv({"/*/ one */"}), split_batch(R"*(/*/ one */)*"));
EXPECT_EQ(strv({"/* one /*/"}), split_batch(R"*(/* one /*/)*"));
EXPECT_EQ(strv({"/* one *"}), split_batch(R"*(/* one *)*"));
EXPECT_EQ(strv({"/* one;\n--\ntwo */"}), split_batch(R"*(/* one;
--
two */)*"));
EXPECT_EQ(strv({"/* one\n--\ntwo */"}), split_batch(R"*(/* one
--
two */)*"));
EXPECT_EQ(strv({"/*! x */ select 1;", "select 2;", "select /* x */ 3;"}),
split_batch(R"*(/*! x */ select 1; select 2;
select /* x */ 3;)*"));
EXPECT_EQ(strv({R"*(/* x
*/ select /* y
z */)*"}),
split_batch(R"*(/* x
*/ select /* y
z */)*"));
EXPECT_EQ(strv({"select 1# bla;\n;", "-- ;;;#", "select 2;"}),
split_batch(R"*(select 1# bla;
;
-- ;;;#
select 2;)*"));
}
TEST_P(Statement_splitter, strings) {
EXPECT_EQ(strv({"select ';';", "select 'delimiter ;';", "select 'foo\nbar';",
"'foo\\'b;a''r';"}),
split_batch("select ';'; select 'delimiter ;'; select "
"'foo\nbar';'foo\\'b;a''r';"));
EXPECT_EQ(
strv({"select `foo`;", "select `Foo\nBar`;", "select `FOO;\\`,\n`BAR`;"}),
split_batch(R"*(select `foo`;
select `Foo
Bar`;
select `FOO;\`,
`BAR`;)*"));
EXPECT_EQ(strv({"select 'foo';", "select 'Foo\nBar';",
"select 'FOO\\';',\n'BAR';"}),
split_batch(R"*(select 'foo';
select 'Foo
Bar'; select 'FOO\';',
'BAR';)*"));
EXPECT_EQ(strv({"/* foo; bar\nbla;\nble */\nselect 1;",
"/* ; bla -- */ select 1, /* # */2, 3;"}),
split_batch(R"*(/* foo; bar
bla;
ble */
select 1;
/* ; bla -- */ select 1, /* # */2, 3;
)*"));
}
TEST_P(Statement_splitter, ansi_quotes) {
// if ansi_quotes then "" is handled the same way as ``
EXPECT_EQ(strv({R"*("a"";b";)*", "'a'';b';", "`a``;b`;"}),
split_batch(R"*("a"";b"; 'a'';b'; `a``;b`;)*", false));
const auto s1 = R"*("a\";b"; 'a\';b'; `a\`;b`;)*";
const auto expected_s1 =
strv({R"*("a\";b";)*", R"*('a\';b';)*", R"*(`a\`;)*", R"*(b`;)*"});
EXPECT_EQ(expected_s1, split_batch(s1, false));
EXPECT_EQ(strv({R"*("a"";b";)*", "'a'';b';", "`a``;b`;"}),
split_batch(R"*("a"";b"; 'a'';b'; `a``;b`;)*", true));
const auto s2 = R"*("a\";b; 'a\';b'; `a\`;b;)*";
const auto expected_s2 =
strv({R"*("a\";)*", R"*(b;)*", R"*('a\';b';)*", R"*(`a\`;)*", R"*(b;)*"});
EXPECT_EQ(expected_s2, split_batch(s2, true));
}
TEST_P(Statement_splitter, no_backslash_escapes) {
std::array<std::string_view, 16> strs{
R"(select 'a'; select 1;)",
R"(select 'a''; select 2;)",
R"(select 'a'''; select 3;)",
R"(select 'a\'; select 4;)",
R"(select 'a\''; select 5;)",
R"(select "a"; select 6;)",
R"(select "a""; select 7;)",
R"(select "a"""; select 8;)",
R"(select "a\"; select 9;)",
R"(select "a\""; select 10;)",
R"(select `a`; select 11;)",
R"(select `a``; select 12;)",
R"(select `a```; select 13;)",
R"(select `a\`; select 14;)",
R"(select `a\``; select 15;)",
R"(create table t2 (`c1\` INT); select 16;)"};
for (size_t i = 0; i < strs.size(); i++) {
auto str = strs[i];
SCOPED_TRACE(shcore::str_format("Test %zu: %.*s", i,
static_cast<int>(str.size()), str.data()));
auto stat = split_batch(str, false, false);
switch (i) {
case 1:
case 3:
case 6:
case 8:
case 11:
case 14:
EXPECT_EQ(stat.size(), 1);
EXPECT_EQ(stat[0], str);
break;
default:
EXPECT_EQ(stat.size(), 2);
EXPECT_EQ(str.substr(0, str.find_first_of(';') + 1), stat[0]);
EXPECT_EQ(str.substr(str.find("; ") + 2), stat[1]);
}
stat = split_batch(str, true, false);
switch (i) {
case 1:
case 3:
case 6:
case 9:
case 11:
case 14:
EXPECT_EQ(stat.size(), 1);
EXPECT_EQ(stat[0], str);
break;
default:
EXPECT_EQ(stat.size(), 2);
EXPECT_EQ(str.substr(0, str.find_first_of(';') + 1), stat[0]);
EXPECT_EQ(str.substr(str.find("; ") + 2), stat[1]);
}
stat = split_batch(str, false, true);
switch (i) {
case 1:
case 4:
case 6:
case 9:
case 11:
case 14:
EXPECT_EQ(stat.size(), 1);
EXPECT_EQ(stat[0], str);
break;
default:
EXPECT_EQ(stat.size(), 2);
EXPECT_EQ(str.substr(0, str.find_first_of(';') + 1), stat[0]);
EXPECT_EQ(str.substr(str.find("; ") + 2), stat[1]);
}
stat = split_batch(str, true, true);
switch (i) {
case 1:
case 4:
case 6:
case 9:
case 11:
case 14:
EXPECT_EQ(stat.size(), 1);
EXPECT_EQ(stat[0], str);
break;
default:
EXPECT_EQ(stat.size(), 2);
EXPECT_EQ(str.substr(0, str.find_first_of(';') + 1), stat[0]);
EXPECT_EQ(str.substr(str.find("; ") + 2), stat[1]);
}
}
}
TEST_P(Statement_splitter, commands) {
EXPECT_EQ(strv({"\\g", ";"}), split_batch(R"*(\g;)*"));
EXPECT_EQ(strv({"select 1\\g", "select 2;", "\\w", "select 3,4\\G"}),
split_batch(R"*(select 1\gselect 2;select 3\w,4\G)*"));
EXPECT_EQ(strv({"\\w", "/* comment */\n\nselect 1;"}),
split_batch(R"*(/* comment */
\w
select 1;)*"));
EXPECT_EQ(strv({"\\W", "\\w", "select 1\\g"}),
split_batch("select 1\\W\\w\\g"));
}
TEST_P(Statement_splitter, bugs) {
EXPECT_EQ(strv({"\\q", "\\;", "x\n\n\n"}), split_batch("x\n\\q\n\\;\n"));
}
TEST_P(Statement_splitter, delimiter) {
delimiter = ";";
EXPECT_THROW(split_batch("delimiter;\n"), std::runtime_error);
EXPECT_EQ(";", delimiter);
EXPECT_THROW(split_batch("delimiter;"), std::runtime_error);
EXPECT_EQ(";", delimiter);
EXPECT_THROW(split_batch(" delimiter;"), std::runtime_error);
EXPECT_EQ(";", delimiter);
delimiter = ";";
EXPECT_EQ(strv({}), split_batch("delimiter ;;\n"));
EXPECT_EQ(";;", delimiter);
delimiter = ";";
EXPECT_EQ(strv({"x;"}), split_batch("x;delimiter ;;\n"));
EXPECT_EQ(";;", delimiter);
delimiter = ";";
EXPECT_EQ(strv({}), split_batch(" delimiter ;;\n"));
EXPECT_EQ(";;", delimiter);
delimiter = ";";
EXPECT_EQ(strv({"a;b$"}), split_batch("delimiter\t$\na;b$"));
EXPECT_EQ("$", delimiter);
delimiter = ";";
EXPECT_EQ(strv({"a;b$"}), split_batch("delimiter\t $ \t;\na;b$"));
EXPECT_EQ("$", delimiter);
delimiter = ";";
EXPECT_EQ(strv({"a;b$"}), split_batch("delimiter $ \t;\na;b$"));
EXPECT_EQ("$", delimiter);
delimiter = ";";
EXPECT_EQ(strv({}), split_batch("delimiter ;select 1;"));
EXPECT_EQ(";select", delimiter);
delimiter = ";";
EXPECT_EQ(strv({}), split_batch("delimiter ;select 1;\n"));
EXPECT_EQ(";select", delimiter);
delimiter = ";";
EXPECT_EQ(strv({"select 1;\nselect 2$", "select $;"}),
split_batch(R"*(delimiter $
select 1;
select 2$
delimiter ;
select $;)*"));
EXPECT_EQ(";", delimiter);
delimiter = "$";
EXPECT_EQ(strv({"select 1;2$", "select 3"}),
split_batch("select 1;2$select 3"));
delimiter = ";;";
EXPECT_EQ(strv({"select 1;2;;", "select 3"}),
split_batch("select 1;2;;select 3"));
delimiter = ";";
EXPECT_EQ(strv({"create table delimiter (a int);", "select delimiter;"}),
split_batch("create table delimiter (a int); select delimiter;"));
EXPECT_EQ(";", delimiter);
EXPECT_EQ(strv({"drop table\ndelimiter;"}),
split_batch("drop table\ndelimiter;"));
EXPECT_EQ(";", delimiter);
EXPECT_EQ(strv({"drop table\ndelimiter ,\nfoobar;"}),
split_batch("drop table\ndelimiter ,\nfoobar;"));
EXPECT_EQ(";", delimiter);
EXPECT_EQ(strv({"select 1//"}), split_batch("/* bla */\n"
"delimiter //\n"
"select 1//"));
EXPECT_EQ("//", delimiter);
EXPECT_EQ(strv({"-- bla", "select 1$$$"}), split_batch("-- bla\n"
"delimiter $$$\n"
"select 1$$$\n"));
EXPECT_EQ("$$$", delimiter);
EXPECT_EQ(strv({"select 1$$"}), split_batch("/* bla\n"
"bla */\n"
"delimiter $$\n"
"select 1$$\n"));
EXPECT_EQ("$$", delimiter);
EXPECT_EQ(strv({"# comment", "select 1#"}), split_batch("# comment\n"
"delimiter #\n"
"select 1#\n"));
EXPECT_EQ("#", delimiter);
EXPECT_EQ(strv({"select 1!%#", "show databases$$"}),
split_batch("\n"
"DELIMITER !%#\n"
"select 1!%#"
"DELIMITER $$\n"
"\n"
"show databases$$\n"));
EXPECT_EQ("$$", delimiter);
EXPECT_EQ(strv({"show databases$$"}), split_batch("\n"
"DELIMITER ;\n"
"\n"
"DELIMITER $$\n"
"\n"
"show databases$$\n"
"delimiter ;"));
EXPECT_EQ(";", delimiter);
// TODO(alfredo): \d as delimiter change command
// select 1\d@ @
// select 1;\d@ select 2@
// select 1, \w\d @ 2, 3@
}
TEST_P(Statement_splitter, line_numbering) {
// clang-format off
{
SCOPED_TRACE("");
check_lnum({"1;\n",
"2;\n",
"3;"});
}
{
SCOPED_TRACE("");
check_lnum({"-- ;\n", // single-line comments are always a separate stmt
";\n",
"2;"});
}
{
SCOPED_TRACE("");
check_lnum({"# ;\n", // single-line comments are always a separate stmt
";\n",
"2;"});
}
{
SCOPED_TRACE("");
check_lnum({"/* ; */\n"
";\n",
"2;"});
}
{
SCOPED_TRACE("");
check_lnum({"1;", "2;\n",
"3;"});
}
{
SCOPED_TRACE("");
check_lnum({"'foo\n"
"bar';", "x;"});
}
{
SCOPED_TRACE("");
check_lnum({"\"foo\n"
"bar\";", "x;"});
}
{
SCOPED_TRACE("");
check_lnum({"`foo\n"
"bar`;", "x;"});
}
{
SCOPED_TRACE("");
check_lnum({"/*foo\n"
"bar*/;", "x;"});
}
{
SCOPED_TRACE("");
check_lnum({"delimiter $$\n",
";$$\n",
"y$$\n",
"x$$"});
delimiter = ";";
}
{
SCOPED_TRACE("");
check_lnum({"delimiter $$\n",
";\n"
";$$\n",
"y$$\n",
"x$$"});
delimiter = ";";
}
{
SCOPED_TRACE("");
check_lnum({"line 1;\n",
"line 2,\n"
"3\n"
"and 4;\n",
"-- line comment\n",
"/* another comment */ line 6;\n",
"/* line 7\n"
" line 8 */;\n",
"# another comment\n",
"'10 long string\n"
"until line 11';",
"line 11.1;",
"line 11.2;"});
}
// clang-format on
}
namespace {
const auto g_format_parameter = [](const auto &info) {
return "chunk_size_" + std::to_string(info.param);
};
} // namespace
// Runs tests processing script in 1 chunk
INSTANTIATE_TEST_SUITE_P(Statement_splitter_full, Statement_splitter,
::testing::Values(0), g_format_parameter);
// Runs tests processing script in small chunks with size incrementing by 1
INSTANTIATE_TEST_SUITE_P(Statement_splitter_1, Statement_splitter,
::testing::Range(1, 16, 1), g_format_parameter);
// Runs tests processing script in multiple chunks with various sizes
INSTANTIATE_TEST_SUITE_P(Statement_splitter_chunks, Statement_splitter,
::testing::Values(4, 8, 16, 32, 64),
g_format_parameter);
} // namespace utils
} // namespace mysqlshdk