getCreateTableQuery and getCreateDatabaseQuery [#CLICKHOUSE-3590]

This commit is contained in:
Nikolai Kochetov 2018-03-13 16:28:32 +03:00
parent cda51eb920
commit d6e9d08d5e
14 changed files with 149 additions and 71 deletions

View File

@ -3,6 +3,11 @@
#include <Interpreters/ExternalDictionaries.h>
#include <Storages/StorageDictionary.h>
#include <common/logger_useful.h>
#include <Parsers/IAST.h>
#include <IO/WriteBufferFromString.h>
#include <IO/Operators.h>
#include <Parsers/ParserCreateQuery.h>
#include <Parsers/parseQuery.h>
namespace DB
{
@ -158,11 +163,34 @@ time_t DatabaseDictionary::getTableMetadataModificationTime(
return static_cast<time_t>(0);
}
ASTPtr DatabaseDictionary::getCreateQuery(
const Context &,
const String &) const
ASTPtr DatabaseDictionary::getCreateTableQuery(
const Context & context,
const String & table_name) const
{
throw Exception("There is no CREATE TABLE query for DatabaseDictionary tables", ErrorCodes::CANNOT_GET_CREATE_TABLE_QUERY);
String query;
{
WriteBufferFromString buffer(query);
auto dictionary = context.getExternalDictionaries().getDictionary(table_name);
auto names_and_types = StorageDictionary::getNamesAndTypes(dictionary->getStructure());
buffer << "CREATE TABLE " << backQuoteIfNeed(name) << '.' << backQuoteIfNeed(table_name) << " (";
buffer << StorageDictionary::generateNamesAndTypesDescription(names_and_types.begin(), names_and_types.end());
buffer << ") Engine = Dictionary(" << backQuoteIfNeed(table_name) << ")";
}
ParserCreateQuery parser;
return parseQuery(parser, query.data(), query.data() + query.size(), "");
}
ASTPtr DatabaseDictionary::getCreateDatabaseQuery(const Context & /*context*/) const
{
String query;
{
WriteBufferFromString buffer(query);
buffer << "CREATE DATABASE " << backQuoteIfNeed(name) << " ENGINE = Dictionary";
}
ParserCreateQuery parser;
return parseQuery(parser, query.data(), query.data() + query.size(), "");
}
void DatabaseDictionary::shutdown()

View File

@ -89,10 +89,12 @@ public:
const Context & context,
const String & table_name) override;
ASTPtr getCreateQuery(
ASTPtr getCreateTableQuery(
const Context & context,
const String & table_name) const override;
ASTPtr getCreateDatabaseQuery(const Context & context) const override;
void shutdown() override;
void drop() override;
};

View File

@ -121,13 +121,18 @@ time_t DatabaseMemory::getTableMetadataModificationTime(
return static_cast<time_t>(0);
}
ASTPtr DatabaseMemory::getCreateQuery(
ASTPtr DatabaseMemory::getCreateTableQuery(
const Context &,
const String &) const
{
throw Exception("There is no CREATE TABLE query for DatabaseMemory tables", ErrorCodes::CANNOT_GET_CREATE_TABLE_QUERY);
}
ASTPtr DatabaseMemory::getCreateDatabaseQuery(const Context &) const
{
throw Exception("There is no CREATE DATABASE query for DatabaseMemory", ErrorCodes::CANNOT_GET_CREATE_TABLE_QUERY);
}
void DatabaseMemory::shutdown()
{
/// You can not hold a lock during shutdown.

View File

@ -80,10 +80,12 @@ public:
const Context & context,
const String & table_name) override;
ASTPtr getCreateQuery(
ASTPtr getCreateTableQuery(
const Context & context,
const String & table_name) const override;
ASTPtr getCreateDatabaseQuery(const Context & context) const override;
void shutdown() override;
void drop() override;
};

View File

@ -335,22 +335,13 @@ void DatabaseOrdinary::removeTable(
}
}
ASTPtr DatabaseOrdinary::getCreateQueryImpl(const String & path, const String & table_name) const
static ASTPtr getQueryFromMetadata(const String & metadata_path)
{
String metadata_path;
if (!Poco::File(metadata_path).exists())
return nullptr;
String query;
if (table_name.empty())
{
metadata_path = detail::getDatabaseMetadataPath(path);
if (!Poco::File(metadata_path).exists())
query = "CREATE DATABASE " + backQuoteIfNeed(name) + " ENGINE = Ordinary";
}
else
metadata_path = detail::getTableMetadataPath(path, table_name);
if (query.empty())
{
ReadBufferFromFile in(metadata_path, 4096);
readStringUntilEOF(query, in);
@ -360,6 +351,20 @@ ASTPtr DatabaseOrdinary::getCreateQueryImpl(const String & path, const String &
return parseQuery(parser, query.data(), query.data() + query.size(), "in file " + metadata_path);
}
static ASTPtr getCreateQueryFromMetadata(const String & metadata_path, const String & database)
{
ASTPtr ast = getQueryFromMetadata(metadata_path);
if (ast)
{
ASTCreateQuery & ast_create_query = typeid_cast<ASTCreateQuery &>(*ast);
ast_create_query.attach = false;
ast_create_query.database = database;
}
return ast;
}
void DatabaseOrdinary::renameTable(
const Context & context,
@ -394,7 +399,9 @@ void DatabaseOrdinary::renameTable(
throw Exception{e};
}
ASTPtr ast = getCreateQueryImpl(metadata_path, table_name);
ASTPtr ast = getQueryFromMetadata(detail::getTableMetadataPath(metadata_path, table_name));
if (!ast)
throw Exception("There is no metadata file for table " + table_name, ErrorCodes::FILE_DOESNT_EXIST);
ASTCreateQuery & ast_create_query = typeid_cast<ASTCreateQuery &>(*ast);
ast_create_query.table = to_table_name;
@ -422,27 +429,40 @@ time_t DatabaseOrdinary::getTableMetadataModificationTime(
}
ASTPtr DatabaseOrdinary::getCreateQuery(
ASTPtr DatabaseOrdinary::getCreateTableQuery(
const Context & context,
const String & table_name) const
{
ASTPtr ast;
try
{
ast = getCreateQueryImpl(metadata_path, table_name);
}
catch (const Exception & e)
{
/// Handle system.* tables for which there are no table.sql files
if (e.code() == ErrorCodes::FILE_DOESNT_EXIST && tryGetTable(context, table_name) != nullptr)
throw Exception("There is no CREATE TABLE query for table " + table_name, ErrorCodes::CANNOT_GET_CREATE_TABLE_QUERY);
throw;
auto table_metadata_path = detail::getTableMetadataPath(metadata_path, table_name);
ast = getCreateQueryFromMetadata(table_metadata_path, name);
if (!ast)
{
/// Handle system.* tables for which there are no table.sql files.
auto msg = tryGetTable(context, table_name)
? "There is no CREATE TABLE query for table "
: "There is no metadata file for table ";
throw Exception(msg + table_name, ErrorCodes::CANNOT_GET_CREATE_TABLE_QUERY);
}
ASTCreateQuery & ast_create_query = typeid_cast<ASTCreateQuery &>(*ast);
ast_create_query.attach = false;
ast_create_query.database = name;
return ast;
}
ASTPtr DatabaseOrdinary::getCreateDatabaseQuery(const Context & /*context*/) const
{
ASTPtr ast;
auto database_metadata_path = detail::getDatabaseMetadataPath(metadata_path);
ast = getCreateQueryFromMetadata(database_metadata_path, name);
if (!ast)
{
/// Handle databases (such as default) for which there are no database.sql files.
String query = "CREATE DATABASE " + backQuoteIfNeed(name) + " ENGINE = Ordinary";
ParserCreateQuery parser;
ast = parseQuery(parser, query.data(), query.data() + query.size(), "");
}
return ast;
}

View File

@ -55,10 +55,12 @@ public:
const Context & context,
const String & table_name) override;
ASTPtr getCreateQuery(
ASTPtr getCreateTableQuery(
const Context & context,
const String & table_name) const override;
ASTPtr getCreateDatabaseQuery(const Context & context) const override;
String getDataPath() const override;
String getMetadataPath() const override;
String getTableMetadataPath(const String & table_name) const override;
@ -68,7 +70,6 @@ public:
private:
void startupTables(ThreadPool * thread_pool);
ASTPtr getCreateQueryImpl(const String & path, const String & table_name) const;
};
}

View File

@ -124,12 +124,14 @@ public:
const Context & context,
const String & name) = 0;
/// Get the CREATE TABLE query for the table or CREATE DATABASE query for database if name is empty.
/// It can also provide information for detached tables for which there is metadata.
virtual ASTPtr getCreateQuery(
/// Get the CREATE TABLE query for the table. It can also provide information for detached tables for which there is metadata.
virtual ASTPtr getCreateTableQuery(
const Context & context,
const String & name) const = 0;
/// Get the CREATE DATABASE query for current database.
virtual ASTPtr getCreateDatabaseQuery(const Context & context) const = 0;
/// Returns path for persistent data storage if the database supports it, empty string otherwise
virtual String getDataPath() const { return {}; }
/// Returns metadata path if the database supports it, empty string otherwise

View File

@ -910,17 +910,17 @@ DatabasePtr Context::detachDatabase(const String & database_name)
}
ASTPtr Context::getCreateQuery(const String & database_name, const String & table_name) const
ASTPtr Context::getCreateTableQuery(const String & database_name, const String & table_name) const
{
auto lock = getLock();
String db = resolveDatabase(database_name, current_database);
assertDatabaseExists(db);
return shared->databases[db]->getCreateQuery(*this, table_name);
return shared->databases[db]->getCreateTableQuery(*this, table_name);
}
ASTPtr Context::getCreateExternalQuery(const String & table_name) const
ASTPtr Context::getCreateExternalTableQuery(const String & table_name) const
{
TableAndCreateASTs::const_iterator jt = external_tables.find(table_name);
if (external_tables.end() == jt)
@ -929,6 +929,15 @@ ASTPtr Context::getCreateExternalQuery(const String & table_name) const
return jt->second.second;
}
ASTPtr Context::getCreateDatabaseQuery(const String & database_name) const
{
auto lock = getLock();
String db = resolveDatabase(database_name, current_database);
assertDatabaseExists(db);
return shared->databases[db]->getCreateDatabaseQuery(*this);
}
Settings Context::getSettings() const
{

View File

@ -241,8 +241,9 @@ public:
UInt16 getTCPPort() const;
/// Get query for the CREATE table.
ASTPtr getCreateQuery(const String & database_name, const String & table_name) const;
ASTPtr getCreateExternalQuery(const String & table_name) const;
ASTPtr getCreateTableQuery(const String & database_name, const String & table_name) const;
ASTPtr getCreateExternalTableQuery(const String & table_name) const;
ASTPtr getCreateDatabaseQuery(const String & database_name) const;
const DatabasePtr getDatabase(const String & database_name) const;
DatabasePtr getDatabase(const String & database_name);

View File

@ -432,7 +432,7 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
String as_database_name = create.as_database.empty() ? context.getCurrentDatabase() : create.as_database;
String as_table_name = create.as_table;
ASTPtr as_create_ptr = context.getCreateQuery(as_database_name, as_table_name);
ASTPtr as_create_ptr = context.getCreateTableQuery(as_database_name, as_table_name);
const auto & as_create = typeid_cast<const ASTCreateQuery &>(*as_create_ptr);
if (as_create.is_view)
@ -461,7 +461,7 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
if (create.attach && !create.storage && !create.columns)
{
// Table SQL definition is available even if the table is detached
auto query = context.getCreateQuery(database_name, table_name);
auto query = context.getCreateTableQuery(database_name, table_name);
auto & as_create = typeid_cast<const ASTCreateQuery &>(*query);
create = as_create; // Copy the saved create query, but use ATTACH instead of CREATE
create.attach = true;

View File

@ -44,8 +44,13 @@ BlockInputStreamPtr InterpreterShowCreateQuery::executeImpl()
if (ast.temporary && !ast.database.empty())
throw Exception("Temporary databases are not possible.", ErrorCodes::SYNTAX_ERROR);
ASTPtr create_query = (ast.temporary ? context.getCreateExternalQuery(ast.table) :
context.getCreateQuery(ast.database, ast.table));
ASTPtr create_query;
if (ast.temporary)
create_query = context.getCreateExternalTableQuery(ast.table);
else if (ast.table.empty())
create_query = context.getCreateDatabaseQuery(ast.database);
else
create_query = context.getCreateTableQuery(ast.database, ast.table);
if (!create_query && ast.temporary)
throw Exception("Unable to show the create query of " + ast.table + ". Maybe it was created by the system.", ErrorCodes::THERE_IS_NO_QUERY);

View File

@ -89,7 +89,6 @@ void StorageDictionary::checkNamesAndTypesCompatibleWithDictionary(const Diction
}
}
void registerStorageDictionary(StorageFactory & factory)
{
factory.registerStorage("Dictionary", [](const StorageFactory::Arguments & args)

View File

@ -4,6 +4,8 @@
#include <Core/Defines.h>
#include <common/MultiVersion.h>
#include <ext/shared_ptr_helper.h>
#include <IO/WriteBufferFromString.h>
#include <IO/Operators.h>
namespace Poco
@ -33,6 +35,26 @@ public:
void drop() override {}
static NamesAndTypesList getNamesAndTypes(const DictionaryStructure & dictionary_structure);
template <typename ForwardIterator>
static std::string generateNamesAndTypesDescription(ForwardIterator begin, ForwardIterator end)
{
std::string description;
{
WriteBufferFromString buffer(description);
bool first = true;
for (; begin != end; ++begin)
{
if (!first)
buffer << ", ";
first = false;
buffer << begin->name << ' ' << begin->type->getName();
}
}
return description;
}
private:
using Ptr = MultiVersion<IDictionaryBase>::Version;
@ -42,24 +64,6 @@ private:
void checkNamesAndTypesCompatibleWithDictionary(const DictionaryStructure & dictionary_structure) const;
template <typename ForwardIterator>
std::string generateNamesAndTypesDescription(ForwardIterator begin, ForwardIterator end) const
{
if (begin == end)
{
return "";
}
std::string description;
for (; begin != end; ++begin)
{
description += ", ";
description += begin->name;
description += ' ';
description += begin->type->getName();
}
return description.substr(2, description.size());
}
protected:
StorageDictionary(const String & table_name_,
const NamesAndTypesList & columns_,

View File

@ -204,7 +204,7 @@ BlockInputStreams StorageSystemTables::read(
try
{
ast = database->getCreateQuery(context, table_name);
ast = database->getCreateTableQuery(context, table_name);
}
catch (const Exception & e)
{