This commit is contained in:
Sergey Magidovich 2015-04-08 12:06:41 +03:00
commit e477d0f7d9
6 changed files with 87 additions and 1072 deletions

View File

@ -11,7 +11,7 @@ cat "$QUERIES_FILE" | sed "s/{table}/${TABLE}/g" | while read query; do
echo -n "[" echo -n "["
for i in $(seq 1 $TRIES); do for i in $(seq 1 $TRIES); do
RES=$(mysql -u root -h 127.0.0.1 -P 3306 --database=test -t -vvv -e "$query" 2>&1 | grep 'in set' | grep -oP '\d+\.\d+') RES=$(mysql -u root -h 127.0.0.1 -P 3306 --database=test -t -vvv -e "$query" 2>&1 | grep ' set ' | grep -oP '\d+\.\d+')
[[ "$?" == "0" ]] && echo -n "$RES" || echo -n "null" [[ "$?" == "0" ]] && echo -n "$RES" || echo -n "null"
[[ "$i" != $TRIES ]] && echo -n ", " [[ "$i" != $TRIES ]] && echo -n ", "

View File

@ -1,3 +1,5 @@
Note: column store in MemSQL was introduced in Feb 2014.
http://www.memsql.com/download/ http://www.memsql.com/download/
http://docs.memsql.com/docs/latest/setup/setup_onprem.html http://docs.memsql.com/docs/latest/setup/setup_onprem.html
wget http://download.memsql.com/8d9f4c4d99a547baa40ba097b171bd15/memsql-3.2.x86_64.deb wget http://download.memsql.com/8d9f4c4d99a547baa40ba097b171bd15/memsql-3.2.x86_64.deb

View File

