2011-11-05 23:31:19 +00:00
|
|
|
|
#include <Poco/File.h>
|
2011-10-24 12:10:59 +00:00
|
|
|
|
#include <Poco/FileStream.h>
|
|
|
|
|
|
2011-11-05 23:31:19 +00:00
|
|
|
|
#include <DB/Common/escapeForFileName.h>
|
|
|
|
|
|
2011-11-01 17:57:37 +00:00
|
|
|
|
#include <DB/IO/WriteBufferFromString.h>
|
|
|
|
|
#include <DB/IO/WriteHelpers.h>
|
|
|
|
|
|
2011-11-06 06:22:52 +00:00
|
|
|
|
#include <DB/DataStreams/MaterializingBlockInputStream.h>
|
2011-11-01 15:16:04 +00:00
|
|
|
|
#include <DB/DataStreams/copyData.h>
|
|
|
|
|
|
2011-08-18 20:33:20 +00:00
|
|
|
|
#include <DB/Parsers/ASTCreateQuery.h>
|
|
|
|
|
#include <DB/Parsers/ASTNameTypePair.h>
|
|
|
|
|
|
|
|
|
|
#include <DB/Storages/StorageLog.h>
|
|
|
|
|
#include <DB/Storages/StorageSystemNumbers.h>
|
|
|
|
|
|
2012-08-20 19:21:04 +00:00
|
|
|
|
#include <DB/Parsers/ParserCreateQuery.h>
|
2012-05-22 20:18:45 +00:00
|
|
|
|
#include <DB/Parsers/formatAST.h>
|
|
|
|
|
|
2011-11-01 15:16:04 +00:00
|
|
|
|
#include <DB/Interpreters/InterpreterSelectQuery.h>
|
2011-08-18 20:33:20 +00:00
|
|
|
|
#include <DB/Interpreters/InterpreterCreateQuery.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
2012-03-05 00:09:41 +00:00
|
|
|
|
InterpreterCreateQuery::InterpreterCreateQuery(ASTPtr query_ptr_, Context & context_)
|
|
|
|
|
: query_ptr(query_ptr_), context(context_)
|
2011-11-01 15:16:04 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-01-17 20:31:44 +00:00
|
|
|
|
StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists)
|
2011-08-18 20:33:20 +00:00
|
|
|
|
{
|
2012-08-02 17:33:31 +00:00
|
|
|
|
String path = context.getPath();
|
|
|
|
|
String current_database = context.getCurrentDatabase();
|
2011-11-05 23:31:19 +00:00
|
|
|
|
|
2011-11-01 15:16:04 +00:00
|
|
|
|
ASTCreateQuery & create = dynamic_cast<ASTCreateQuery &>(*query_ptr);
|
2011-08-18 20:33:20 +00:00
|
|
|
|
|
2012-08-02 17:33:31 +00:00
|
|
|
|
String database_name = create.database.empty() ? current_database : create.database;
|
2011-11-05 23:31:19 +00:00
|
|
|
|
String database_name_escaped = escapeForFileName(database_name);
|
2011-10-30 05:19:41 +00:00
|
|
|
|
String table_name = create.table;
|
2011-11-05 23:31:19 +00:00
|
|
|
|
String table_name_escaped = escapeForFileName(table_name);
|
2012-08-02 17:33:31 +00:00
|
|
|
|
String as_database_name = create.as_database.empty() ? current_database : create.as_database;
|
2011-10-31 17:30:44 +00:00
|
|
|
|
String as_table_name = create.as_table;
|
|
|
|
|
|
2012-08-02 17:33:31 +00:00
|
|
|
|
String data_path = path + "data/" + database_name_escaped + "/";
|
|
|
|
|
String metadata_path = path + "metadata/" + database_name_escaped + "/" + (!table_name.empty() ? table_name_escaped + ".sql" : "");
|
2011-08-18 20:33:20 +00:00
|
|
|
|
|
2011-11-05 23:31:19 +00:00
|
|
|
|
/// CREATE|ATTACH DATABASE
|
|
|
|
|
if (!database_name.empty() && table_name.empty())
|
2011-08-19 18:31:14 +00:00
|
|
|
|
{
|
2011-11-05 23:31:19 +00:00
|
|
|
|
if (create.attach)
|
|
|
|
|
{
|
|
|
|
|
if (!Poco::File(data_path).exists())
|
|
|
|
|
throw Exception("Directory " + data_path + " doesn't exist.", ErrorCodes::DIRECTORY_DOESNT_EXIST);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!create.if_not_exists && Poco::File(metadata_path).exists())
|
|
|
|
|
throw Exception("Directory " + metadata_path + " already exists.", ErrorCodes::DIRECTORY_ALREADY_EXISTS);
|
|
|
|
|
if (!create.if_not_exists && Poco::File(data_path).exists())
|
|
|
|
|
throw Exception("Directory " + data_path + " already exists.", ErrorCodes::DIRECTORY_ALREADY_EXISTS);
|
|
|
|
|
|
|
|
|
|
Poco::File(metadata_path).createDirectory();
|
|
|
|
|
Poco::File(data_path).createDirectory();
|
2011-08-19 18:31:14 +00:00
|
|
|
|
}
|
2011-11-05 23:31:19 +00:00
|
|
|
|
|
2013-06-25 12:03:36 +00:00
|
|
|
|
if (!create.if_not_exists || !context.isDatabaseExist(database_name))
|
|
|
|
|
context.addDatabase(database_name);
|
|
|
|
|
|
2013-01-23 17:38:03 +00:00
|
|
|
|
return StoragePtr();
|
2011-11-05 23:31:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-02 17:33:31 +00:00
|
|
|
|
SharedPtr<InterpreterSelectQuery> interpreter_select;
|
2014-03-20 10:59:45 +00:00
|
|
|
|
Block select_sample;
|
2014-05-16 11:06:31 +00:00
|
|
|
|
/// Для таблиц типа вью, чтобы получить столбцы, может понадобиться sample block.
|
|
|
|
|
if (create.select && (!create.attach || (!create.columns && (create.is_view || create.is_materialized_view))))
|
2014-03-20 10:59:45 +00:00
|
|
|
|
{
|
|
|
|
|
interpreter_select = new InterpreterSelectQuery(create.select, context);
|
|
|
|
|
select_sample = interpreter_select->getSampleBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StoragePtr res;
|
2013-10-30 13:52:02 +00:00
|
|
|
|
String storage_name;
|
2013-11-13 14:39:48 +00:00
|
|
|
|
NamesAndTypesListPtr columns = new NamesAndTypesList;
|
2012-08-02 17:33:31 +00:00
|
|
|
|
|
2014-03-20 10:59:45 +00:00
|
|
|
|
StoragePtr as_storage;
|
|
|
|
|
IStorage::TableStructureReadLockPtr as_storage_lock;
|
|
|
|
|
if (!as_table_name.empty())
|
|
|
|
|
{
|
|
|
|
|
as_storage = context.getTable(as_database_name, as_table_name);
|
|
|
|
|
as_storage_lock = as_storage->lockStructure(false);
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-05 23:31:19 +00:00
|
|
|
|
{
|
2012-08-02 17:33:31 +00:00
|
|
|
|
Poco::ScopedLock<Poco::Mutex> lock(context.getMutex());
|
2011-08-18 20:33:20 +00:00
|
|
|
|
|
2014-03-12 13:14:16 +00:00
|
|
|
|
if (!create.is_temporary)
|
2012-08-02 17:33:31 +00:00
|
|
|
|
{
|
2014-03-12 13:14:16 +00:00
|
|
|
|
context.assertDatabaseExists(database_name);
|
|
|
|
|
|
|
|
|
|
if (context.isTableExist(database_name, table_name))
|
|
|
|
|
{
|
|
|
|
|
if (create.if_not_exists)
|
|
|
|
|
return context.getTable(database_name, table_name);
|
|
|
|
|
else
|
|
|
|
|
throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS);
|
|
|
|
|
}
|
2012-08-02 17:33:31 +00:00
|
|
|
|
}
|
2011-11-01 15:16:04 +00:00
|
|
|
|
|
2012-08-02 17:33:31 +00:00
|
|
|
|
/// Получаем список столбцов
|
|
|
|
|
if (create.columns)
|
2011-10-31 17:30:44 +00:00
|
|
|
|
{
|
2012-08-02 17:33:31 +00:00
|
|
|
|
ASTExpressionList & columns_list = dynamic_cast<ASTExpressionList &>(*create.columns);
|
|
|
|
|
for (ASTs::iterator it = columns_list.children.begin(); it != columns_list.children.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
ASTNameTypePair & name_and_type_pair = dynamic_cast<ASTNameTypePair &>(**it);
|
|
|
|
|
StringRange type_range = name_and_type_pair.type->range;
|
|
|
|
|
columns->push_back(NameAndTypePair(
|
|
|
|
|
name_and_type_pair.name,
|
|
|
|
|
context.getDataTypeFactory().get(String(type_range.first, type_range.second - type_range.first))));
|
|
|
|
|
}
|
2011-10-31 17:30:44 +00:00
|
|
|
|
}
|
2012-08-02 17:33:31 +00:00
|
|
|
|
else if (!create.as_table.empty())
|
2014-03-20 10:59:45 +00:00
|
|
|
|
columns = new NamesAndTypesList(as_storage->getColumnsList());
|
2012-08-02 17:33:31 +00:00
|
|
|
|
else if (create.select)
|
|
|
|
|
{
|
|
|
|
|
columns = new NamesAndTypesList;
|
2014-03-20 10:59:45 +00:00
|
|
|
|
for (size_t i = 0; i < select_sample.columns(); ++i)
|
|
|
|
|
columns->push_back(NameAndTypePair(select_sample.getByPosition(i).name, select_sample.getByPosition(i).type));
|
2012-08-02 17:33:31 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw Exception("Incorrect CREATE query: required list of column descriptions or AS section or SELECT.", ErrorCodes::INCORRECT_QUERY);
|
2011-08-18 20:33:20 +00:00
|
|
|
|
|
2012-08-20 19:21:04 +00:00
|
|
|
|
/// Дополняем запрос списком столбцов из другой таблицы, если его не было.
|
|
|
|
|
if (!create.columns)
|
|
|
|
|
{
|
|
|
|
|
ASTPtr columns_list_ptr = new ASTExpressionList;
|
|
|
|
|
ASTExpressionList & columns_list = dynamic_cast<ASTExpressionList &>(*columns_list_ptr);
|
|
|
|
|
|
|
|
|
|
for (NamesAndTypesList::const_iterator it = columns->begin(); it != columns->end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
ASTPtr name_and_type_pair_ptr = new ASTNameTypePair;
|
|
|
|
|
ASTNameTypePair & name_and_type_pair = dynamic_cast<ASTNameTypePair &>(*name_and_type_pair_ptr);
|
|
|
|
|
name_and_type_pair.name = it->first;
|
2013-11-15 09:43:50 +00:00
|
|
|
|
StringPtr type_name = new String(it->second->getName());
|
2012-08-20 19:21:04 +00:00
|
|
|
|
|
|
|
|
|
ParserIdentifierWithOptionalParameters storage_p;
|
2014-03-10 12:25:37 +00:00
|
|
|
|
const char * expected = "";
|
2013-11-13 14:39:48 +00:00
|
|
|
|
const char * pos = type_name->data();
|
|
|
|
|
const char * end = pos + type_name->size();
|
|
|
|
|
|
2012-08-20 19:21:04 +00:00
|
|
|
|
if (!storage_p.parse(pos, end, name_and_type_pair.type, expected))
|
|
|
|
|
throw Exception("Cannot parse data type.", ErrorCodes::SYNTAX_ERROR);
|
|
|
|
|
|
2013-11-13 14:39:48 +00:00
|
|
|
|
name_and_type_pair.type->query_string = type_name;
|
2012-08-20 19:21:04 +00:00
|
|
|
|
columns_list.children.push_back(name_and_type_pair_ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
create.columns = columns_list_ptr;
|
|
|
|
|
create.children.push_back(create.columns);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-02 17:33:31 +00:00
|
|
|
|
/// Выбор нужного движка таблицы
|
|
|
|
|
if (create.storage)
|
2014-02-26 19:50:04 +00:00
|
|
|
|
{
|
2012-08-02 17:33:31 +00:00
|
|
|
|
storage_name = dynamic_cast<ASTFunction &>(*create.storage).name;
|
2014-02-26 19:50:04 +00:00
|
|
|
|
}
|
2012-08-02 17:33:31 +00:00
|
|
|
|
else if (!create.as_table.empty())
|
2012-08-20 19:21:04 +00:00
|
|
|
|
{
|
2014-03-20 10:59:45 +00:00
|
|
|
|
storage_name = as_storage->getName();
|
2012-08-20 19:21:04 +00:00
|
|
|
|
create.storage = dynamic_cast<const ASTCreateQuery &>(*context.getCreateQuery(as_database_name, as_table_name)).storage;
|
2014-02-26 19:50:04 +00:00
|
|
|
|
}
|
2014-03-12 13:14:16 +00:00
|
|
|
|
else if (create.is_temporary)
|
|
|
|
|
{
|
|
|
|
|
storage_name = "Memory";
|
|
|
|
|
ASTFunction * func = new ASTFunction();
|
|
|
|
|
func->name = storage_name;
|
|
|
|
|
create.storage = func;
|
|
|
|
|
}
|
2014-02-26 19:50:04 +00:00
|
|
|
|
else if (create.is_view)
|
2013-11-08 17:43:03 +00:00
|
|
|
|
{
|
|
|
|
|
storage_name = "View";
|
|
|
|
|
ASTFunction * func = new ASTFunction();
|
|
|
|
|
func->name = storage_name;
|
|
|
|
|
create.storage = func;
|
2014-02-26 19:50:04 +00:00
|
|
|
|
}
|
|
|
|
|
else if (create.is_materialized_view)
|
2013-11-08 17:43:03 +00:00
|
|
|
|
{
|
|
|
|
|
storage_name = "MaterializedView";
|
|
|
|
|
ASTFunction * func = new ASTFunction();
|
|
|
|
|
func->name = storage_name;
|
|
|
|
|
create.storage = func;
|
2014-02-26 19:50:04 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2012-08-20 19:21:04 +00:00
|
|
|
|
throw Exception("Incorrect CREATE query: required ENGINE.", ErrorCodes::ENGINE_REQUIRED);
|
2011-08-18 20:33:20 +00:00
|
|
|
|
|
2013-05-06 11:28:45 +00:00
|
|
|
|
res = context.getStorageFactory().get(
|
|
|
|
|
storage_name, data_path, table_name, database_name, context.getGlobalContext(), query_ptr, columns, create.attach);
|
2011-08-18 20:33:20 +00:00
|
|
|
|
|
2012-08-02 17:33:31 +00:00
|
|
|
|
/// Проверка наличия метаданных таблицы на диске и создание метаданных
|
2014-03-12 13:14:16 +00:00
|
|
|
|
if (!assume_metadata_exists && !create.is_temporary)
|
2012-08-02 17:33:31 +00:00
|
|
|
|
{
|
2013-01-17 20:31:44 +00:00
|
|
|
|
if (Poco::File(metadata_path).exists())
|
|
|
|
|
{
|
|
|
|
|
/** Запрос ATTACH TABLE может использоваться, чтобы создать в оперативке ссылку на уже существующую таблицу.
|
|
|
|
|
* Это используется, например, при загрузке сервера.
|
|
|
|
|
*/
|
|
|
|
|
if (!create.attach)
|
|
|
|
|
throw Exception("Metadata for table " + database_name + "." + table_name + " already exists.",
|
|
|
|
|
ErrorCodes::TABLE_METADATA_ALREADY_EXISTS);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/// Меняем CREATE на ATTACH и пишем запрос в файл.
|
|
|
|
|
ASTPtr attach_ptr = query_ptr->clone();
|
|
|
|
|
ASTCreateQuery & attach = dynamic_cast<ASTCreateQuery &>(*attach_ptr);
|
|
|
|
|
|
|
|
|
|
attach.attach = true;
|
|
|
|
|
attach.database.clear();
|
|
|
|
|
attach.as_database.clear();
|
|
|
|
|
attach.as_table.clear();
|
|
|
|
|
attach.if_not_exists = false;
|
2013-12-24 17:01:50 +00:00
|
|
|
|
attach.is_populate = false;
|
|
|
|
|
|
2013-10-30 13:52:02 +00:00
|
|
|
|
/// Для engine VIEW необходимо сохранить сам селект запрос, для остальных - наоборот
|
2013-11-08 17:43:03 +00:00
|
|
|
|
if (storage_name != "View" && storage_name != "MaterializedView")
|
2014-04-08 07:31:51 +00:00
|
|
|
|
attach.select = nullptr;
|
2013-01-17 20:31:44 +00:00
|
|
|
|
|
|
|
|
|
Poco::FileOutputStream metadata_file(metadata_path);
|
|
|
|
|
formatAST(attach, metadata_file, 0, false);
|
|
|
|
|
metadata_file << "\n";
|
|
|
|
|
}
|
2012-08-02 17:33:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-12 13:14:16 +00:00
|
|
|
|
if (create.is_temporary)
|
|
|
|
|
{
|
2014-04-01 13:41:03 +00:00
|
|
|
|
// res->is_dropped = true;
|
2014-03-13 15:00:06 +00:00
|
|
|
|
context.getSessionContext().addExternalTable(table_name, res);
|
2014-03-12 13:14:16 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
context.addTable(database_name, table_name, res);
|
2012-08-02 17:33:31 +00:00
|
|
|
|
}
|
2011-11-01 15:16:04 +00:00
|
|
|
|
|
|
|
|
|
/// Если запрос CREATE SELECT, то вставим в таблицу данные
|
2013-12-10 12:23:43 +00:00
|
|
|
|
if (create.select && storage_name != "View" && (storage_name != "MaterializedView" || create.is_populate))
|
2011-11-06 06:22:52 +00:00
|
|
|
|
{
|
|
|
|
|
BlockInputStreamPtr from = new MaterializingBlockInputStream(interpreter_select->execute());
|
|
|
|
|
copyData(*from, *res->write(query_ptr));
|
|
|
|
|
}
|
2011-08-19 18:31:14 +00:00
|
|
|
|
|
2011-08-18 20:33:20 +00:00
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|