This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 56d90714608 branch-4.2: [fix](cloud) Harden metadata key encoding
validation (#68457) (#69233)
56d90714608 is described below
commit 56d9071460828863b137a38aa6fe1124a6d97126
Author: Gavin Chou <[email protected]>
AuthorDate: Mon Oct 19 08:59:26 2026 +0800
branch-4.2: [fix](cloud) Harden metadata key encoding validation (#68457)
(#69233)
## Proposed changes
Backport #68457 to branch-4.2.
- Harden metadata key decoding against malformed tags, extra fields, and
truncated snapshot reference keys.
- Add independent golden coverage and verify HTTP key registry coverage.
- Keep the backport scoped to key types available on branch-4.2; the
Table Stream and TSO fence pieces from master are omitted because those
keyspaces are not present on this branch.
## Validation
- sh run-cloud-ut.sh --run --fdb fdb_cluster0:[email protected]:4500
--filter keys_test:*.*
- sh run-cloud-ut.sh --run --fdb fdb_cluster0:[email protected]:4500
--filter http_encode_key_test:*.*
- sh run-cloud-ut.sh --run --fdb fdb_cluster0:[email protected]:4500
--filter codec_test:*.*
---
cloud/src/meta-service/http_encode_key.cpp | 18 +++
cloud/src/meta-service/meta_service_http.h | 6 +
cloud/src/meta-store/codec.cpp | 2 +-
cloud/src/meta-store/keys.cpp | 34 ++++-
cloud/src/meta-store/keys.h | 44 ++++--
cloud/test/http_encode_key_test.cpp | 76 ++++++++--
cloud/test/keys_test.cpp | 230 ++++++++++++++++++++++-------
7 files changed, 327 insertions(+), 83 deletions(-)
diff --git a/cloud/src/meta-service/http_encode_key.cpp
b/cloud/src/meta-service/http_encode_key.cpp
index cae621cbfa9..19baf88cbf3 100644
--- a/cloud/src/meta-service/http_encode_key.cpp
+++ b/cloud/src/meta-service/http_encode_key.cpp
@@ -308,6 +308,24 @@ static std::unordered_map<std::string_view,
};
// clang-format on
+std::vector<std::string_view> get_supported_http_key_types() {
+ std::vector<std::string_view> key_types;
+ key_types.reserve(param_set.size());
+ for (const auto& entry : param_set) {
+ key_types.push_back(entry.first);
+ }
+ return key_types;
+}
+
+std::vector<std::string_view> get_supported_http_versioned_key_types() {
+ std::vector<std::string_view> key_types;
+ key_types.reserve(versioned_param_set.size());
+ for (const auto& entry : versioned_param_set) {
+ key_types.push_back(entry.first);
+ }
+ return key_types;
+}
+
static MetaServiceResponseStatus encode_key(const brpc::URI& uri, std::string*
key,
std::string* key_type = nullptr) {
MetaServiceResponseStatus status;
diff --git a/cloud/src/meta-service/meta_service_http.h
b/cloud/src/meta-service/meta_service_http.h
index 3c989483923..cb6b3a87e98 100644
--- a/cloud/src/meta-service/meta_service_http.h
+++ b/cloud/src/meta-service/meta_service_http.h
@@ -26,6 +26,7 @@
#include <string>
#include <string_view>
#include <unordered_map>
+#include <vector>
#include "common/util.h"
@@ -48,6 +49,11 @@ HttpResponse process_http_set_value(TxnKv* txn_kv,
brpc::Controller* ctrl);
HttpResponse process_http_encode_key(const brpc::URI& uri);
+// The order is unspecified. These lists let callers verify that tooling
covers every registered
+// key type without duplicating the registry itself.
+std::vector<std::string_view> get_supported_http_key_types();
+std::vector<std::string_view> get_supported_http_versioned_key_types();
+
/// Return the query value or an empty string if not exists.
inline static std::string_view http_query(const brpc::URI& uri, const char*
name) {
return uri.GetQuery(name) ? *uri.GetQuery(name) : std::string_view();
diff --git a/cloud/src/meta-store/codec.cpp b/cloud/src/meta-store/codec.cpp
index 2fbb3671a74..ba07fdceeb9 100644
--- a/cloud/src/meta-store/codec.cpp
+++ b/cloud/src/meta-store/codec.cpp
@@ -51,7 +51,7 @@ void encode_bytes(std::string_view bytes, std::string* b) {
* @return 0 for success
*/
int decode_bytes(std::string_view* in, std::string* out) {
- if (in->at(0) != EncodingTag::BYTES_TAG) return -1;
+ if (in->empty() || in->at(0) != EncodingTag::BYTES_TAG) return -1;
using byte = unsigned char;
in->remove_prefix(1); // Remove bytes marker
while (true) {
diff --git a/cloud/src/meta-store/keys.cpp b/cloud/src/meta-store/keys.cpp
index cffe5154bff..7dc321b6d33 100644
--- a/cloud/src/meta-store/keys.cpp
+++ b/cloud/src/meta-store/keys.cpp
@@ -360,7 +360,7 @@ void packed_file_key(const PackedFileKeyInfo& in,
std::string* out) {
void meta_pending_delete_bitmap_key(const MetaPendingDeleteBitmapInfo& in,
std::string* out) {
encode_prefix(in, out); // 0x01 "meta"
${instance_id}
encode_bytes(META_KEY_INFIX_DELETE_BITMAP_PENDING, out); //
"delete_bitmap_pending"
- encode_int64(std::get<1>(in), out); // table_id
+ encode_int64(std::get<1>(in), out); // tablet_id
}
void meta_schema_pb_dictionary_key(const MetaSchemaPBDictionaryInfo& in,
std::string* out) {
@@ -609,7 +609,7 @@ std::string version_key_prefix(std::string_view
instance_id) {
std::string index_key_prefix(std::string_view instance_id) {
std::string out;
out.push_back(CLOUD_VERSIONED_KEY_SPACE03);
- encode_bytes(INDEX_INDEX_KEY_INFIX, &out); // "version"
+ encode_bytes(INDEX_INDEX_KEY_INFIX, &out); // "index"
encode_bytes(instance_id, &out); // instance_id
return out;
}
@@ -953,7 +953,7 @@ bool decode_table_version_key(std::string_view* in,
int64_t* table_id, Versionst
std::vector<std::tuple<std::variant<int64_t, std::string>, int, int>> out;
auto res = decode_key(in, &out, timestamp);
- if (res != 0 || out.size() != 5) {
+ if (res != 0 || out.size() != 5 || std::get<1>(out.back()) !=
EncodingTag::VERSIONSTAMP_TAG) {
return false;
}
@@ -962,6 +962,7 @@ bool decode_table_version_key(std::string_view* in,
int64_t* table_id, Versionst
std::get<std::string>(std::get<0>(out[2])) !=
TABLE_VERSION_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*table_id = std::get<int64_t>(std::get<0>(out[3]));
} catch (const std::bad_variant_access& e) {
return false;
@@ -985,9 +986,11 @@ bool decode_partition_inverted_index_key(std::string_view*
in, int64_t* db_id, i
}
try {
- if (std::get<std::string>(std::get<0>(out[2])) !=
PARTITION_INVERTED_INDEX_KEY_INFIX) {
+ if (std::get<std::string>(std::get<0>(out[0])) != INDEX_KEY_PREFIX ||
+ std::get<std::string>(std::get<0>(out[2])) !=
PARTITION_INVERTED_INDEX_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*db_id = std::get<int64_t>(std::get<0>(out[3]));
*table_id = std::get<int64_t>(std::get<0>(out[4]));
*partition_id = std::get<int64_t>(std::get<0>(out[5]));
@@ -1008,7 +1011,7 @@ bool decode_meta_partition_key(std::string_view* in,
int64_t* partition_id,
std::vector<std::tuple<std::variant<int64_t, std::string>, int, int>> out;
auto res = decode_key(in, &out, timestamp);
- if (res != 0 || out.size() != 5) {
+ if (res != 0 || out.size() != 5 || std::get<1>(out.back()) !=
EncodingTag::VERSIONSTAMP_TAG) {
return false;
}
@@ -1017,6 +1020,7 @@ bool decode_meta_partition_key(std::string_view* in,
int64_t* partition_id,
std::get<std::string>(std::get<0>(out[2])) !=
META_PARTITION_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*partition_id = std::get<int64_t>(std::get<0>(out[3]));
} catch (const std::bad_variant_access& e) {
return false;
@@ -1034,7 +1038,7 @@ bool decode_meta_index_key(std::string_view* in, int64_t*
index_id, Versionstamp
std::vector<std::tuple<std::variant<int64_t, std::string>, int, int>> out;
auto res = decode_key(in, &out, timestamp);
- if (res != 0 || out.size() != 5) {
+ if (res != 0 || out.size() != 5 || std::get<1>(out.back()) !=
EncodingTag::VERSIONSTAMP_TAG) {
return false;
}
@@ -1043,6 +1047,7 @@ bool decode_meta_index_key(std::string_view* in, int64_t*
index_id, Versionstamp
std::get<std::string>(std::get<0>(out[2])) !=
META_INDEX_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*index_id = std::get<int64_t>(std::get<0>(out[3]));
} catch (const std::bad_variant_access& e) {
return false;
@@ -1069,6 +1074,7 @@ bool decode_meta_schema_key(std::string_view* in,
int64_t* index_id, int64_t* sc
std::get<std::string>(std::get<0>(out[2])) !=
META_KEY_INFIX_SCHEMA) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*index_id = std::get<int64_t>(std::get<0>(out[3]));
*schema_version = std::get<int64_t>(std::get<0>(out[4]));
} catch (const std::bad_variant_access& e) {
@@ -1087,7 +1093,7 @@ bool decode_meta_tablet_key(std::string_view* in,
int64_t* tablet_id, Versionsta
std::vector<std::tuple<std::variant<int64_t, std::string>, int, int>> out;
auto res = decode_key(in, &out, timestamp);
- if (res != 0 || out.size() < 5) {
+ if (res != 0 || out.size() != 5 || std::get<1>(out.back()) !=
EncodingTag::VERSIONSTAMP_TAG) {
return false;
}
@@ -1096,6 +1102,7 @@ bool decode_meta_tablet_key(std::string_view* in,
int64_t* tablet_id, Versionsta
std::get<std::string>(std::get<0>(out[2])) !=
META_KEY_INFIX_TABLET) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*tablet_id = std::get<int64_t>(std::get<0>(out[3]));
} catch (const std::bad_variant_access& e) {
return false;
@@ -1126,6 +1133,7 @@ bool decode_tablet_inverted_index_key(std::string_view*
in, int64_t* db_id, int6
std::get<std::string>(std::get<0>(out[2])) !=
TABLET_INVERTED_INDEX_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*db_id = std::get<int64_t>(std::get<0>(out[3]));
*table_id = std::get<int64_t>(std::get<0>(out[4]));
*index_id = std::get<int64_t>(std::get<0>(out[5]));
@@ -1182,7 +1190,9 @@ bool decode_snapshot_ref_key(std::string_view* in,
std::string* instance_id,
}
// Decode ref_instance_id
- if (ref_instance_id && decode_bytes(in, ref_instance_id) != 0) {
+ std::string ignored_ref_instance_id;
+ if (decode_bytes(in, ref_instance_id != nullptr ? ref_instance_id :
&ignored_ref_instance_id) !=
+ 0) {
return false;
}
@@ -1208,6 +1218,7 @@ bool decode_data_rowset_ref_count_key(std::string_view*
in, int64_t* tablet_id,
std::get<std::string>(std::get<0>(out[2])) !=
META_ROWSET_REF_COUNT_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*tablet_id = std::get<int64_t>(std::get<0>(out[3]));
*rowset_id = std::get<std::string>(std::get<0>(out[4]));
} catch (const std::bad_variant_access& e) {
@@ -1238,6 +1249,7 @@ bool decode_stats_tablet_key(std::string_view* in,
int64_t* table_id, int64_t* i
std::get<std::string>(std::get<0>(out[2])) !=
STATS_KEY_INFIX_TABLET) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*table_id = std::get<int64_t>(std::get<0>(out[3]));
*index_id = std::get<int64_t>(std::get<0>(out[4]));
*partition_id = std::get<int64_t>(std::get<0>(out[5]));
@@ -1268,6 +1280,7 @@ bool decode_table_version_key(std::string_view* in,
int64_t* db_id, int64_t* tbl
std::get<std::string>(std::get<0>(out[2])) !=
TABLE_VERSION_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*db_id = std::get<int64_t>(std::get<0>(out[3]));
*tbl_id = std::get<int64_t>(std::get<0>(out[4]));
} catch (const std::bad_variant_access& e) {
@@ -1296,6 +1309,7 @@ bool decode_tablet_schema_key(std::string_view* in,
int64_t* index_id, int64_t*
std::get<std::string>(std::get<0>(out[2])) !=
META_KEY_INFIX_SCHEMA) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*index_id = std::get<int64_t>(std::get<0>(out[3]));
*schema_version = std::get<int64_t>(std::get<0>(out[4]));
} catch (const std::bad_variant_access& e) {
@@ -1325,6 +1339,7 @@ bool decode_partition_version_key(std::string_view* in,
int64_t* db_id, int64_t*
std::get<std::string>(std::get<0>(out[2])) !=
PARTITION_VERSION_KEY_INFIX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*db_id = std::get<int64_t>(std::get<0>(out[3]));
*tbl_id = std::get<int64_t>(std::get<0>(out[4]));
*partition_id = std::get<int64_t>(std::get<0>(out[5]));
@@ -1355,6 +1370,7 @@ bool decode_meta_tablet_key(std::string_view* in,
int64_t* table_id, int64_t* in
std::get<std::string>(std::get<0>(out[2])) !=
META_KEY_INFIX_TABLET) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*table_id = std::get<int64_t>(std::get<0>(out[3]));
*index_id = std::get<int64_t>(std::get<0>(out[4]));
*partition_id = std::get<int64_t>(std::get<0>(out[5]));
@@ -1385,6 +1401,7 @@ bool decode_meta_rowset_key(std::string_view* in,
int64_t* tablet_id, int64_t* v
std::get<std::string>(std::get<0>(out[2])) !=
META_KEY_INFIX_ROWSET) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*tablet_id = std::get<int64_t>(std::get<0>(out[3]));
*version = std::get<int64_t>(std::get<0>(out[4]));
} catch (const std::bad_variant_access& e) {
@@ -1413,6 +1430,7 @@ bool decode_meta_tablet_idx_key(std::string_view* in,
int64_t* tablet_id) {
std::get<std::string>(std::get<0>(out[2])) !=
META_KEY_INFIX_TABLET_IDX) {
return false;
}
+ (void)std::get<std::string>(std::get<0>(out[1]));
*tablet_id = std::get<int64_t>(std::get<0>(out[3]));
} catch (const std::bad_variant_access& e) {
return false;
diff --git a/cloud/src/meta-store/keys.h b/cloud/src/meta-store/keys.h
index be05418979a..f3f1c356379 100644
--- a/cloud/src/meta-store/keys.h
+++ b/cloud/src/meta-store/keys.h
@@ -20,6 +20,8 @@
#include <cstdint>
#include <string>
#include <tuple>
+#include <type_traits>
+#include <utility>
#include <variant>
#include <vector>
@@ -32,7 +34,7 @@
//
// 0x01 "txn" ${instance_id} "txn_label" ${db_id} ${label}
-> TxnLabelPB ${version_timestamp}
// 0x01 "txn" ${instance_id} "txn_info" ${db_id} ${txn_id}
-> TxnInfoPB
-// 0x01 "txn" ${instance_id} "txn_db_tbl" ${txn_id}
-> TxnIndexPB
+// 0x01 "txn" ${instance_id} "txn_index" ${txn_id}
-> TxnIndexPB
// 0x01 "txn" ${instance_id} "txn_running" ${db_id} ${txn_id}
-> TxnRunningPB
//
// 0x01 "version" ${instance_id} "partition" ${db_id} ${tbl_id}
${partition_id} -> VersionPB
@@ -44,11 +46,11 @@
// 0x01 "meta" ${instance_id} "tablet_index" ${tablet_id}
-> TabletIndexPB
// 0x01 "meta" ${instance_id} "schema" ${index_id} ${schema_version}
-> TabletSchemaCloudPB
// 0x01 "meta" ${instance_id} "delete_bitmap_lock" ${table_id} ${partition_id}
-> DeleteBitmapUpdateLockPB
-// 0x01 "meta" ${instance_id} "delete_bitmap_pending" ${table_id}
-> PendingDeleteBitmapPB
+// 0x01 "meta" ${instance_id} "delete_bitmap_pending" ${tablet_id}
-> PendingDeleteBitmapPB
// 0x01 "meta" ${instance_id} "delete_bitmap" ${tablet_id} ${rowset_id}
${version} ${segment_id} -> roaringbitmap
// 0x01 "meta" ${instance_id} "tablet_schema_pb_dict" ${index_id}
-> SchemaCloudDictionary
-// 0x01 "meta" ${instance_id} "mow_tablet_job" ${table_id} ${initiator_id}
-> MowTabletJobPB
-// 0x01 "meta" ${instance_id} "packed_file" ${packed_file_path}
-> PackedFileInfoPB
+// 0x01 "meta" ${instance_id} "mow_tablet_job" ${table_id} ${initiator_id}
-> MowTabletJobPB
+// 0x01 "meta" ${instance_id} "packed_file" ${packed_file_path}
-> PackedFileInfoPB
//
// 0x01 "stats" ${instance_id} "tablet" ${table_id} ${index_id}
${partition_id} ${tablet_id} -> TabletStatsPB
// 0x01 "stats" ${instance_id} "tablet" ${table_id} ${index_id}
${partition_id} ${tablet_id} "data_size" -> int64
@@ -69,12 +71,13 @@
// 0x01 "job" ${instance_id} "check"
-> JobRecyclePB
// 0x01 "job" ${instance_id} "snapshot_data_migrator"
-> JobRecyclePB
// 0x01 "job" ${instance_id} "snapshot_chain_compactor"
-> JobRecyclePB
+// 0x01 "job" ${instance_id} "routine_load_progress" ${db_id} ${job_id}
-> RoutineLoadProgressPB
// 0x01 "job" ${instance_id} "streaming_job" ${db_id} ${job_id}
-> StreamingJobPB
//
// 0x01 "copy" ${instance_id} "job" ${stage_id} ${table_id} ${copy_id}
${group_id} -> CopyJobPB
// 0x01 "copy" ${instance_id} "loading_file" ${stage_id} ${table_id}
${obj_name} ${etag} -> CopyFilePB
//
-// 0x01 "storage_vault" ${instance_id} "vault" ${resource_id}
-> StorageVaultPB
+// 0x01 "storage_vault" ${instance_id} "vault" ${resource_id/vault_id}
-> StorageVaultPB
//
// 0x01 "job" ${instance_id} "restore_tablet" ${tablet_id}
-> RestoreJobCloudPB
// 0x01 "job" ${instance_id} "restore_rowset" ${tablet_id} ${version}
-> RowsetMetaCloudPB
@@ -131,17 +134,40 @@ static constexpr std::string_view
STATS_KEY_SUFFIX_SEGMENT_SIZE = "segment_size"
// clang-format off
/**
- * Wraps std::tuple for differnet types even if the underlying type is the
same.
+ * Wraps std::tuple to create different types even if the underlying type is
the same.
*
- * @param N for elemination of same underlying types of type alias when we use
- * `using` to declare a new type.
+ * @param N distinguishes type aliases that have the same underlying tuple
type.
*
* @param Base for base tuple, the underlying type
*/
template<size_t N, typename Base>
+struct BasicKeyInfo;
+
+template<typename T>
+struct IsBasicKeyInfo : std::false_type {};
+
+template<size_t N, typename Base>
+struct IsBasicKeyInfo<BasicKeyInfo<N, Base>> : std::true_type {};
+
+template<typename T>
+inline constexpr bool is_basic_key_info_v =
+ IsBasicKeyInfo<std::remove_cv_t<std::remove_reference_t<T>>>::value;
+
+template<size_t N, typename Base>
struct BasicKeyInfo : Base {
- template<typename... Args>
+ template<typename... Args,
+ std::enable_if_t<(!is_basic_key_info_v<Args> && ...), int> = 0>
BasicKeyInfo(Args&&... args) : Base(std::forward<Args>(args)...) {}
+
+ // KeyInfo wrappers with different semantics may share the same tuple
Base. Require
+ // cross-wrapper conversions to be explicit, and keep the forwarding
constructor from
+ // silently providing another conversion path.
+ template<size_t M>
+ explicit BasicKeyInfo(const BasicKeyInfo<M, Base>& other) : Base(other) {}
+
+ template<size_t M>
+ explicit BasicKeyInfo(BasicKeyInfo<M, Base>&& other) :
Base(std::move(other)) {}
+
constexpr static size_t n = N;
using base_type = Base;
};
diff --git a/cloud/test/http_encode_key_test.cpp
b/cloud/test/http_encode_key_test.cpp
index 1d29076313d..fff10c3a2c3 100644
--- a/cloud/test/http_encode_key_test.cpp
+++ b/cloud/test/http_encode_key_test.cpp
@@ -19,6 +19,8 @@
#include <gen_cpp/cloud.pb.h>
#include <gtest/gtest.h>
+#include <set>
+
#include "common/defer.h"
#include "common/logging.h"
#include "cpp/sync_point.h"
@@ -331,6 +333,8 @@ txn_id=126419752960)",
"01107374617473000110676176696e2d696e7374616e63650001107461626c6574000112000000000000276612000000000000271a1200000000000027101200000000000f63d9106e756d5f726f77730001",
"01107374617473000110676176696e2d696e7374616e63650001107461626c6574000112000000000000276612000000000000271a1200000000000027101200000000000f63d9106e756d5f726f77736574730001",
"01107374617473000110676176696e2d696e7374616e63650001107461626c6574000112000000000000276612000000000000271a1200000000000027101200000000000f63d9106e756d5f736567730001",
+
"01107374617473000110676176696e2d696e7374616e63650001107461626c6574000112000000000000276612000000000000271a1200000000000027101200000000000f63d910696e6465785f73697a650001",
+
"01107374617473000110676176696e2d696e7374616e63650001107461626c6574000112000000000000276612000000000000271a1200000000000027101200000000000f63d9107365676d656e745f73697a650001",
},
[]() -> std::vector<std::string> {
TabletStatsPB pb;
@@ -345,11 +349,11 @@ txn_id=126419752960)",
pb.set_num_segments(12);
pb.set_index_size(1);
pb.set_segment_size(1);
- return {pb.SerializeAsString(),
{"\x01\x00\x00\x00\x00\x00\x00\x00",8}, {"\x02\x00\x00\x00\x00\x00\x00\x00",8},
{"\x03\x00\x00\x00\x00\x00\x00\x00",8}, {"\x04\x00\x00\x00\x00\x00\x00\x00",8}};
+ return {pb.SerializeAsString(),
{"\x01\x00\x00\x00\x00\x00\x00\x00",8}, {"\x02\x00\x00\x00\x00\x00\x00\x00",8},
{"\x03\x00\x00\x00\x00\x00\x00\x00",8}, {"\x04\x00\x00\x00\x00\x00\x00\x00",8},
{"\x05\x00\x00\x00\x00\x00\x00\x00",8}, {"\x06\x00\x00\x00\x00\x00\x00\x00",8}};
},
R"(aggregated_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"10000","tablet_id":"1008601"},"data_size":"2","num_rows":"10","num_rowsets":"11","num_segments":"12","index_size":"1","segment_size":"1"}
-detached_stats:
{"data_size":"1","num_rows":"2","num_rowsets":"3","num_segments":"4","index_size":"0","segment_size":"0"}
-merged_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"10000","tablet_id":"1008601"},"data_size":"3","num_rows":"12","num_rowsets":"14","num_segments":"16","index_size":"1","segment_size":"1"}
+detached_stats:
{"data_size":"1","num_rows":"2","num_rowsets":"3","num_segments":"4","index_size":"5","segment_size":"6"}
+merged_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"10000","tablet_id":"1008601"},"data_size":"3","num_rows":"12","num_rowsets":"14","num_segments":"16","index_size":"6","segment_size":"7"}
)",
},
Input { // aggregated_stats + half detached_stats (num_segs == 0, there is
num_rowsets detached stats)
@@ -391,7 +395,7 @@ merged_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"100
idx->set_table_id(10086);
idx->set_index_id(100010);
idx->set_partition_id(10000);
- idx->set_tablet_id(1008602);
+ idx->set_tablet_id(1008603);
pb.set_data_size(2);
pb.set_num_rows(10);
pb.set_num_rowsets(11);
@@ -400,9 +404,9 @@ merged_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"100
pb.set_segment_size(1);
return {pb.SerializeAsString()};
},
- R"(aggregated_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"10000","tablet_id":"1008602"},"data_size":"2","num_rows":"10","num_rowsets":"11","num_segments":"12","index_size":"1","segment_size":"1"}
+ R"(aggregated_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"10000","tablet_id":"1008603"},"data_size":"2","num_rows":"10","num_rowsets":"11","num_segments":"12","index_size":"1","segment_size":"1"}
detached_stats:
{"data_size":"0","num_rows":"0","num_rowsets":"0","num_segments":"0","index_size":"0","segment_size":"0"}
-merged_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"10000","tablet_id":"1008602"},"data_size":"2","num_rows":"10","num_rowsets":"11","num_segments":"12","index_size":"1","segment_size":"1"}
+merged_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"10000","tablet_id":"1008603"},"data_size":"2","num_rows":"10","num_rowsets":"11","num_segments":"12","index_size":"1","segment_size":"1"}
)",
},
Input {
@@ -541,6 +545,30 @@ merged_stats:
{"idx":{"table_id":"10086","index_id":"100010","partition_id":"100
},
R"({"partition_to_offset":{"1000":"1234"}})",
},
+ Input {
+ "StorageVaultKey",
+ "instance_id=gavin-instance&vault_id=vault-id",
+
{"011073746f726167655f7661756c74000110676176696e2d696e7374616e63650001107661756c740001107661756c742d69640001"},
+ []() -> std::vector<std::string> {
+ StorageVaultPB pb;
+ pb.set_id("vault-id");
+ pb.set_name("vault-name");
+ return {pb.SerializeAsString()};
+ },
+ R"({"id":"vault-id","name":"vault-name"})",
+ },
+ Input {
+ "MetaSchemaPBDictionaryKey",
+ "instance_id=gavin-instance&index_id=10086",
+
{"01106d657461000110676176696e2d696e7374616e63650001107461626c65745f736368656d615f70625f646963740001120000000000002766"},
+ []() -> std::vector<std::string> {
+ doris::SchemaCloudDictionary pb;
+ pb.set_current_column_dict_id(1);
+ pb.set_current_index_dict_id(2);
+ return {pb.SerializeAsString()};
+ },
+ R"({"current_column_dict_id":"1","current_index_dict_id":"2"})",
+ },
Input {
"MetaServiceRegistryKey",
"",
@@ -596,7 +624,8 @@ TEST(HttpEncodeKeyTest,
process_http_encode_key_test_cover_all_template) {
EXPECT_EQ(uri.SetHttpURL(url.str()), 0); // clear and set query string
auto http_res = process_http_encode_key(uri);
EXPECT_EQ(http_res.status_code, 200);
- EXPECT_NE(http_res.body.find(input.key[0]), std::string::npos)
+ const std::string encoded_key_line = "\n" + input.key[0] + "\n";
+ EXPECT_NE(http_res.body.find(encoded_key_line), std::string::npos)
<< "real full text: " << http_res.body << "\nexpect contains:
" << input.key[0];
}
}
@@ -871,6 +900,27 @@ static auto versioned_test_inputs = std::array {
};
// clang-format on
+// Keep the HTTP key registries and their golden fixtures in one-to-one
correspondence.
+TEST(HttpEncodeKeyTest, RegisteredKeyTypesMatchGoldenFixtures) {
+ std::set<std::string_view> fixture_key_types;
+ for (const auto& input : test_inputs) {
+ fixture_key_types.insert(input.key_type);
+ }
+ const auto supported_key_types = get_supported_http_key_types();
+ const std::set<std::string_view>
registered_key_types(supported_key_types.begin(),
+
supported_key_types.end());
+ EXPECT_EQ(fixture_key_types, registered_key_types);
+
+ std::set<std::string_view> versioned_fixture_key_types;
+ for (const auto& input : versioned_test_inputs) {
+ versioned_fixture_key_types.insert(input.key_type);
+ }
+ const auto supported_versioned_key_types =
get_supported_http_versioned_key_types();
+ const std::set<std::string_view> registered_versioned_key_types(
+ supported_versioned_key_types.begin(),
supported_versioned_key_types.end());
+ EXPECT_EQ(versioned_fixture_key_types, registered_versioned_key_types);
+}
+
TEST(HttpEncodeKeyTest, process_http_encode_versioned_key_test) {
static auto format_fdb_key = [](const std::string& s) {
std::stringstream r;
@@ -891,7 +941,8 @@ TEST(HttpEncodeKeyTest,
process_http_encode_versioned_key_test) {
EXPECT_EQ(uri.SetHttpURL(url.str()), 0); // clear and set query string
auto http_res = process_http_encode_key(uri);
EXPECT_EQ(http_res.status_code, 200) << "Failed for key_type: " <<
input.key_type;
- EXPECT_NE(http_res.body.find(input.key[0]), std::string::npos)
+ const std::string encoded_key_line = "\n" + input.key[0] + "\n";
+ EXPECT_NE(http_res.body.find(encoded_key_line), std::string::npos)
<< "real full text: " << http_res.body << "\nexpect contains:
" << input.key[0]
<< "\n"
<< format_fdb_key(input.key[0]) << "\nkey_type: " <<
input.key_type
@@ -912,10 +963,11 @@ TEST(HttpEncodeKeyTest,
process_http_encode_versioned_key_with_versionstamp) {
auto http_res = process_http_encode_key(uri);
EXPECT_EQ(http_res.status_code, 200);
- // The encoded key should contain both the base key and the versionstamp
-
EXPECT_NE(http_res.body.find("031076657273696f6e000110676176696e2d696e7374616e63650001107061727"
- "46974696f6e0001120000000000002766"),
- std::string::npos);
+ const std::string encoded_key_line =
+
"\n031076657273696f6e000110676176696e2d696e7374616e6365000110706172746974696f6e"
+ "00011200000000000027661300000000000000010001ff\n";
+ EXPECT_NE(http_res.body.find(encoded_key_line), std::string::npos)
+ << "real full text: " << http_res.body << "\nexpect contains: " <<
encoded_key_line;
}
TEST(HttpGetValueTest, process_http_get_value_versioned_key_test) {
diff --git a/cloud/test/keys_test.cpp b/cloud/test/keys_test.cpp
index c6bd381ce14..a2baa2ccef3 100644
--- a/cloud/test/keys_test.cpp
+++ b/cloud/test/keys_test.cpp
@@ -24,6 +24,9 @@
#include <cstdint>
#include <cstring>
#include <iostream>
+#include <limits>
+#include <type_traits>
+#include <utility>
#include <vector>
#include "common/util.h"
@@ -58,31 +61,6 @@ void encode_bytes(std::string_view bytes, std::string* b);
int decode_bytes(std::string_view* in, std::string* out);
} // namespace doris::cloud
-// clang-format off
-// Possible key encoding schemas:
-//
-// 0x01 "instance" ${instance_id} -> InstanceInfoPB
-//
-// 0x01 "txn" ${instance_id} "txn_label" ${db_id} ${label} -> TxnLabelPB
${version_timestamp}
-// 0x01 "txn" ${instance_id} "txn_info" ${db_id} ${version_timestamp} ->
TxnInfoPB
-// 0x01 "txn" ${instance_id} "txn_index" ${version_timestamp} -> TxnIndexPB
-// 0x01 "txn" ${instance_id} "txn_running" ${db_id} ${version_timestamp} ->
TxnRunningPB // creaet at begin, delete at commit
-//
-// 0x01 "version" ${instance_id} "partition" ${db_id} ${tbl_id}
${partition_id} -> VersionPB
-//
-// 0x01 "meta" ${instance_id} "rowset" ${tablet_id} ${version} ${rowset_id} ->
RowsetMetaCloudPB
-// 0x01 "meta" ${instance_id} "rowset_tmp" ${txn_id} ${rowset_id} ->
RowsetMetaCloudPB
-// 0x01 "meta" ${instance_id} "tablet" ${table_id} ${tablet_id} ->
TabletMetaCloudPB
-// 0x01 "meta" ${instance_id} "tablet_table" ${tablet_id} -> ${table_id}
-// 0x01 "meta" ${instance_id} "tablet_tmp" ${table_id} ${tablet_id} ->
TabletMetaCloudPB
-//
-// 0x01 "trash" ${instacne_id} "table" -> TableTrashPB
-//
-// 0x01 "node_status" ${instance_id} "compute" ${backend_id} ->
ComputeNodeStatusPB
-//
-// 0x01 "job" ${instance_id} "streaming_job" ${db_id} ${job_id} ->
StreamingJobPB
-// clang-format on
-
TEST(KeysTest, InstanceKeyTest) {
using namespace doris::cloud;
@@ -106,6 +84,34 @@ TEST(KeysTest, InstanceKeyTest) {
EXPECT_EQ(instance_id, dec_instance_id);
}
+TEST(KeysTest, FixedEncodingGoldenTest) {
+ using namespace doris::cloud;
+
+ static_assert(!std::is_convertible_v<MetaRowsetKeyInfo,
MetaRowsetTmpKeyInfo>);
+ static_assert(std::is_constructible_v<MetaRowsetTmpKeyInfo,
MetaRowsetKeyInfo>);
+
+ EXPECT_EQ(hex(mow_tablet_job_key({"gavin-instance", 10086, 10010})),
+
"01106d657461000110676176696e2d696e7374616e63650001106d6f775f7461626c65745f"
+ "6a6f62000112000000000000276612000000000000271a");
+ EXPECT_EQ(hex(storage_vault_key({"gavin-instance", "vault-id"})),
+
"011073746f726167655f7661756c74000110676176696e2d696e7374616e6365000110766175"
+ "6c740001107661756c742d69640001");
+ EXPECT_EQ(hex(meta_schema_pb_dictionary_key({"gavin-instance", 10086})),
+
"01106d657461000110676176696e2d696e7374616e63650001107461626c65745f736368656d"
+ "615f70625f646963740001120000000000002766");
+
+ const std::string all_partitions_lock =
+ meta_delete_bitmap_update_lock_key({"gavin-instance", 10086, -1});
+ EXPECT_EQ(hex(all_partitions_lock),
+
"01106d657461000110676176696e2d696e7374616e636500011064656c6574655f6269746d61"
+ "705f6c6f636b0001120000000000002766110000000000000001");
+ EXPECT_LT(all_partitions_lock,
+ meta_delete_bitmap_update_lock_key({"gavin-instance", 10086,
0}));
+ EXPECT_LT(meta_delete_bitmap_update_lock_key({"gavin-instance", 10086, 0}),
+ meta_delete_bitmap_update_lock_key(
+ {"gavin-instance", 10086,
std::numeric_limits<int64_t>::max()}));
+}
+
TEST(KeysTest, MetaKeysTest) {
using namespace doris::cloud;
std::string instance_id = "instance_id_deadbeef";
@@ -150,18 +156,18 @@ TEST(KeysTest, MetaKeysTest) {
}
// tmp rowset meta key
- // 0x01 "meta" ${instance_id} "rowset_tmp" ${tablet_id} ${version}
+ // 0x01 "meta" ${instance_id} "rowset_tmp" ${txn_id} ${tablet_id}
{
- int64_t tablet_id = 10086;
- int64_t version = 100;
- MetaRowsetKeyInfo rowset_key {instance_id, tablet_id, version};
+ int64_t txn_id = 10086;
+ int64_t tablet_id = 100;
+ MetaRowsetTmpKeyInfo rowset_key {instance_id, txn_id, tablet_id};
std::string encoded_rowset_key0;
meta_rowset_tmp_key(rowset_key, &encoded_rowset_key0);
std::cout << hex(encoded_rowset_key0) << std::endl;
std::string dec_instance_id;
+ int64_t dec_txn_id = 0;
int64_t dec_tablet_id = 0;
- int64_t dec_version = 0;
std::string_view key_sv(encoded_rowset_key0);
std::string dec_meta_prefix;
@@ -170,17 +176,17 @@ TEST(KeysTest, MetaKeysTest) {
ASSERT_EQ(decode_bytes(&key_sv, &dec_meta_prefix), 0);
ASSERT_EQ(decode_bytes(&key_sv, &dec_instance_id), 0);
ASSERT_EQ(decode_bytes(&key_sv, &dec_rowset_prefix), 0);
- ASSERT_EQ(decode_int64(&key_sv, &dec_tablet_id), 0) << hex(key_sv);
- ASSERT_EQ(decode_int64(&key_sv, &dec_version), 0);
+ ASSERT_EQ(decode_int64(&key_sv, &dec_txn_id), 0) << hex(key_sv);
+ ASSERT_EQ(decode_int64(&key_sv, &dec_tablet_id), 0);
ASSERT_TRUE(key_sv.empty());
EXPECT_EQ("meta", dec_meta_prefix);
EXPECT_EQ("rowset_tmp", dec_rowset_prefix);
EXPECT_EQ(instance_id, dec_instance_id);
+ EXPECT_EQ(txn_id, dec_txn_id);
EXPECT_EQ(tablet_id, dec_tablet_id);
- EXPECT_EQ(version, dec_version);
- std::get<2>(rowset_key) = version + 1;
+ std::get<2>(rowset_key) = tablet_id + 1;
std::string encoded_rowset_key1;
meta_rowset_tmp_key(rowset_key, &encoded_rowset_key1);
std::cout << hex(encoded_rowset_key1) << std::endl;
@@ -227,7 +233,7 @@ TEST(KeysTest, MetaKeysTest) {
EXPECT_EQ(partition_id, dec_partition_id);
EXPECT_EQ(tablet_id, dec_tablet_id);
- std::get<2>(tablet_key) = tablet_id + 1;
+ std::get<4>(tablet_key) = tablet_id + 1;
std::string encoded_rowset_key1;
meta_tablet_key(tablet_key, &encoded_rowset_key1);
std::cout << hex(encoded_rowset_key1) << std::endl;
@@ -429,7 +435,7 @@ TEST(KeysTest, TxnKeysTest) {
ASSERT_GT(encoded_txn_index_key1, encoded_txn_index_key0);
}
- // 0x01 "txn" ${instance_id} "txn_info" ${db_id} ${version_timestamp} ->
TxnInfoPB
+ // 0x01 "txn" ${instance_id} "txn_info" ${db_id} ${txn_id} -> TxnInfoPB
{
int64_t db_id = 12345678;
int64_t txn_id = 10086;
@@ -474,7 +480,7 @@ TEST(KeysTest, TxnKeysTest) {
ASSERT_GT(encoded_txn_info_key2, encoded_txn_info_key0);
}
- // 0x01 "txn" ${instance_id} "txn_index" ${version_timestamp} -> TxnIndexPB
+ // 0x01 "txn" ${instance_id} "txn_index" ${txn_id} -> TxnIndexPB
{
int64_t txn_id = 12343212453;
TxnIndexKeyInfo txn_index_key_ {instance_id, txn_id};
@@ -508,7 +514,7 @@ TEST(KeysTest, TxnKeysTest) {
ASSERT_GT(encoded_txn_index_key1, encoded_txn_index_key0);
}
- // 0x01 "txn" ${instance_id} "txn_running" ${db_id} ${version_timestamp}
-> ${table_id_list}
+ // 0x01 "txn" ${instance_id} "txn_running" ${db_id} ${txn_id} ->
${table_id_list}
{
int64_t db_id = 98712345;
int64_t txn_id = 12343212453;
@@ -1064,7 +1070,7 @@ TEST(KeysTest, CopyKeysTest) {
EXPECT_EQ(group_id, dec_group_id);
}
- // 0x01 "copy" ${instance_id} "loading_files" ${stage_id} ${table_id}
${obj_name} ${etag} -> CopyFilePB
+ // 0x01 "copy" ${instance_id} "loading_file" ${stage_id} ${table_id}
${obj_name} ${etag} -> CopyFilePB
{
std::string stage_id = "9482049283";
int64_t table_id = 3745823784;
@@ -1104,20 +1110,6 @@ TEST(KeysTest, CopyKeysTest) {
}
}
-TEST(KeysTest, DecodeKeysTest) {
- using namespace doris::cloud;
- // clang-format off
- std::string key =
"011074786e000110696e7374616e63655f69645f646561646265656600011074786e5f696e646578000112000000000000271310696e736572745f336664356164313264303035346139622d386337373664333231386336616462370001";
- // clang-format on
- auto pretty_key = prettify_key(key);
- ASSERT_TRUE(!pretty_key.empty()) << key;
- std::cout << "\n" << pretty_key << std::endl;
-
- pretty_key = prettify_key(key, true);
- ASSERT_TRUE(!pretty_key.empty()) << key;
- std::cout << "\n" << pretty_key << std::endl;
-}
-
TEST(KeysTest, MetaSchemaPBDictionaryTest) {
using namespace doris::cloud;
std::string instance_id = "instance_id_meta_dict";
@@ -2408,6 +2400,23 @@ TEST(KeysTest, DecodeSnapshotRefKeyTest) {
SnapshotReferenceKeyInfo key_info {instance_id, timestamp,
ref_instance_id};
std::string encoded_key = snapshot_reference_key(key_info);
+ std::string prefix = snapshot_reference_key_prefix(instance_id);
+ EXPECT_TRUE(encoded_key.starts_with(prefix));
+ {
+ std::string_view prefix_view = prefix;
+ remove_versioned_space_prefix(&prefix_view);
+ std::string decoded_prefix;
+ std::string decoded_instance_id;
+ std::string decoded_infix;
+ ASSERT_EQ(decode_bytes(&prefix_view, &decoded_prefix), 0);
+ ASSERT_EQ(decode_bytes(&prefix_view, &decoded_instance_id), 0);
+ ASSERT_EQ(decode_bytes(&prefix_view, &decoded_infix), 0);
+ EXPECT_TRUE(prefix_view.empty());
+ EXPECT_EQ(decoded_prefix, "snapshot");
+ EXPECT_EQ(decoded_instance_id, instance_id);
+ EXPECT_EQ(decoded_infix, "reference");
+ }
+
// Test decode_snapshot_ref_key - decode all fields
{
std::string dec_instance_id;
@@ -2418,6 +2427,7 @@ TEST(KeysTest, DecodeSnapshotRefKeyTest) {
bool ret = decode_snapshot_ref_key(&key_view, &dec_instance_id,
&dec_timestamp,
&dec_ref_instance_id);
ASSERT_TRUE(ret);
+ EXPECT_TRUE(key_view.empty());
EXPECT_EQ(dec_instance_id, instance_id);
EXPECT_EQ(dec_timestamp, timestamp);
EXPECT_EQ(dec_ref_instance_id, ref_instance_id);
@@ -2429,6 +2439,7 @@ TEST(KeysTest, DecodeSnapshotRefKeyTest) {
std::string_view key_view = encoded_key;
bool ret = decode_snapshot_ref_key(&key_view, nullptr, nullptr,
&dec_ref_instance_id);
ASSERT_TRUE(ret);
+ EXPECT_TRUE(key_view.empty());
EXPECT_EQ(dec_ref_instance_id, ref_instance_id);
}
@@ -2439,6 +2450,7 @@ TEST(KeysTest, DecodeSnapshotRefKeyTest) {
std::string_view key_view = encoded_key;
bool ret = decode_snapshot_ref_key(&key_view, &dec_instance_id,
&dec_timestamp, nullptr);
ASSERT_TRUE(ret);
+ EXPECT_TRUE(key_view.empty());
EXPECT_EQ(dec_instance_id, instance_id);
EXPECT_EQ(dec_timestamp, timestamp);
}
@@ -2460,6 +2472,31 @@ TEST(KeysTest, DecodeSnapshotRefKeyTest) {
ASSERT_FALSE(ret);
}
+ // Every strict prefix of a valid key must be rejected without throwing.
+ for (size_t size = 0; size < encoded_key.size(); ++size) {
+ SCOPED_TRACE(size);
+ std::string_view key_view(encoded_key.data(), size);
+ EXPECT_FALSE(decode_snapshot_ref_key(&key_view, nullptr, nullptr,
nullptr));
+ }
+
+ // A successfully decoded key may retain a caller-owned suffix.
+ {
+ std::string key_with_suffix = encoded_key;
+ key_with_suffix.push_back('\0');
+ std::string_view key_view = key_with_suffix;
+ EXPECT_TRUE(decode_snapshot_ref_key(&key_view, nullptr, nullptr,
nullptr));
+ EXPECT_EQ(key_view, std::string_view("\0", 1));
+ }
+
+ // The timestamp field must carry a versionstamp tag, not merely another
decodable field.
+ {
+ std::string invalid_key = prefix;
+ encode_int64(1, &invalid_key);
+ encode_bytes(ref_instance_id, &invalid_key);
+ std::string_view key_view = invalid_key;
+ EXPECT_FALSE(decode_snapshot_ref_key(&key_view, nullptr, nullptr,
nullptr));
+ }
+
// Test with multiple ref_instance_ids to ensure uniqueness
{
std::string ref_id1 = "ref_instance_1";
@@ -2481,6 +2518,9 @@ TEST(KeysTest, DecodeSnapshotRefKeyTest) {
ASSERT_TRUE(decode_snapshot_ref_key(&key_view1, nullptr, nullptr,
&dec_ref1));
ASSERT_TRUE(decode_snapshot_ref_key(&key_view2, nullptr, nullptr,
&dec_ref2));
ASSERT_TRUE(decode_snapshot_ref_key(&key_view3, nullptr, nullptr,
&dec_ref3));
+ EXPECT_TRUE(key_view1.empty());
+ EXPECT_TRUE(key_view2.empty());
+ EXPECT_TRUE(key_view3.empty());
EXPECT_EQ(dec_ref1, ref_id1);
EXPECT_EQ(dec_ref2, ref_id2);
@@ -2488,6 +2528,90 @@ TEST(KeysTest, DecodeSnapshotRefKeyTest) {
}
}
+TEST(KeysTest, VersionedDecodersRequireExactSchema) {
+ using namespace doris::cloud;
+
+ const Versionstamp timestamp(123, 4);
+ auto append_timestamp = [&](std::string key) {
+ encode_versionstamp(timestamp, &key);
+ encode_versionstamp_end(&key);
+ return key;
+ };
+ auto append_int = [](std::string key) {
+ encode_int64(123, &key);
+ return key;
+ };
+
+ {
+ std::string valid_key =
+ append_timestamp(versioned::table_version_key({"instance-id",
100}));
+ std::string_view key_view = valid_key;
+ int64_t table_id = 0;
+ Versionstamp decoded_timestamp;
+ ASSERT_TRUE(versioned::decode_table_version_key(&key_view, &table_id,
&decoded_timestamp));
+ EXPECT_TRUE(key_view.empty());
+ EXPECT_EQ(table_id, 100);
+ EXPECT_EQ(decoded_timestamp, timestamp);
+
+ std::string invalid_key =
append_int(versioned::table_version_key({"instance-id", 100}));
+ key_view = invalid_key;
+ EXPECT_FALSE(versioned::decode_table_version_key(&key_view, &table_id,
&decoded_timestamp));
+
+ invalid_key.assign(1, static_cast<char>(CLOUD_VERSIONED_KEY_SPACE03));
+ encode_bytes("version", &invalid_key);
+ encode_int64(1, &invalid_key); // instance_id must be encoded as bytes.
+ encode_bytes("table", &invalid_key);
+ encode_int64(100, &invalid_key);
+ invalid_key = append_timestamp(std::move(invalid_key));
+ key_view = invalid_key;
+ EXPECT_FALSE(versioned::decode_table_version_key(&key_view, &table_id,
&decoded_timestamp));
+ }
+
+ {
+ std::string invalid_key =
append_int(versioned::meta_partition_key({"instance-id", 101}));
+ std::string_view key_view = invalid_key;
+ int64_t partition_id = 0;
+ Versionstamp decoded_timestamp;
+ EXPECT_FALSE(
+ versioned::decode_meta_partition_key(&key_view, &partition_id,
&decoded_timestamp));
+ }
+
+ {
+ std::string invalid_key =
append_int(versioned::meta_index_key({"instance-id", 102}));
+ std::string_view key_view = invalid_key;
+ int64_t index_id = 0;
+ Versionstamp decoded_timestamp;
+ EXPECT_FALSE(versioned::decode_meta_index_key(&key_view, &index_id,
&decoded_timestamp));
+ }
+
+ {
+ std::string invalid_key =
append_int(versioned::meta_tablet_key({"instance-id", 103}));
+ std::string_view key_view = invalid_key;
+ int64_t tablet_id = 0;
+ Versionstamp decoded_timestamp;
+ EXPECT_FALSE(versioned::decode_meta_tablet_key(&key_view, &tablet_id,
&decoded_timestamp));
+
+ invalid_key = versioned::meta_tablet_key({"instance-id", 103});
+ encode_int64(104, &invalid_key);
+ invalid_key = append_timestamp(std::move(invalid_key));
+ key_view = invalid_key;
+ EXPECT_FALSE(versioned::decode_meta_tablet_key(&key_view, &tablet_id,
&decoded_timestamp));
+ }
+
+ {
+ std::string invalid_key =
+ versioned::partition_inverted_index_key({"instance-id", 100,
101, 102});
+ ASSERT_GT(invalid_key.size(), 2);
+ invalid_key[2] = 'x'; // Corrupt the encoded "index" family prefix.
+ std::string_view key_view = invalid_key;
+ int64_t db_id = 0;
+ int64_t table_id = 0;
+ int64_t partition_id = 0;
+ EXPECT_FALSE(versioned::decode_partition_inverted_index_key(&key_view,
&db_id, &table_id,
+
&partition_id));
+ }
+}
+
TEST(KeysTest, DecodeInstanceKey) {
using namespace doris::cloud;
std::string instance_id = "instance_id_deadbeef";
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]