diff --git a/dbms/src/Databases/DatabaseDictionary.cpp b/dbms/src/Databases/DatabaseDictionary.cpp index 8a10535fd09..6c14312c2e3 100644 --- a/dbms/src/Databases/DatabaseDictionary.cpp +++ b/dbms/src/Databases/DatabaseDictionary.cpp @@ -3,6 +3,11 @@ #include #include #include +#include +#include +#include +#include +#include namespace DB { @@ -158,11 +163,34 @@ time_t DatabaseDictionary::getTableMetadataModificationTime( return static_cast(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() diff --git a/dbms/src/Databases/DatabaseDictionary.h b/dbms/src/Databases/DatabaseDictionary.h index 1308ea20a40..f62bfc3e1c3 100644 --- a/dbms/src/Databases/DatabaseDictionary.h +++ b/dbms/src/Databases/DatabaseDictionary.h @@ -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; }; diff --git a/dbms/src/Databases/DatabaseMemory.cpp b/dbms/src/Databases/DatabaseMemory.cpp index 66b23f07ca6..b50fb78468e 100644 --- a/dbms/src/Databases/DatabaseMemory.cpp +++ b/dbms/src/Databases/DatabaseMemory.cpp @@ -121,13 +121,18 @@ time_t DatabaseMemory::getTableMetadataModificationTime( return static_cast(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. diff --git a/dbms/src/Databases/DatabaseMemory.h b/dbms/src/Databases/DatabaseMemory.h index be095ad3755..dbf37acb5f6 100644 --- a/dbms/src/Databases/DatabaseMemory.h +++ b/dbms/src/Databases/DatabaseMemory.h @@ -80,9 +80,11 @@ public: const Context & context, const String & table_name) override; - ASTPtr getCreateQuery( - const Context & context, - const String & table_name) const override; + ASTPtr getCreateTableQuery( + const Context & context, + const String & table_name) const override; + + ASTPtr getCreateDatabaseQuery(const Context & context) const override; void shutdown() override; void drop() override; diff --git a/dbms/src/Databases/DatabaseOrdinary.cpp b/dbms/src/Databases/DatabaseOrdinary.cpp index 73152387cf6..1f09c118666 100644 --- a/dbms/src/Databases/DatabaseOrdinary.cpp +++ b/dbms/src/Databases/DatabaseOrdinary.cpp @@ -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(*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(*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(*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; } diff --git a/dbms/src/Databases/DatabaseOrdinary.h b/dbms/src/Databases/DatabaseOrdinary.h index 4cd77c85895..9d8d2458df6 100644 --- a/dbms/src/Databases/DatabaseOrdinary.h +++ b/dbms/src/Databases/DatabaseOrdinary.h @@ -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; }; } diff --git a/dbms/src/Databases/IDatabase.h b/dbms/src/Databases/IDatabase.h index 8d62c0e3d45..01c8970901d 100644 --- a/dbms/src/Databases/IDatabase.h +++ b/dbms/src/Databases/IDatabase.h @@ -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 diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 6fddcbc2f05..e3e0e268f98 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -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 { diff --git a/dbms/src/Interpreters/Context.h b/dbms/src/Interpreters/Context.h index bab3be07add..f1e44705f3a 100644 --- a/dbms/src/Interpreters/Context.h +++ b/dbms/src/Interpreters/Context.h @@ -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); diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index 2b6d333d1f6..347d6208e70 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -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(*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(*query); create = as_create; // Copy the saved create query, but use ATTACH instead of CREATE create.attach = true; diff --git a/dbms/src/Interpreters/InterpreterShowCreateQuery.cpp b/dbms/src/Interpreters/InterpreterShowCreateQuery.cpp index 705b9bb4120..a4c3bd7224c 100644 --- a/dbms/src/Interpreters/InterpreterShowCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterShowCreateQuery.cpp @@ -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); diff --git a/dbms/src/Storages/StorageDictionary.cpp b/dbms/src/Storages/StorageDictionary.cpp index 2bb69748c6d..89d59e11b38 100644 --- a/dbms/src/Storages/StorageDictionary.cpp +++ b/dbms/src/Storages/StorageDictionary.cpp @@ -89,7 +89,6 @@ void StorageDictionary::checkNamesAndTypesCompatibleWithDictionary(const Diction } } - void registerStorageDictionary(StorageFactory & factory) { factory.registerStorage("Dictionary", [](const StorageFactory::Arguments & args) diff --git a/dbms/src/Storages/StorageDictionary.h b/dbms/src/Storages/StorageDictionary.h index 0f21373f546..079c65ff134 100644 --- a/dbms/src/Storages/StorageDictionary.h +++ b/dbms/src/Storages/StorageDictionary.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace Poco @@ -33,6 +35,26 @@ public: void drop() override {} static NamesAndTypesList getNamesAndTypes(const DictionaryStructure & dictionary_structure); + template + 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::Version; @@ -42,24 +64,6 @@ private: void checkNamesAndTypesCompatibleWithDictionary(const DictionaryStructure & dictionary_structure) const; - template - 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_, diff --git a/dbms/src/Storages/System/StorageSystemTables.cpp b/dbms/src/Storages/System/StorageSystemTables.cpp index 852392a63d1..493750d5dfa 100644 --- a/dbms/src/Storages/System/StorageSystemTables.cpp +++ b/dbms/src/Storages/System/StorageSystemTables.cpp @@ -204,7 +204,7 @@ BlockInputStreams StorageSystemTables::read( try { - ast = database->getCreateQuery(context, table_name); + ast = database->getCreateTableQuery(context, table_name); } catch (const Exception & e) {