1#pragma once
2
3#include <Access/IAccessEntity.h>
4#include <Core/Types.h>
5#include <Core/UUID.h>
6#include <functional>
7#include <optional>
8#include <vector>
9#include <atomic>
10
11
12namespace Poco { class Logger; }
13
14namespace DB
15{
16/// Contains entities, i.e. instances of classes derived from IAccessEntity.
17/// The implementations of this class MUST be thread-safe.
18class IAccessStorage
19{
20public:
21 IAccessStorage(const String & storage_name_) : storage_name(storage_name_) {}
22 virtual ~IAccessStorage() {}
23
24 /// Returns the name of this storage.
25 const String & getStorageName() const { return storage_name; }
26
27 /// Returns the identifiers of all the entities of a specified type contained in the storage.
28 std::vector<UUID> findAll(std::type_index type) const;
29
30 template <typename EntityType>
31 std::vector<UUID> findAll() const { return findAll(typeid(EntityType)); }
32
33 /// Searchs for an entity with specified type and name. Returns std::nullopt if not found.
34 std::optional<UUID> find(std::type_index type, const String & name) const;
35
36 template <typename EntityType>
37 std::optional<UUID> find(const String & name) const { return find(typeid(EntityType), name); }
38
39 std::vector<UUID> find(std::type_index type, const Strings & names) const;
40
41 template <typename EntityType>
42 std::vector<UUID> find(const Strings & names) const { return find(typeid(EntityType), names); }
43
44 /// Searchs for an entity with specified name and type. Throws an exception if not found.
45 UUID getID(std::type_index type, const String & name) const;
46
47 template <typename EntityType>
48 UUID getID(const String & name) const { return getID(typeid(EntityType), name); }
49
50 std::vector<UUID> getIDs(std::type_index type, const Strings & names) const;
51
52 template <typename EntityType>
53 std::vector<UUID> getIDs(const Strings & names) const { return getIDs(typeid(EntityType), names); }
54
55 /// Returns whether there is an entity with such identifier in the storage.
56 bool exists(const UUID & id) const;
57
58 /// Reads an entity. Throws an exception if not found.
59 template <typename EntityType = IAccessEntity>
60 std::shared_ptr<const EntityType> read(const UUID & id) const;
61
62 template <typename EntityType = IAccessEntity>
63 std::shared_ptr<const EntityType> read(const String & name) const;
64
65 /// Reads an entity. Returns nullptr if not found.
66 template <typename EntityType = IAccessEntity>
67 std::shared_ptr<const EntityType> tryRead(const UUID & id) const;
68
69 template <typename EntityType = IAccessEntity>
70 std::shared_ptr<const EntityType> tryRead(const String & name) const;
71
72 /// Reads only name of an entity.
73 String readName(const UUID & id) const;
74 std::optional<String> tryReadName(const UUID & id) const;
75
76 /// Inserts an entity to the storage. Returns ID of a new entry in the storage.
77 /// Throws an exception if the specified name already exists.
78 UUID insert(const AccessEntityPtr & entity);
79 std::vector<UUID> insert(const std::vector<AccessEntityPtr> & multiple_entities);
80
81 /// Inserts an entity to the storage. Returns ID of a new entry in the storage.
82 std::optional<UUID> tryInsert(const AccessEntityPtr & entity);
83 std::vector<UUID> tryInsert(const std::vector<AccessEntityPtr> & multiple_entities);
84
85 /// Inserts an entity to the storage. Return ID of a new entry in the storage.
86 /// Replaces an existing entry in the storage if the specified name already exists.
87 UUID insertOrReplace(const AccessEntityPtr & entity);
88 std::vector<UUID> insertOrReplace(const std::vector<AccessEntityPtr> & multiple_entities);
89
90 /// Removes an entity from the storage. Throws an exception if couldn't remove.
91 void remove(const UUID & id);
92 void remove(const std::vector<UUID> & ids);
93
94 /// Removes an entity from the storage. Returns false if couldn't remove.
95 bool tryRemove(const UUID & id);
96
97 /// Removes multiple entities from the storage. Returns the list of successfully dropped.
98 std::vector<UUID> tryRemove(const std::vector<UUID> & ids);
99
100 using UpdateFunc = std::function<AccessEntityPtr(const AccessEntityPtr &)>;
101
102 /// Updates an entity stored in the storage. Throws an exception if couldn't update.
103 void update(const UUID & id, const UpdateFunc & update_func);
104 void update(const std::vector<UUID> & ids, const UpdateFunc & update_func);
105
106 /// Updates an entity stored in the storage. Returns false if couldn't update.
107 bool tryUpdate(const UUID & id, const UpdateFunc & update_func);
108
109 /// Updates multiple entities in the storage. Returns the list of successfully updated.
110 std::vector<UUID> tryUpdate(const std::vector<UUID> & ids, const UpdateFunc & update_func);
111
112 class Subscription
113 {
114 public:
115 virtual ~Subscription() {}
116 };
117
118 using SubscriptionPtr = std::unique_ptr<Subscription>;
119 using OnChangedHandler = std::function<void(const UUID & /* id */, const AccessEntityPtr & /* new or changed entity, null if removed */)>;
120
121 /// Subscribes for all changes.
122 /// Can return nullptr if cannot subscribe (identifier not found) or if it doesn't make sense (the storage is read-only).
123 SubscriptionPtr subscribeForChanges(std::type_index type, const OnChangedHandler & handler) const;
124
125 template <typename EntityType>
126 SubscriptionPtr subscribeForChanges(OnChangedHandler handler) const { return subscribeForChanges(typeid(EntityType), handler); }
127
128 /// Subscribes for changes of a specific entry.
129 /// Can return nullptr if cannot subscribe (identifier not found) or if it doesn't make sense (the storage is read-only).
130 SubscriptionPtr subscribeForChanges(const UUID & id, const OnChangedHandler & handler) const;
131 SubscriptionPtr subscribeForChanges(const std::vector<UUID> & ids, const OnChangedHandler & handler) const;
132
133 bool hasSubscription(std::type_index type) const;
134 bool hasSubscription(const UUID & id) const;
135
136protected:
137 virtual std::optional<UUID> findImpl(std::type_index type, const String & name) const = 0;
138 virtual std::vector<UUID> findAllImpl(std::type_index type) const = 0;
139 virtual bool existsImpl(const UUID & id) const = 0;
140 virtual AccessEntityPtr readImpl(const UUID & id) const = 0;
141 virtual String readNameImpl(const UUID & id) const = 0;
142 virtual UUID insertImpl(const AccessEntityPtr & entity, bool replace_if_exists) = 0;
143 virtual void removeImpl(const UUID & id) = 0;
144 virtual void updateImpl(const UUID & id, const UpdateFunc & update_func) = 0;
145 virtual SubscriptionPtr subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const = 0;
146 virtual SubscriptionPtr subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const = 0;
147 virtual bool hasSubscriptionImpl(const UUID & id) const = 0;
148 virtual bool hasSubscriptionImpl(std::type_index type) const = 0;
149
150 static UUID generateRandomID();
151 Poco::Logger * getLogger() const;
152 static String getTypeName(std::type_index type) { return IAccessEntity::getTypeName(type); }
153 [[noreturn]] void throwNotFound(const UUID & id) const;
154 [[noreturn]] void throwNotFound(std::type_index type, const String & name) const;
155 [[noreturn]] void throwBadCast(const UUID & id, std::type_index type, const String & name, std::type_index required_type) const;
156 [[noreturn]] void throwIDCollisionCannotInsert(const UUID & id, std::type_index type, const String & name, std::type_index existing_type, const String & existing_name) const;
157 [[noreturn]] void throwNameCollisionCannotInsert(std::type_index type, const String & name) const;
158 [[noreturn]] void throwNameCollisionCannotRename(std::type_index type, const String & old_name, const String & new_name) const;
159 [[noreturn]] void throwReadonlyCannotInsert(std::type_index type, const String & name) const;
160 [[noreturn]] void throwReadonlyCannotUpdate(std::type_index type, const String & name) const;
161 [[noreturn]] void throwReadonlyCannotRemove(std::type_index type, const String & name) const;
162
163 using Notification = std::tuple<OnChangedHandler, UUID, AccessEntityPtr>;
164 using Notifications = std::vector<Notification>;
165 static void notify(const Notifications & notifications);
166
167private:
168 AccessEntityPtr tryReadBase(const UUID & id) const;
169
170 const String storage_name;
171 mutable std::atomic<Poco::Logger *> log = nullptr;
172};
173
174
175template <typename EntityType>
176std::shared_ptr<const EntityType> IAccessStorage::read(const UUID & id) const
177{
178 auto entity = readImpl(id);
179 auto ptr = typeid_cast<std::shared_ptr<const EntityType>>(entity);
180 if (ptr)
181 return ptr;
182 throwBadCast(id, entity->getType(), entity->getFullName(), typeid(EntityType));
183}
184
185
186template <typename EntityType>
187std::shared_ptr<const EntityType> IAccessStorage::read(const String & name) const
188{
189 return read<EntityType>(getID<EntityType>(name));
190}
191
192
193template <typename EntityType>
194std::shared_ptr<const EntityType> IAccessStorage::tryRead(const UUID & id) const
195{
196 auto entity = tryReadBase(id);
197 if (!entity)
198 return nullptr;
199 return typeid_cast<std::shared_ptr<const EntityType>>(entity);
200}
201
202
203template <typename EntityType>
204std::shared_ptr<const EntityType> IAccessStorage::tryRead(const String & name) const
205{
206 auto id = find<EntityType>(name);
207 return id ? tryRead<EntityType>(*id) : nullptr;
208}
209}
210