diff options
author | ilnaz <ilnaz@ydb.tech> | 2023-03-24 16:58:17 +0300 |
---|---|---|
committer | ilnaz <ilnaz@ydb.tech> | 2023-03-24 16:58:17 +0300 |
commit | 6897ec78eb22dfc9613232dc1cf6e2e6bb52c71c (patch) | |
tree | f5c7b5f1120ecf0bb5365942abd77c7b99f9cf41 | |
parent | 4a19ef5d96f0b6af337c7b562e8626f067bc0cf7 (diff) | |
download | ydb-6897ec78eb22dfc9613232dc1cf6e2e6bb52c71c.tar.gz |
(refactoring) User attrs
-rw-r--r-- | ydb/core/tx/schemeshard/CMakeLists.darwin-x86_64.txt | 1 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/CMakeLists.linux-aarch64.txt | 1 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/CMakeLists.linux-x86_64.txt | 1 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/CMakeLists.windows-x86_64.txt | 1 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/schemeshard_path_element.cpp | 30 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/schemeshard_path_element.h | 298 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/user_attributes.cpp | 222 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/user_attributes.h | 96 | ||||
-rw-r--r-- | ydb/core/tx/schemeshard/ya.make | 1 |
9 files changed, 344 insertions, 307 deletions
diff --git a/ydb/core/tx/schemeshard/CMakeLists.darwin-x86_64.txt b/ydb/core/tx/schemeshard/CMakeLists.darwin-x86_64.txt index b63e3c58d22..fc366875e06 100644 --- a/ydb/core/tx/schemeshard/CMakeLists.darwin-x86_64.txt +++ b/ydb/core/tx/schemeshard/CMakeLists.darwin-x86_64.txt @@ -264,6 +264,7 @@ target_sources(core-tx-schemeshard PRIVATE ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__get.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__progress.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_validate_ttl.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/user_attributes.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_import_scheme_getter.cpp ) generate_enum_serilization(core-tx-schemeshard diff --git a/ydb/core/tx/schemeshard/CMakeLists.linux-aarch64.txt b/ydb/core/tx/schemeshard/CMakeLists.linux-aarch64.txt index dbe99b1f323..213ff7b5b4e 100644 --- a/ydb/core/tx/schemeshard/CMakeLists.linux-aarch64.txt +++ b/ydb/core/tx/schemeshard/CMakeLists.linux-aarch64.txt @@ -265,6 +265,7 @@ target_sources(core-tx-schemeshard PRIVATE ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__get.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__progress.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_validate_ttl.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/user_attributes.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_import_scheme_getter.cpp ) generate_enum_serilization(core-tx-schemeshard diff --git a/ydb/core/tx/schemeshard/CMakeLists.linux-x86_64.txt b/ydb/core/tx/schemeshard/CMakeLists.linux-x86_64.txt index dbe99b1f323..213ff7b5b4e 100644 --- a/ydb/core/tx/schemeshard/CMakeLists.linux-x86_64.txt +++ b/ydb/core/tx/schemeshard/CMakeLists.linux-x86_64.txt @@ -265,6 +265,7 @@ target_sources(core-tx-schemeshard PRIVATE ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__get.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__progress.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_validate_ttl.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/user_attributes.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_import_scheme_getter.cpp ) generate_enum_serilization(core-tx-schemeshard diff --git a/ydb/core/tx/schemeshard/CMakeLists.windows-x86_64.txt b/ydb/core/tx/schemeshard/CMakeLists.windows-x86_64.txt index 8a5eeb775cb..acc7c9238b5 100644 --- a/ydb/core/tx/schemeshard/CMakeLists.windows-x86_64.txt +++ b/ydb/core/tx/schemeshard/CMakeLists.windows-x86_64.txt @@ -264,6 +264,7 @@ target_sources(core-tx-schemeshard PRIVATE ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__get.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_build_index__progress.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_validate_ttl.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/user_attributes.cpp ${CMAKE_SOURCE_DIR}/ydb/core/tx/schemeshard/schemeshard_import_scheme_getter_fallback.cpp ) generate_enum_serilization(core-tx-schemeshard diff --git a/ydb/core/tx/schemeshard/schemeshard_path_element.cpp b/ydb/core/tx/schemeshard/schemeshard_path_element.cpp index 6cfc783fc18..5882b895b13 100644 --- a/ydb/core/tx/schemeshard/schemeshard_path_element.cpp +++ b/ydb/core/tx/schemeshard/schemeshard_path_element.cpp @@ -1,7 +1,8 @@ #include "schemeshard_path_element.h" -namespace NKikimr { -namespace NSchemeShard { +#include <library/cpp/json/json_reader.h> + +namespace NKikimr::NSchemeShard { TPathElement::TPathElement(TPathId pathId, TPathId parentPathId, TPathId domainPathId, const TString& name, const TString& owner) : PathId(pathId) @@ -256,31 +257,34 @@ void TPathElement::ApplySpecialAttributes() { VolumeSpaceSSDNonrepl.Limit = Max<ui64>(); VolumeSpaceSSDSystem.Limit = Max<ui64>(); ExtraPathSymbolsAllowed = TString(); - for (const auto& item : UserAttrs->Attrs) { - switch (TUserAttributes::ParseName(item.first)) { + DocumentApiVersion = 0; + AsyncReplication = NJson::TJsonValue(); + + for (const auto& [key, value] : UserAttrs->Attrs) { + switch (TUserAttributes::ParseName(key)) { case EAttribute::VOLUME_SPACE_LIMIT: - HandleAttributeValue(item.second, VolumeSpaceRaw.Limit); + HandleAttributeValue(value, VolumeSpaceRaw.Limit); break; case EAttribute::VOLUME_SPACE_LIMIT_SSD: - HandleAttributeValue(item.second, VolumeSpaceSSD.Limit); + HandleAttributeValue(value, VolumeSpaceSSD.Limit); break; case EAttribute::VOLUME_SPACE_LIMIT_HDD: - HandleAttributeValue(item.second, VolumeSpaceHDD.Limit); + HandleAttributeValue(value, VolumeSpaceHDD.Limit); break; case EAttribute::VOLUME_SPACE_LIMIT_SSD_NONREPL: - HandleAttributeValue(item.second, VolumeSpaceSSDNonrepl.Limit); + HandleAttributeValue(value, VolumeSpaceSSDNonrepl.Limit); break; case EAttribute::VOLUME_SPACE_LIMIT_SSD_SYSTEM: - HandleAttributeValue(item.second, VolumeSpaceSSDSystem.Limit); + HandleAttributeValue(value, VolumeSpaceSSDSystem.Limit); break; case EAttribute::EXTRA_PATH_SYMBOLS_ALLOWED: - HandleAttributeValue(item.second, ExtraPathSymbolsAllowed); + HandleAttributeValue(value, ExtraPathSymbolsAllowed); break; case EAttribute::DOCUMENT_API_VERSION: - HandleAttributeValue(item.second, DocumentApiVersion); + HandleAttributeValue(value, DocumentApiVersion); break; case EAttribute::ASYNC_REPLICATION: - HandleAttributeValue(item.second, AsyncReplication); + HandleAttributeValue(value, AsyncReplication); break; default: break; @@ -380,5 +384,5 @@ void TPathElement::SerializeRuntimeAttrs( process(VolumeSpaceSSDNonrepl, "__volume_space_allocated_ssd_nonrepl"); process(VolumeSpaceSSDSystem, "__volume_space_allocated_ssd_system"); } -} + } diff --git a/ydb/core/tx/schemeshard/schemeshard_path_element.h b/ydb/core/tx/schemeshard/schemeshard_path_element.h index fe633992064..fb9f98e95c8 100644 --- a/ydb/core/tx/schemeshard/schemeshard_path_element.h +++ b/ydb/core/tx/schemeshard/schemeshard_path_element.h @@ -2,66 +2,17 @@ #include "schemeshard_types.h" #include "schemeshard_effective_acl.h" -#include "schemeshard_user_attr_limits.h" +#include "user_attributes.h" #include <ydb/core/protos/flat_scheme_op.pb.h> -#include <ydb/core/util/yverify_stream.h> #include <ydb/library/aclib/aclib.h> -#include <library/cpp/json/json_reader.h> +#include <library/cpp/json/json_value.h> #include <util/generic/map.h> #include <util/generic/ptr.h> -#include <util/string/cast.h> -namespace NKikimr { -namespace NSchemeShard { - -class TPath; - -constexpr TStringBuf ATTR_PREFIX = "__"; -constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT = "__volume_space_limit"; -constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_HDD = "__volume_space_limit_hdd"; -constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_SSD = "__volume_space_limit_ssd"; -constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_SSD_NONREPL = "__volume_space_limit_ssd_nonrepl"; -constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_SSD_SYSTEM = "__volume_space_limit_ssd_system"; -constexpr TStringBuf ATTR_EXTRA_PATH_SYMBOLS_ALLOWED = "__extra_path_symbols_allowed"; -constexpr TStringBuf ATTR_DOCUMENT_API_VERSION = "__document_api_version"; -constexpr TStringBuf ATTR_ASYNC_REPLICATION = "__async_replication"; - -inline bool WeakCheck(char c) { - // 33: ! " # $ % & ' ( ) * + , - . / - // 48: 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ - // 65: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - // 91: [ \ ] ^ _ ` - // 97: a b c d e f g h i j k l m n o p q r s t u v w x y z - // 123: { | } ~ - if (c >= 33 && c <= 126) - return true; - return false; -} - -inline bool IsValidPathName_WeakCheck(const TString& name) { - for (auto c: name) { - if (!WeakCheck(c) || c == '/') { - return false; - } - } - return true; -} - -enum class EAttribute { - USER, - UNKNOWN, - VOLUME_SPACE_LIMIT, - VOLUME_SPACE_LIMIT_HDD, - VOLUME_SPACE_LIMIT_SSD, - EXTRA_PATH_SYMBOLS_ALLOWED, // deprecated - VOLUME_SPACE_LIMIT_SSD_NONREPL, - DOCUMENT_API_VERSION, - VOLUME_SPACE_LIMIT_SSD_SYSTEM, - ASYNC_REPLICATION, -}; +namespace NKikimr::NSchemeShard { struct TVolumeSpace { ui64 Raw = 0; @@ -76,247 +27,6 @@ struct TVolumeSpaceLimits { ui64 Limit = Max<ui64>(); }; -enum class EUserAttributesOp { - InitRoot, - MkDir, - AlterUserAttrs, - CreateTable, - CreateSubDomain, - CreateExtSubDomain, - SyncUpdateTenants, - CreateChangefeed, -}; - -struct TUserAttributes: TSimpleRefCount<TUserAttributes> { - using TPtr = TIntrusivePtr<TUserAttributes>; - using TAttrs = TMap<TString, TString>; - - TAttrs Attrs; - ui64 AlterVersion; - TPtr AlterData; - - explicit TUserAttributes(ui64 version) - : AlterVersion(version) - {} - - TUserAttributes(const TUserAttributes&) = default; - - TPtr CreateNextVersion() { - auto result = new TUserAttributes(*this); - ++result->AlterVersion; - return result; - } - - static EAttribute ParseName(TStringBuf name) { - if (name.StartsWith(ATTR_PREFIX)) { - #define HANDLE_ATTR(attr) \ - if (name == ATTR_ ## attr) { \ - return EAttribute::attr; \ - } - HANDLE_ATTR(VOLUME_SPACE_LIMIT); - HANDLE_ATTR(VOLUME_SPACE_LIMIT_HDD); - HANDLE_ATTR(VOLUME_SPACE_LIMIT_SSD); - HANDLE_ATTR(VOLUME_SPACE_LIMIT_SSD_NONREPL); - HANDLE_ATTR(VOLUME_SPACE_LIMIT_SSD_SYSTEM); - HANDLE_ATTR(EXTRA_PATH_SYMBOLS_ALLOWED); - HANDLE_ATTR(DOCUMENT_API_VERSION); - HANDLE_ATTR(ASYNC_REPLICATION); - #undef HANDLE_ATTR - return EAttribute::UNKNOWN; - } - - return EAttribute::USER; - } - - bool ApplyPatch(EUserAttributesOp op, const NKikimrSchemeOp::TAlterUserAttributes& patch, TString& errStr) { - return ApplyPatch(op, patch.GetUserAttributes(), errStr); - } - - template <class TContainer> - bool ApplyPatch(EUserAttributesOp op, const TContainer& patch, TString& errStr) { - for (auto& item: patch) { - const auto& name = item.GetKey(); - - if (item.HasValue()) { - const auto& value = item.GetValue(); - if (!CheckAttribute(op, name, value, errStr)) { - return false; - } - - Attrs[name] = value; - } else { - if (!CheckAttributeRemove(op, name, errStr)) { - return false; - } - - Attrs.erase(name); - } - } - - return true; - } - - void Set(const TString& name, const TString& value) { - Attrs[name] = value; - } - - ui32 Size() const { - return Attrs.size(); - } - - ui64 Bytes() const { - ui64 bytes = 0; - - for (const auto& [key, value] : Attrs) { - bytes += key.size(); - bytes += value.size(); - } - - return bytes; - } - - bool CheckLimits(TString& errStr) const { - const ui64 bytes = Bytes(); - if (bytes > TUserAttributesLimits::MaxBytes) { - errStr = Sprintf("UserAttributes::CheckLimits: user attributes too big: %" PRIu64, bytes); - return false; - } - - return true; - } - - static bool CheckAttribute(EUserAttributesOp op, const TString& name, const TString& value, TString& errStr) { - if (op == EUserAttributesOp::SyncUpdateTenants) { - // Migration, must never fail - return true; - } - - if (name.size() > TUserAttributesLimits::MaxNameLen) { - errStr = Sprintf("UserAttributes: name too long, name# '%s' value# '%s'" - , name.c_str(), value.c_str()); - return false; - } - - if (value.size() > TUserAttributesLimits::MaxValueLen) { - errStr = Sprintf("UserAttributes: value too long, name# '%s' value# '%s'" - , name.c_str(), value.c_str()); - return false; - } - - switch (ParseName(name)) { - case EAttribute::USER: - return true; - case EAttribute::UNKNOWN: - errStr = Sprintf("UserAttributes: unsupported attribute '%s'", name.c_str()); - return false; - case EAttribute::VOLUME_SPACE_LIMIT: - case EAttribute::VOLUME_SPACE_LIMIT_HDD: - case EAttribute::VOLUME_SPACE_LIMIT_SSD: - case EAttribute::VOLUME_SPACE_LIMIT_SSD_NONREPL: - case EAttribute::VOLUME_SPACE_LIMIT_SSD_SYSTEM: - return CheckAttributeUint64(name, value, errStr); - case EAttribute::EXTRA_PATH_SYMBOLS_ALLOWED: - return CheckAttributeStringWithWeakCheck(name, value, errStr); - case EAttribute::DOCUMENT_API_VERSION: - if (op != EUserAttributesOp::CreateTable) { - errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateTable", name.c_str()); - return false; - } - return CheckAttributeUint64(name, value, errStr, /* minValue = */ 1); - case EAttribute::ASYNC_REPLICATION: - if (op != EUserAttributesOp::CreateChangefeed) { - errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateChangefeed", name.c_str()); - return false; - } - return CheckAttributeJson(name, value, errStr); - } - - Y_UNREACHABLE(); - } - - static bool CheckAttributeRemove(EUserAttributesOp op, const TString& name, TString& errStr) { - if (op == EUserAttributesOp::SyncUpdateTenants) { - // Migration, must never fail - return true; - } - - switch (ParseName(name)) { - case EAttribute::USER: - return true; - case EAttribute::UNKNOWN: - errStr = Sprintf("UserAttributes: unsupported attribute '%s'", name.c_str()); - return false; - case EAttribute::VOLUME_SPACE_LIMIT: - case EAttribute::VOLUME_SPACE_LIMIT_HDD: - case EAttribute::VOLUME_SPACE_LIMIT_SSD: - case EAttribute::VOLUME_SPACE_LIMIT_SSD_NONREPL: - case EAttribute::VOLUME_SPACE_LIMIT_SSD_SYSTEM: - case EAttribute::EXTRA_PATH_SYMBOLS_ALLOWED: - return true; - case EAttribute::DOCUMENT_API_VERSION: - if (op != EUserAttributesOp::CreateTable) { - errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateTable", name.c_str()); - return false; - } - return true; - case EAttribute::ASYNC_REPLICATION: - if (op != EUserAttributesOp::CreateChangefeed) { - errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateChangefeed", name.c_str()); - return false; - } - return true; - } - - Y_UNREACHABLE(); - } - - static bool CheckAttributeStringWithWeakCheck(const TString& name, const TString& value, TString& errStr) { - if (!IsValidPathName_WeakCheck(value)) { - errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s', forbidden symbols are found", - name.c_str(), value.c_str()); - return false; - } - return true; - } - - static bool CheckAttributeUint64(const TString& name, const TString& value, TString& errStr, ui64 minValue = 0, ui64 maxValue = Max<ui64>()) { - ui64 parsed; - if (!TryFromString(value, parsed)) { - errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s'", - name.c_str(), value.c_str()); - return false; - } - if (parsed < minValue) { - errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s' < %" PRIu64, - name.c_str(), value.c_str(), minValue); - return false; - } - if (parsed > maxValue) { - errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s' > %" PRIu64, - name.c_str(), value.c_str(), maxValue); - return false; - } - return true; - } - - static bool CheckAttributeUnknown(const std::pair<const TString, TString>& item, bool& ok, TString& errStr) { - Y_UNUSED(item); - ok = false; - errStr = Sprintf("UserAttributes::CheckLimits: unsupported attribute '%s'", item.first.c_str()); - return true; - } - - static bool CheckAttributeJson(const TString& name, const TString& value, TString& errStr) { - NJson::TJsonValue unused; - if (!NJson::ReadJsonTree(value, &unused)) { - errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s'", - name.c_str(), value.c_str()); - return false; - } - return true; - } -}; - struct TPathElement : TSimpleRefCount<TPathElement> { using TPtr = TIntrusivePtr<TPathElement>; using TChildrenCont = TMap<TString, TPathId>; @@ -434,5 +144,5 @@ public: bool HasRuntimeAttrs() const; void SerializeRuntimeAttrs(google::protobuf::RepeatedPtrField<NKikimrSchemeOp::TUserAttribute>* userAttrs) const; }; -} + } diff --git a/ydb/core/tx/schemeshard/user_attributes.cpp b/ydb/core/tx/schemeshard/user_attributes.cpp new file mode 100644 index 00000000000..b005c58eee1 --- /dev/null +++ b/ydb/core/tx/schemeshard/user_attributes.cpp @@ -0,0 +1,222 @@ +#include "user_attributes.h" +#include "schemeshard_user_attr_limits.h" + +#include <library/cpp/json/json_reader.h> + +#include <util/string/cast.h> +#include <util/string/printf.h> + +namespace NKikimr::NSchemeShard { + +inline bool WeakCheck(char c) { + // 33: ! " # $ % & ' ( ) * + , - . / + // 48: 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ + // 65: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + // 91: [ \ ] ^ _ ` + // 97: a b c d e f g h i j k l m n o p q r s t u v w x y z + // 123: { | } ~ + if (c >= 33 && c <= 126) + return true; + return false; +} + +inline bool IsValidPathName_WeakCheck(const TString& name) { + for (auto c : name) { + if (!WeakCheck(c) || c == '/') { + return false; + } + } + return true; +} + + TUserAttributes::TUserAttributes(ui64 version) + : AlterVersion(version) + {} + + TUserAttributes::TPtr TUserAttributes::CreateNextVersion() const { + auto result = new TUserAttributes(*this); + ++result->AlterVersion; + return result; + } + + EAttribute TUserAttributes::ParseName(TStringBuf name) { + if (name.StartsWith(ATTR_PREFIX)) { + #define HANDLE_ATTR(attr) \ + if (name == ATTR_ ## attr) { \ + return EAttribute::attr; \ + } + + HANDLE_ATTR(VOLUME_SPACE_LIMIT); + HANDLE_ATTR(VOLUME_SPACE_LIMIT_HDD); + HANDLE_ATTR(VOLUME_SPACE_LIMIT_SSD); + HANDLE_ATTR(VOLUME_SPACE_LIMIT_SSD_NONREPL); + HANDLE_ATTR(VOLUME_SPACE_LIMIT_SSD_SYSTEM); + HANDLE_ATTR(EXTRA_PATH_SYMBOLS_ALLOWED); + HANDLE_ATTR(DOCUMENT_API_VERSION); + HANDLE_ATTR(ASYNC_REPLICATION); + + #undef HANDLE_ATTR + return EAttribute::UNKNOWN; + } + + return EAttribute::USER; + } + + bool TUserAttributes::ApplyPatch(EUserAttributesOp op, const NKikimrSchemeOp::TAlterUserAttributes& patch, TString& errStr) { + return ApplyPatch(op, patch.GetUserAttributes(), errStr); + } + + void TUserAttributes::Set(const TString& name, const TString& value) { + Attrs[name] = value; + } + + ui32 TUserAttributes::Size() const { + return Attrs.size(); + } + + ui64 TUserAttributes::Bytes() const { + ui64 bytes = 0; + + for (const auto& [key, value] : Attrs) { + bytes += key.size(); + bytes += value.size(); + } + + return bytes; + } + + bool TUserAttributes::CheckLimits(TString& errStr) const { + const ui64 bytes = Bytes(); + if (bytes > TUserAttributesLimits::MaxBytes) { + errStr = Sprintf("UserAttributes::CheckLimits: user attributes too big: %" PRIu64, bytes); + return false; + } + + return true; + } + + bool TUserAttributes::CheckAttribute(EUserAttributesOp op, const TString& name, const TString& value, TString& errStr) { + if (op == EUserAttributesOp::SyncUpdateTenants) { + // Migration, must never fail + return true; + } + + if (name.size() > TUserAttributesLimits::MaxNameLen) { + errStr = Sprintf("UserAttributes: name too long, name# '%s' value# '%s'" + , name.c_str(), value.c_str()); + return false; + } + + if (value.size() > TUserAttributesLimits::MaxValueLen) { + errStr = Sprintf("UserAttributes: value too long, name# '%s' value# '%s'" + , name.c_str(), value.c_str()); + return false; + } + + switch (ParseName(name)) { + case EAttribute::USER: + return true; + case EAttribute::UNKNOWN: + errStr = Sprintf("UserAttributes: unsupported attribute '%s'", name.c_str()); + return false; + case EAttribute::VOLUME_SPACE_LIMIT: + case EAttribute::VOLUME_SPACE_LIMIT_HDD: + case EAttribute::VOLUME_SPACE_LIMIT_SSD: + case EAttribute::VOLUME_SPACE_LIMIT_SSD_NONREPL: + case EAttribute::VOLUME_SPACE_LIMIT_SSD_SYSTEM: + return CheckValueUint64(name, value, errStr); + case EAttribute::EXTRA_PATH_SYMBOLS_ALLOWED: + return CheckValueStringWeak(name, value, errStr); + case EAttribute::DOCUMENT_API_VERSION: + if (op != EUserAttributesOp::CreateTable) { + errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateTable", name.c_str()); + return false; + } + return CheckValueUint64(name, value, errStr, /* minValue = */ 1); + case EAttribute::ASYNC_REPLICATION: + if (op != EUserAttributesOp::CreateChangefeed) { + errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateChangefeed", name.c_str()); + return false; + } + return CheckValueJson(name, value, errStr); + } + + Y_UNREACHABLE(); + } + + bool TUserAttributes::CheckAttributeRemove(EUserAttributesOp op, const TString& name, TString& errStr) { + if (op == EUserAttributesOp::SyncUpdateTenants) { + // Migration, must never fail + return true; + } + + switch (ParseName(name)) { + case EAttribute::USER: + return true; + case EAttribute::UNKNOWN: + errStr = Sprintf("UserAttributes: unsupported attribute '%s'", name.c_str()); + return false; + case EAttribute::VOLUME_SPACE_LIMIT: + case EAttribute::VOLUME_SPACE_LIMIT_HDD: + case EAttribute::VOLUME_SPACE_LIMIT_SSD: + case EAttribute::VOLUME_SPACE_LIMIT_SSD_NONREPL: + case EAttribute::VOLUME_SPACE_LIMIT_SSD_SYSTEM: + case EAttribute::EXTRA_PATH_SYMBOLS_ALLOWED: + return true; + case EAttribute::DOCUMENT_API_VERSION: + if (op != EUserAttributesOp::CreateTable) { + errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateTable", name.c_str()); + return false; + } + return true; + case EAttribute::ASYNC_REPLICATION: + if (op != EUserAttributesOp::CreateChangefeed) { + errStr = Sprintf("UserAttributes: attribute '%s' can only be set during CreateChangefeed", name.c_str()); + return false; + } + return true; + } + + Y_UNREACHABLE(); + } + + bool TUserAttributes::CheckValueStringWeak(const TString& name, const TString& value, TString& errStr) { + if (!IsValidPathName_WeakCheck(value)) { + errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s', forbidden symbols are found", + name.c_str(), value.c_str()); + return false; + } + return true; + } + + bool TUserAttributes::CheckValueUint64(const TString& name, const TString& value, TString& errStr, ui64 minValue, ui64 maxValue) { + ui64 parsed; + if (!TryFromString(value, parsed)) { + errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s'", + name.c_str(), value.c_str()); + return false; + } + if (parsed < minValue) { + errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s' < %" PRIu64, + name.c_str(), value.c_str(), minValue); + return false; + } + if (parsed > maxValue) { + errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s' > %" PRIu64, + name.c_str(), value.c_str(), maxValue); + return false; + } + return true; + } + + bool TUserAttributes::CheckValueJson(const TString& name, const TString& value, TString& errStr) { + NJson::TJsonValue unused; + if (!NJson::ReadJsonTree(value, &unused)) { + errStr = Sprintf("UserAttributes: attribute '%s' has invalid value '%s'", + name.c_str(), value.c_str()); + return false; + } + return true; + } + +} diff --git a/ydb/core/tx/schemeshard/user_attributes.h b/ydb/core/tx/schemeshard/user_attributes.h new file mode 100644 index 00000000000..eacd3d54088 --- /dev/null +++ b/ydb/core/tx/schemeshard/user_attributes.h @@ -0,0 +1,96 @@ +#pragma once + +#include <ydb/core/protos/flat_scheme_op.pb.h> + +#include <util/generic/map.h> +#include <util/generic/ptr.h> + +namespace NKikimr::NSchemeShard { + +constexpr TStringBuf ATTR_PREFIX = "__"; +constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT = "__volume_space_limit"; +constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_HDD = "__volume_space_limit_hdd"; +constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_SSD = "__volume_space_limit_ssd"; +constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_SSD_NONREPL = "__volume_space_limit_ssd_nonrepl"; +constexpr TStringBuf ATTR_VOLUME_SPACE_LIMIT_SSD_SYSTEM = "__volume_space_limit_ssd_system"; +constexpr TStringBuf ATTR_EXTRA_PATH_SYMBOLS_ALLOWED = "__extra_path_symbols_allowed"; +constexpr TStringBuf ATTR_DOCUMENT_API_VERSION = "__document_api_version"; +constexpr TStringBuf ATTR_ASYNC_REPLICATION = "__async_replication"; + +enum class EAttribute { + USER, + UNKNOWN, + VOLUME_SPACE_LIMIT, + VOLUME_SPACE_LIMIT_HDD, + VOLUME_SPACE_LIMIT_SSD, + EXTRA_PATH_SYMBOLS_ALLOWED, // deprecated + VOLUME_SPACE_LIMIT_SSD_NONREPL, + DOCUMENT_API_VERSION, + VOLUME_SPACE_LIMIT_SSD_SYSTEM, + ASYNC_REPLICATION, +}; + +enum class EUserAttributesOp { + InitRoot, + MkDir, + AlterUserAttrs, + CreateTable, + CreateSubDomain, + CreateExtSubDomain, + SyncUpdateTenants, + CreateChangefeed, +}; + +struct TUserAttributes: TSimpleRefCount<TUserAttributes> { + using TPtr = TIntrusivePtr<TUserAttributes>; + + TMap<TString, TString> Attrs; + ui64 AlterVersion; + TPtr AlterData; + + explicit TUserAttributes(ui64 version); + TUserAttributes(const TUserAttributes&) = default; + + TPtr CreateNextVersion() const; + + static EAttribute ParseName(TStringBuf name); + + bool ApplyPatch(EUserAttributesOp op, const NKikimrSchemeOp::TAlterUserAttributes& patch, TString& errStr); + + template <class TContainer> + bool ApplyPatch(EUserAttributesOp op, const TContainer& patch, TString& errStr) { + for (const auto& item : patch) { + const auto& name = item.GetKey(); + + if (item.HasValue()) { + const auto& value = item.GetValue(); + if (!CheckAttribute(op, name, value, errStr)) { + return false; + } + + Attrs[name] = value; + } else { + if (!CheckAttributeRemove(op, name, errStr)) { + return false; + } + + Attrs.erase(name); + } + } + + return true; + } + + void Set(const TString& name, const TString& value); + ui32 Size() const; + ui64 Bytes() const; + bool CheckLimits(TString& errStr) const; + + static bool CheckAttribute(EUserAttributesOp op, const TString& name, const TString& value, TString& errStr); + static bool CheckAttributeRemove(EUserAttributesOp op, const TString& name, TString& errStr); + static bool CheckValueStringWeak(const TString& name, const TString& value, TString& errStr); + static bool CheckValueUint64(const TString& name, const TString& value, TString& errStr, ui64 minValue = 0, ui64 maxValue = Max<ui64>()); + static bool CheckValueJson(const TString& name, const TString& value, TString& errStr); +}; + +} diff --git a/ydb/core/tx/schemeshard/ya.make b/ydb/core/tx/schemeshard/ya.make index 4de0cbf7b3c..0ffa6b109b7 100644 --- a/ydb/core/tx/schemeshard/ya.make +++ b/ydb/core/tx/schemeshard/ya.make @@ -221,6 +221,7 @@ SRCS( schemeshard_build_index__progress.cpp schemeshard_validate_ttl.cpp operation_queue_timer.h + user_attributes.cpp ) GENERATE_ENUM_SERIALIZATION(schemeshard_info_types.h) |