@ -110,7 +110,11 @@ public:
*/ */
TableFullWriteLockPtr lockForAlter() TableFullWriteLockPtr lockForAlter()
{ {
return std::make_pair(lockDataForAlter(), lockStructureForAlter()); /// Порядок вычисления важен.
auto data_lock = lockDataForAlter();
auto structure_lock = lockStructureForAlter();
return {std::move(data_lock), std::move(structure_lock)};
} }
/** Не дает изменять данные в таблице. (Более того, не дает посмотреть на структуру таблицы с намерением изменить данные). /** Не дает изменять данные в таблице. (Более того, не дает посмотреть на структуру таблицы с намерением изменить данные).

View File

@ -26,6 +26,37 @@ InterpreterRenameQuery::InterpreterRenameQuery(ASTPtr query_ptr_, Context & cont
} }
struct RenameDescription
{
RenameDescription(const ASTRenameQuery::Element & elem, const String & path, const String & current_database) :
from_database_name(elem.from.database.empty() ? current_database : elem.from.database),
from_database_name_escaped(escapeForFileName(from_database_name)),
from_table_name(elem.from.table),
from_table_name_escaped(escapeForFileName(from_table_name)),
from_metadata_path(path + "metadata/" + from_database_name_escaped + "/"
+ (!from_table_name.empty() ? from_table_name_escaped + ".sql" : "")),
to_database_name(elem.to.database.empty() ? current_database : elem.to.database),
to_database_name_escaped(escapeForFileName(to_database_name)),
to_table_name(elem.to.table),
to_table_name_escaped(escapeForFileName(to_table_name)),
to_metadata_path(path + "metadata/" + to_database_name_escaped + "/"
+ (!to_table_name.empty() ? to_table_name_escaped + ".sql" : ""))
{}
String from_database_name;
String from_database_name_escaped;
String from_table_name;
String from_table_name_escaped;
String from_metadata_path;
String to_database_name;
String to_database_name_escaped;
String to_table_name;
String to_table_name_escaped;
String to_metadata_path;
};
void InterpreterRenameQuery::execute() void InterpreterRenameQuery::execute()
{ {
String path = context.getPath(); String path = context.getPath();
@ -37,38 +68,59 @@ void InterpreterRenameQuery::execute()
* или состояние может стать неконсистентным. (Это имеет смысл исправить.) * или состояние может стать неконсистентным. (Это имеет смысл исправить.)
*/ */
for (ASTRenameQuery::Elements::const_iterator it = rename.elements.begin(); it != rename.elements.end(); ++it) std::vector<RenameDescription> descriptions;
descriptions.reserve(rename.elements.size());
/// Для того, чтобы захватывать блокировки таблиц в одном и том же порядке в разных RENAME-ах.
struct UniqueTableName
{ {
String from_database_name = it->from.database.empty() ? current_database : it->from.database; String database_name;
String from_database_name_escaped = escapeForFileName(from_database_name); String table_name;
String from_table_name = it->from.table;
String from_table_name_escaped = escapeForFileName(from_table_name);
String from_metadata_path = path + "metadata/" + from_database_name_escaped + "/" + (!from_table_name.empty() ? from_table_name_escaped + ".sql" : "");
String to_database_name = it->to.database.empty() ? current_database : it->to.database; UniqueTableName(const String & database_name_, const String & table_name_)
String to_database_name_escaped = escapeForFileName(to_database_name); : database_name(database_name_), table_name(table_name_) {}
String to_table_name = it->to.table;
String to_table_name_escaped = escapeForFileName(to_table_name);
String to_metadata_path = path + "metadata/" + to_database_name_escaped + "/" + (!to_table_name.empty() ? to_table_name_escaped + ".sql" : "");
/// Заблокировать таблицу нужно при незаблокированном контексте. bool operator< (const UniqueTableName & rhs) const
StoragePtr table = context.getTable(from_database_name, from_table_name); {
auto table_lock = table->lockForAlter(); return std::tie(database_name, table_name) < std::tie(rhs.database_name, rhs.table_name);
}
};
std::set<UniqueTableName> unique_tables;
for (const auto & elem : rename.elements)
{
descriptions.emplace_back(elem, path, current_database);
unique_tables.emplace(descriptions.back().from_database_name, descriptions.back().from_table_name);
}
std::vector<IStorage::TableFullWriteLockPtr> locks;
locks.reserve(unique_tables.size());
for (const auto & names : unique_tables)
if (auto table = context.tryGetTable(names.database_name, names.table_name))
locks.emplace_back(table->lockForAlter());
/** Все таблицы заблокированы. Теперь можно блокировать Context. Порядок важен, чтобы избежать deadlock-ов.
* Это обеспечивает атомарность всех указанных RENAME с точки зрения пользователя СУБД,
* но лишь в случаях, когда в процессе переименования не было исключений и сервер не падал.
*/
/** Все таблицы переименовываются под глобальной блокировкой. */
Poco::ScopedLock<Poco::Mutex> lock(context.getMutex()); Poco::ScopedLock<Poco::Mutex> lock(context.getMutex());
context.assertTableDoesntExist(to_database_name, to_table_name); for (const auto & elem : descriptions)
{
context.assertTableDoesntExist(elem.to_database_name, elem.to_table_name);
/// Уведомляем таблицу о том, что она переименовывается. Если таблица не поддерживает переименование - кинется исключение. /// Уведомляем таблицу о том, что она переименовывается. Если таблица не поддерживает переименование - кинется исключение.
StoragePtr table = context.getTable(elem.from_database_name, elem.from_table_name);
table->rename(path + "data/" + to_database_name_escaped + "/", to_database_name, to_table_name); table->rename(path + "data/" + elem.to_database_name_escaped + "/", elem.to_database_name, elem.to_table_name);
/// Пишем новый файл с метаданными. /// Пишем новый файл с метаданными.
{ {
String create_query; String create_query;
{ {
ReadBufferFromFile in(from_metadata_path, 1024); ReadBufferFromFile in(elem.from_metadata_path, 1024);
WriteBufferFromString out(create_query); WriteBufferFromString out(create_query);
copyData(in, out); copyData(in, out);
} }
@ -82,22 +134,22 @@ void InterpreterRenameQuery::execute()
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой. /// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
if (!parse_res || (pos != end && *pos != ';')) if (!parse_res || (pos != end && *pos != ';'))
throw Exception(getSyntaxErrorMessage(parse_res, create_query.data(), end, pos, expected, "in file " + from_metadata_path), throw Exception(getSyntaxErrorMessage(parse_res, create_query.data(), end, pos, expected, "in file " + elem.from_metadata_path),
ErrorCodes::SYNTAX_ERROR); ErrorCodes::SYNTAX_ERROR);
typeid_cast<ASTCreateQuery &>(*ast).table = to_table_name; typeid_cast<ASTCreateQuery &>(*ast).table = elem.to_table_name;
Poco::FileOutputStream ostr(to_metadata_path); Poco::FileOutputStream ostr(elem.to_metadata_path);
formatAST(*ast, ostr, 0, false); formatAST(*ast, ostr, 0, false);
ostr << '\n'; ostr << '\n';
} }
/// Переименовываем таблицу в контексте. /// Переименовываем таблицу в контексте.
context.addTable(to_database_name, to_table_name, context.addTable(elem.to_database_name, elem.to_table_name,
context.detachTable(from_database_name, from_table_name)); context.detachTable(elem.from_database_name, elem.from_table_name));
/// Удаляем старый файл с метаданными. /// Удаляем старый файл с метаданными.
Poco::File(from_metadata_path).remove(); Poco::File(elem.from_metadata_path).remove();
} }
} }