forked from ClickHouse/ClickHouse
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathDatabaseMemory.cpp
More file actions
255 lines (221 loc) · 9.93 KB
/
Copy pathDatabaseMemory.cpp
File metadata and controls
255 lines (221 loc) · 9.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#include <Databases/DDLDependencyVisitor.h>
#include <Databases/DDLLoadingDependencyVisitor.h>
#include <Databases/DatabaseFactory.h>
#include <Databases/DatabaseMemory.h>
#include <Databases/DatabasesCommon.h>
#include <Disks/IDisk.h>
#include <Interpreters/Context.h>
#include <Interpreters/DatabaseCatalog.h>
#include <Parsers/ASTCreateQuery.h>
#include <Parsers/ASTFunction.h>
#include <Common/ZooKeeper/ZooKeeperCommon.h>
#include <Common/quoteString.h>
#include <Storages/IStorage.h>
#include <Core/UUID.h>
namespace DB
{
namespace ErrorCodes
{
extern const int UNKNOWN_TABLE;
extern const int LOGICAL_ERROR;
}
DatabaseMemory::DatabaseMemory(const String & name_, ContextPtr context_)
: DatabaseWithOwnTablesBase(name_, "DatabaseMemory(" + name_ + ")", context_)
, data_path(DatabaseCatalog::getDataDirPath(name_) / "")
{
auto component_guard = Coordination::setCurrentComponent("DatabaseMemory::DatabaseMemory");
/// Temporary database should not have any data at the moment of its creation.
/// In case of starting up after sudden server shutdown, remove the database folder of the temporary database.
if (name_ == DatabaseCatalog::TEMPORARY_DATABASE)
removeDataPath(context_);
}
void DatabaseMemory::createTable(
ContextPtr /*context*/,
const String & table_name,
const StoragePtr & table,
const ASTPtr & query)
{
std::lock_guard lock{mutex};
attachTableUnlocked(table_name, table);
/// Clean the query from temporary flags.
ASTPtr query_to_store = query;
if (query)
{
query_to_store = query->clone();
auto * create = query_to_store->as<ASTCreateQuery>();
if (!create)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Query '{}' is not CREATE query", query->formatForErrorMessage());
cleanupObjectDefinitionFromTemporaryFlags(*create);
}
create_queries.emplace(table_name, query_to_store);
}
void DatabaseMemory::dropTable(
ContextPtr /*context*/,
const String & table_name,
bool /*sync*/)
{
StoragePtr table;
{
std::lock_guard lock{mutex};
table = detachTableUnlocked(table_name);
}
try
{
/// Remove table without lock since
/// - it does not require it
/// - it may cause lock-order-inversion if underlying storage need to resolve tables
table->drop();
if (table->storesDataOnDisk())
{
auto metdata_disk = getDisk();
metdata_disk->removeRecursive(getTableDataPath(table_name));
}
}
catch (...)
{
std::lock_guard lock{mutex};
attachTableUnlocked(table_name, table);
throw;
}
std::lock_guard lock{mutex};
table->is_dropped = true;
create_queries.erase(table_name);
snapshot_detached_tables.erase(table_name);
UUID table_uuid = table->getStorageID().uuid;
if (table_uuid != UUIDHelpers::Nil)
DatabaseCatalog::instance().removeUUIDMappingFinally(table_uuid);
}
ASTPtr DatabaseMemory::getCreateDatabaseQueryImpl() const
{
auto create_query = make_intrusive<ASTCreateQuery>();
create_query->setDatabase(database_name);
create_query->set(create_query->storage, make_intrusive<ASTStorage>());
auto engine = makeASTFunction(getEngineName());
engine->setNoEmptyArgs(true);
create_query->storage->set(create_query->storage->engine, engine);
if (!comment.empty())
create_query->set(create_query->comment, make_intrusive<ASTLiteral>(comment));
return create_query;
}
ASTPtr DatabaseMemory::getCreateTableQueryImpl(const String & table_name, ContextPtr, bool throw_on_error) const
{
std::lock_guard lock{mutex};
auto it = create_queries.find(table_name);
if (it == create_queries.end() || !it->second)
{
if (throw_on_error)
throw Exception(ErrorCodes::UNKNOWN_TABLE, "There is no metadata of table {} in database {}", table_name, database_name);
return {};
}
return it->second->clone();
}
UUID DatabaseMemory::tryGetTableUUID(const String & table_name) const
{
if (auto table = tryGetTable(table_name, getContext()))
return table->getStorageID().uuid;
return UUIDHelpers::Nil;
}
void DatabaseMemory::removeDataPath(ContextPtr)
{
/// This method is called in two cases:
/// 1. During startup for the temporary database (_temporary_and_external_tables) to clean up
/// stale directories from previous server sessions (e.g., after crash or Ctrl+C).
/// Temporary tables with disk-based engines (like MergeTree) may leave behind files that
/// need to be removed.
/// 2. On explicit DROP DATABASE to remove all data.
///
/// We must use removeRecursive() instead of removeDirectoryIfExists() because the directory
/// may contain files from temporary tables. Using removeDirectoryIfExists()
/// would fail or throw an exception if the directory is not empty.
auto db_disk = getDisk();
db_disk->removeRecursive(data_path);
}
void DatabaseMemory::drop(ContextPtr local_context)
{
/// Remove data on explicit DROP DATABASE
removeDataPath(local_context);
}
void DatabaseMemory::alterTable(ContextPtr local_context, const StorageID & table_id, const StorageInMemoryMetadata & metadata, const bool validate_new_create_query)
{
ASTPtr create_query;
{
std::lock_guard lock{mutex};
auto it = tables.find(table_id.table_name);
if (it == tables.end() || (table_id.uuid != UUIDHelpers::Nil && it->second->getStorageID().uuid != table_id.uuid))
throw Exception(ErrorCodes::UNKNOWN_TABLE, "Table {} doesn't exist", table_id.getNameForLogs());
auto it_query = create_queries.find(table_id.table_name);
if (it_query == create_queries.end() || !it_query->second)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot alter: There is no metadata of table {}", table_id.getNameForLogs());
create_query = it_query->second->clone();
}
/// Apply metadata changes to the cloned AST without holding a lock to avoid possible deadlock
/// (i.e. when ALTER contains IN (table)).
applyMetadataChangesToCreateQuery(create_query, metadata, local_context, validate_new_create_query);
/// The create query of the table has been just changed, we need to update dependencies too.
auto ref_dependencies = getDependenciesFromCreateQuery(local_context->getGlobalContext(), table_id.getQualifiedName(), create_query, local_context->getCurrentDatabase());
auto loading_dependencies = getLoadingDependenciesFromCreateQuery(local_context->getGlobalContext(), table_id.getQualifiedName(), create_query);
DatabaseCatalog::instance().checkTableCanBeAddedWithNoCyclicDependencies(table_id.getQualifiedName(), ref_dependencies.dependencies, loading_dependencies);
{
std::lock_guard lock{mutex};
create_queries[table_id.table_name] = create_query;
}
DatabaseCatalog::instance().updateDependencies(table_id, ref_dependencies.dependencies, loading_dependencies, ref_dependencies.mv_from_dependency ? TableNamesSet{ref_dependencies.mv_from_dependency->getQualifiedName()} : TableNamesSet{});
}
std::vector<std::pair<ASTPtr, StoragePtr>> DatabaseMemory::getTablesForBackup(const FilterByNameFunction & filter, const ContextPtr & local_context) const
{
/// We need a special processing for the temporary database.
if (getDatabaseName() != DatabaseCatalog::TEMPORARY_DATABASE)
return DatabaseWithOwnTablesBase::getTablesForBackup(filter, local_context);
std::vector<std::pair<ASTPtr, StoragePtr>> res;
/// `this->tables` for the temporary database doesn't contain real names of tables.
/// That's why we need to call Context::getExternalTables() and then resolve those names using tryResolveStorageID() below.
auto external_tables = local_context->getExternalTables();
for (const auto & [table_name, storage] : external_tables)
{
if (!filter(table_name))
continue;
auto storage_id = local_context->tryResolveStorageID(StorageID{"", table_name}, Context::ResolveExternal);
if (!storage_id)
{
LOG_WARNING(log, "Couldn't resolve the name of temporary table {}", backQuoteIfNeed(table_name));
continue;
}
/// Here `storage_id.table_name` looks like looks like "_tmp_ab9b15a3-fb43-4670-abec-14a0e9eb70f1"
/// it's not the real name of the table.
auto create_table_query = tryGetCreateTableQuery(storage_id.table_name, local_context);
if (!create_table_query)
{
LOG_WARNING(log, "Couldn't get a create query for temporary table {}", backQuoteIfNeed(table_name));
continue;
}
auto * create = create_table_query->as<ASTCreateQuery>();
if (create->getTable() != table_name)
{
/// Probably the database has been just renamed. Use the older name for backup to keep the backup consistent.
LOG_WARNING(log, "Got a create query with unexpected name {} for temporary table {}",
backQuoteIfNeed(create->getTable()), backQuoteIfNeed(table_name));
create_table_query = create_table_query->clone();
create = create_table_query->as<ASTCreateQuery>();
create->setTable(table_name);
}
chassert(storage);
storage->applyMetadataChangesToCreateQueryForBackup(create_table_query);
res.emplace_back(create_table_query, storage);
}
return res;
}
void registerDatabaseMemory(DatabaseFactory & factory);
void registerDatabaseMemory(DatabaseFactory & factory)
{
auto create_fn = [](const DatabaseFactory::Arguments & args)
{
return make_shared<DatabaseMemory>(
args.database_name,
args.context);
};
factory.registerDatabase("Memory", create_fn, {}, Documentation{
.description = "An in-memory database whose metadata is not persisted and is lost on restart; tables and data live only for the duration of the server session.",
.syntax = "ENGINE = Memory",
.related = {"Atomic"}});
}
}