mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-10 01:25:21 +00:00
Merge pull request #52208 from valbok/parse-ddl
MaterializedMySQL: Add tests to parse db and table names from DDL
This commit is contained in:
commit
d051a2a128
@ -3,6 +3,7 @@
|
||||
#if USE_MYSQL
|
||||
|
||||
#include <Databases/MySQL/MaterializedMySQLSyncThread.h>
|
||||
#include <Databases/MySQL/tryParseTableIDFromDDL.h>
|
||||
#include <cstdlib>
|
||||
#include <random>
|
||||
#include <string_view>
|
||||
@ -151,61 +152,6 @@ static void checkMySQLVariables(const mysqlxx::Pool::Entry & connection, const S
|
||||
}
|
||||
}
|
||||
|
||||
static std::tuple<String, String> tryExtractTableNameFromDDL(const String & ddl)
|
||||
{
|
||||
String table_name;
|
||||
String database_name;
|
||||
if (ddl.empty()) return std::make_tuple(database_name, table_name);
|
||||
|
||||
bool parse_failed = false;
|
||||
Tokens tokens(ddl.data(), ddl.data() + ddl.size());
|
||||
IParser::Pos pos(tokens, 0);
|
||||
Expected expected;
|
||||
ASTPtr res;
|
||||
ASTPtr table;
|
||||
if (ParserKeyword("CREATE TEMPORARY TABLE").ignore(pos, expected) || ParserKeyword("CREATE TABLE").ignore(pos, expected))
|
||||
{
|
||||
ParserKeyword("IF NOT EXISTS").ignore(pos, expected);
|
||||
if (!ParserCompoundIdentifier(true).parse(pos, table, expected))
|
||||
parse_failed = true;
|
||||
}
|
||||
else if (ParserKeyword("ALTER TABLE").ignore(pos, expected))
|
||||
{
|
||||
if (!ParserCompoundIdentifier(true).parse(pos, table, expected))
|
||||
parse_failed = true;
|
||||
}
|
||||
else if (ParserKeyword("DROP TABLE").ignore(pos, expected) || ParserKeyword("DROP TEMPORARY TABLE").ignore(pos, expected))
|
||||
{
|
||||
ParserKeyword("IF EXISTS").ignore(pos, expected);
|
||||
if (!ParserCompoundIdentifier(true).parse(pos, table, expected))
|
||||
parse_failed = true;
|
||||
}
|
||||
else if (ParserKeyword("TRUNCATE").ignore(pos, expected))
|
||||
{
|
||||
ParserKeyword("TABLE").ignore(pos, expected);
|
||||
if (!ParserCompoundIdentifier(true).parse(pos, table, expected))
|
||||
parse_failed = true;
|
||||
}
|
||||
else if (ParserKeyword("RENAME TABLE").ignore(pos, expected))
|
||||
{
|
||||
if (!ParserCompoundIdentifier(true).parse(pos, table, expected))
|
||||
parse_failed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_failed = true;
|
||||
}
|
||||
if (!parse_failed)
|
||||
{
|
||||
if (auto table_id = table->as<ASTTableIdentifier>()->getTableId())
|
||||
{
|
||||
database_name = table_id.database_name;
|
||||
table_name = table_id.table_name;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(database_name, table_name);
|
||||
}
|
||||
|
||||
MaterializedMySQLSyncThread::MaterializedMySQLSyncThread(
|
||||
ContextPtr context_,
|
||||
const String & database_name_,
|
||||
@ -868,14 +814,12 @@ void MaterializedMySQLSyncThread::executeDDLAtomic(const QueryEvent & query_even
|
||||
String query = query_event.query;
|
||||
if (!materialized_tables_list.empty())
|
||||
{
|
||||
auto [ddl_database_name, ddl_table_name] = tryExtractTableNameFromDDL(query_event.query);
|
||||
|
||||
if (!ddl_table_name.empty())
|
||||
auto table_id = tryParseTableIDFromDDL(query, query_event.schema);
|
||||
if (!table_id.table_name.empty())
|
||||
{
|
||||
ddl_database_name = ddl_database_name.empty() ? query_event.schema: ddl_database_name;
|
||||
if (ddl_database_name != mysql_database_name || !materialized_tables_list.contains(ddl_table_name))
|
||||
if (table_id.database_name != mysql_database_name || !materialized_tables_list.contains(table_id.table_name))
|
||||
{
|
||||
LOG_DEBUG(log, "Skip MySQL DDL: \n {}", query_event.query);
|
||||
LOG_DEBUG(log, "Skip MySQL DDL for {}.{}:\n{}", table_id.database_name, table_id.table_name, query);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
185
src/Databases/MySQL/tests/gtest_try_parse_table_id_from_ddl.cpp
Normal file
185
src/Databases/MySQL/tests/gtest_try_parse_table_id_from_ddl.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <Databases/MySQL/tryParseTableIDFromDDL.h>
|
||||
|
||||
using namespace DB;
|
||||
|
||||
struct ParseTableIDFromDDLTestCase
|
||||
{
|
||||
String query;
|
||||
String database_name;
|
||||
String table_name;
|
||||
|
||||
ParseTableIDFromDDLTestCase(
|
||||
const String & query_,
|
||||
const String & database_name_,
|
||||
const String & table_name_)
|
||||
: query(query_)
|
||||
, database_name(database_name_)
|
||||
, table_name(table_name_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & ostr, const ParseTableIDFromDDLTestCase & test_case)
|
||||
{
|
||||
return ostr << '"' << test_case.query << "\" extracts `" << test_case.database_name << "`.`" << test_case.table_name << "`";
|
||||
}
|
||||
|
||||
class ParseTableIDFromDDLTest : public ::testing::TestWithParam<ParseTableIDFromDDLTestCase>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(ParseTableIDFromDDLTest, parse)
|
||||
{
|
||||
const auto & [query, expected_database_name, expected_table_name] = GetParam();
|
||||
auto table_id = tryParseTableIDFromDDL(query, "default");
|
||||
EXPECT_EQ(expected_database_name, table_id.database_name);
|
||||
EXPECT_EQ(expected_table_name, table_id.table_name);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(MaterializedMySQL, ParseTableIDFromDDLTest, ::testing::ValuesIn(std::initializer_list<ParseTableIDFromDDLTestCase>{
|
||||
{
|
||||
"SELECT * FROM db.table",
|
||||
"",
|
||||
""
|
||||
},
|
||||
{
|
||||
"CREATE TEMPORARY TABLE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"CREATE TEMPORARY TABLE IF NOT EXISTS db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"CREATE TEMPORARY TABLE table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"CREATE TEMPORARY TABLE IF NOT EXISTS table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"CREATE TABLE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"CREATE TABLE IF NOT EXISTS db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"CREATE TABLE table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"CREATE TABLE IF NOT EXISTS table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"ALTER TABLE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"ALTER TABLE table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TABLE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TABLE IF EXISTS db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TABLE table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TABLE IF EXISTS table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TEMPORARY TABLE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TEMPORARY TABLE IF EXISTS db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TEMPORARY TABLE table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP TEMPORARY TABLE IF EXISTS table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"TRUNCATE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"TRUNCATE TABLE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"TRUNCATE table1",
|
||||
"default",
|
||||
"table1"
|
||||
},
|
||||
{
|
||||
"TRUNCATE TABLE table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"RENAME TABLE db.table",
|
||||
"db",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"RENAME TABLE table",
|
||||
"default",
|
||||
"table"
|
||||
},
|
||||
{
|
||||
"DROP DATABASE db",
|
||||
"",
|
||||
""
|
||||
},
|
||||
{
|
||||
"DROP DATA`BASE db",
|
||||
"",
|
||||
""
|
||||
},
|
||||
{
|
||||
"NOT A SQL",
|
||||
"",
|
||||
""
|
||||
},
|
||||
|
||||
}));
|
44
src/Databases/MySQL/tryParseTableIDFromDDL.cpp
Normal file
44
src/Databases/MySQL/tryParseTableIDFromDDL.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include <Databases/MySQL/tryParseTableIDFromDDL.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/CommonParsers.h>
|
||||
#include <Parsers/ExpressionElementParsers.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
StorageID tryParseTableIDFromDDL(const String & query, const String & default_database_name)
|
||||
{
|
||||
bool is_ddl = false;
|
||||
Tokens tokens(query.data(), query.data() + query.size());
|
||||
IParser::Pos pos(tokens, 0);
|
||||
Expected expected;
|
||||
if (ParserKeyword("CREATE TEMPORARY TABLE").ignore(pos, expected) || ParserKeyword("CREATE TABLE").ignore(pos, expected))
|
||||
{
|
||||
ParserKeyword("IF NOT EXISTS").ignore(pos, expected);
|
||||
is_ddl = true;
|
||||
}
|
||||
else if (ParserKeyword("ALTER TABLE").ignore(pos, expected) || ParserKeyword("RENAME TABLE").ignore(pos, expected))
|
||||
{
|
||||
is_ddl = true;
|
||||
}
|
||||
else if (ParserKeyword("DROP TABLE").ignore(pos, expected) || ParserKeyword("DROP TEMPORARY TABLE").ignore(pos, expected))
|
||||
{
|
||||
ParserKeyword("IF EXISTS").ignore(pos, expected);
|
||||
is_ddl = true;
|
||||
}
|
||||
else if (ParserKeyword("TRUNCATE").ignore(pos, expected))
|
||||
{
|
||||
ParserKeyword("TABLE").ignore(pos, expected);
|
||||
is_ddl = true;
|
||||
}
|
||||
|
||||
ASTPtr table;
|
||||
if (!is_ddl || !ParserCompoundIdentifier(true).parse(pos, table, expected))
|
||||
return StorageID::createEmpty();
|
||||
auto table_id = table->as<ASTTableIdentifier>()->getTableId();
|
||||
if (table_id.database_name.empty())
|
||||
table_id.database_name = default_database_name;
|
||||
return table_id;
|
||||
}
|
||||
|
||||
}
|
11
src/Databases/MySQL/tryParseTableIDFromDDL.h
Normal file
11
src/Databases/MySQL/tryParseTableIDFromDDL.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <base/types.h>
|
||||
#include <Storages/IStorage.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
StorageID tryParseTableIDFromDDL(const String & query, const String & default_database_name);
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user