aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorilnaz <ilnaz@ydb.tech>2023-03-24 16:58:17 +0300
committerilnaz <ilnaz@ydb.tech>2023-03-24 16:58:17 +0300
commit6897ec78eb22dfc9613232dc1cf6e2e6bb52c71c (patch)
treef5c7b5f1120ecf0bb5365942abd77c7b99f9cf41
parent4a19ef5d96f0b6af337c7b562e8626f067bc0cf7 (diff)
downloadydb-6897ec78eb22dfc9613232dc1cf6e2e6bb52c71c.tar.gz
(refactoring) User attrs
-rw-r--r--ydb/core/tx/schemeshard/CMakeLists.darwin-x86_64.txt1
-rw-r--r--ydb/core/tx/schemeshard/CMakeLists.linux-aarch64.txt1
-rw-r--r--ydb/core/tx/schemeshard/CMakeLists.linux-x86_64.txt1
-rw-r--r--ydb/core/tx/schemeshard/CMakeLists.windows-x86_64.txt1
-rw-r--r--ydb/core/tx/schemeshard/schemeshard_path_element.cpp30
-rw-r--r--ydb/core/tx/schemeshard/schemeshard_path_element.h298
-rw-r--r--ydb/core/tx/schemeshard/user_attributes.cpp222
-rw-r--r--ydb/core/tx/schemeshard/user_attributes.h96
-rw-r--r--ydb/core/tx/schemeshard/ya.make1
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)