Skip to content

Commit

Permalink
Add toSafePatch(...) and fromSafePatch(...) methods to StructPatch/Un…
Browse files Browse the repository at this point in the history
…ionPatch/AnyPatch (redo)

Reviewed By: thedavekwon

Differential Revision: D66397526

fbshipit-source-id: 551b4ba052c8e0f0f1b1cc43f4571a559355d456
  • Loading branch information
TJ Yin authored and facebook-github-bot committed Nov 24, 2024
1 parent 742539a commit 0169593
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 150 deletions.
30 changes: 6 additions & 24 deletions thrift/lib/cpp2/op/Patch.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,36 +149,18 @@ std::string prettyPrintPatch(
* required by `SafePatch`).
*/
template <typename T, typename Tag = type::infer_tag<T>>
op::patch_type<Tag> fromSafePatch(const op::safe_patch_type<Tag>& safePatch) {
if (safePatch.version() == 0) {
throw std::runtime_error("Invalid Safe Patch");
}
if (safePatch.version() > detail::kThriftStaticPatchVersion) {
throw std::runtime_error(
fmt::format("Unsupported patch version: {}", *safePatch.version()));
}
op::patch_type<Tag> patch;
CompactProtocolReader reader;
reader.setInput(safePatch.data()->get());
op::decode<type::infer_tag<op::patch_type<Tag>>>(reader, patch);
return patch;
[[deprecated("Use fromSafePatch(...) method instead.")]] op::patch_type<Tag>
fromSafePatch(const op::safe_patch_type<Tag>& safePatch) {
return op::patch_type<Tag>::fromSafePatch(safePatch);
}

/**
* Returns a `SafePatch` instance corresponding to the encoded Thrift Patch.
*/
template <typename T, typename Tag = type::infer_tag<T>>
op::safe_patch_type<Tag> toSafePatch(const op::patch_type<Tag>& patch) {
folly::IOBufQueue queue;
CompactProtocolWriter writer;
writer.setOutput(&queue);
op::encode<type::infer_tag<op::patch_type<Tag>>>(writer, patch);

op::safe_patch_type<Tag> safePatch;
safePatch.data() = queue.move();
safePatch.version() = detail::calculateMinSafePatchVersion(patch);

return safePatch;
[[deprecated("Use toSafePatch(...) method instead.")]] op::safe_patch_type<Tag>
toSafePatch(const op::patch_type<Tag>& patch) {
return patch.toSafePatch();
}

} // namespace apache::thrift::op
126 changes: 1 addition & 125 deletions thrift/lib/cpp2/op/detail/Patch.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,131 +25,7 @@
#include <thrift/lib/cpp2/Adapter.h>
#include <thrift/lib/cpp2/op/detail/BasePatch.h>
#include <thrift/lib/cpp2/op/detail/ContainerPatch.h>
#include <thrift/lib/cpp2/op/detail/PatchTraits.h>
#include <thrift/lib/cpp2/op/detail/StructPatch.h>
#include <thrift/lib/cpp2/op/detail/ValuePatch.h>
#include <thrift/lib/thrift/gen-cpp2/patch_op_types.h>

namespace apache::thrift::op::detail {

template <class>
class AnyPatch;

template <class>
class AssignPatch;

// Latest Thrift Static Patch version that the process is aware of. Any Thrift
// Static Patch with a version higher than this will not be processed by the
// binary. This is to ensure that the binary does not attempt to process a
// Thrift Static Patch that includes operations or features it does not support,
// which could lead to data corruption or other issues
inline constexpr int32_t kThriftStaticPatchVersion = 2;

// Adapter for all base types.
template <typename T>
using AssignPatchAdapter = InlineAdapter<AssignPatch<T>>;
template <typename T>
using BoolPatchAdapter = InlineAdapter<BoolPatch<T>>;
template <typename T>
using NumberPatchAdapter = InlineAdapter<NumberPatch<T>>;
template <typename T>
using StringPatchAdapter = InlineAdapter<StringPatch<T>>;
template <typename T>
using BinaryPatchAdapter = InlineAdapter<BinaryPatch<T>>;

// Adapters for structred types.
template <typename T>
using FieldPatchAdapter = InlineAdapter<FieldPatch<T>>;
template <typename T>
using StructPatchAdapter = InlineAdapter<StructPatch<T>>;
template <typename T>
using UnionPatchAdapter = InlineAdapter<UnionPatch<T>>;

// Adapters for containers.
template <typename T>
using ListPatchAdapter = InlineAdapter<ListPatch<T>>;
template <typename T>
using SetPatchAdapter = InlineAdapter<SetPatch<T>>;
template <typename T>
using MapPatchAdapter = InlineAdapter<MapPatch<T>>;

template <class>
constexpr inline bool is_map_patch_v = false;
template <class Patch>
constexpr inline bool is_map_patch_v<MapPatch<Patch>> = true;
template <class>
constexpr inline bool is_structured_patch_v = false;
template <class Patch>
constexpr inline bool is_structured_patch_v<StructPatch<Patch>> = true;
template <class Patch>
constexpr inline bool is_structured_patch_v<UnionPatch<Patch>> = true;
template <class>
constexpr inline bool is_any_patch_v = false;
template <class Patch>
constexpr inline bool is_any_patch_v<AnyPatch<Patch>> = true;

class MinSafePatchVersionVisitor {
public:
// Shared
template <typename T>
void assign(const T&) {}
void clear() {}
template <typename Patch>
void recurse(const Patch& patch) {
if constexpr (
is_map_patch_v<Patch> || is_structured_patch_v<Patch> ||
is_any_patch_v<Patch>) {
// Since all v2 operations are supported with AnyPatch, skip all other
// patches.
MinSafePatchVersionVisitor visitor;
patch.customVisit(visitor);
version = std::max(version, visitor.version);
}
}

// Container
template <typename T>
void add(const T&) {}
template <typename T>
void putMulti(const T&) {}
template <typename T>
void remove(const T&) {}
template <typename T>
void removeMulti(const T&) {}
template <typename Key, typename ValuePatch>
void patchIfSet(const folly::F14NodeMap<Key, ValuePatch>& patches) {
for (const auto& [k, vp] : patches) {
recurse(vp);
}
}

// Structured
template <typename>
void ensure() {}
template <typename, typename Field>
void ensure(const Field&) {}
template <typename>
void remove() {}
template <typename, typename FieldPatch>
void patchIfSet(const FieldPatch& fieldPatch) {
recurse(fieldPatch);
}

// Thrift Any
template <typename... T>
void patchIfTypeIs(T&&...) {
version = std::max(version, 2);
}
void ensureAny(const type::AnyStruct&) { version = std::max(version, 2); }

int32_t version = 1;
};

template <typename Patch>
int32_t calculateMinSafePatchVersion(const Patch& patch) {
// is_patch_v
MinSafePatchVersionVisitor visitor;
patch.customVisit(visitor);
return visitor.version;
}

} // namespace apache::thrift::op::detail
176 changes: 176 additions & 0 deletions thrift/lib/cpp2/op/detail/PatchTraits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <folly/container/F14Map.h>
#include <thrift/lib/cpp2/Adapter.h>
#include <thrift/lib/cpp2/type/Any.h>

namespace apache::thrift::op::detail {

template <class>
class AnyPatch;

template <class>
class BoolPatch;

template <class>
class NumberPatch;

template <class>
class StringPatch;

template <class>
class BinaryPatch;

template <class>
class AssignPatch;

template <class>
class FieldPatch;

template <class>
class StructPatch;

template <class>
class UnionPatch;

template <class>
class ListPatch;

template <class>
class SetPatch;

template <class>
class MapPatch;

// Latest Thrift Static Patch version that the process is aware of. Any Thrift
// Static Patch with a version higher than this will not be processed by the
// binary. This is to ensure that the binary does not attempt to process a
// Thrift Static Patch that includes operations or features it does not support,
// which could lead to data corruption or other issues
inline constexpr int32_t kThriftStaticPatchVersion = 2;

// Adapter for all base types.
template <typename T>
using AssignPatchAdapter = InlineAdapter<AssignPatch<T>>;
template <typename T>
using BoolPatchAdapter = InlineAdapter<BoolPatch<T>>;
template <typename T>
using NumberPatchAdapter = InlineAdapter<NumberPatch<T>>;
template <typename T>
using StringPatchAdapter = InlineAdapter<StringPatch<T>>;
template <typename T>
using BinaryPatchAdapter = InlineAdapter<BinaryPatch<T>>;

// Adapters for structred types.
template <typename T>
using FieldPatchAdapter = InlineAdapter<FieldPatch<T>>;
template <typename T>
using StructPatchAdapter = InlineAdapter<StructPatch<T>>;
template <typename T>
using UnionPatchAdapter = InlineAdapter<UnionPatch<T>>;

// Adapters for containers.
template <typename T>
using ListPatchAdapter = InlineAdapter<ListPatch<T>>;
template <typename T>
using SetPatchAdapter = InlineAdapter<SetPatch<T>>;
template <typename T>
using MapPatchAdapter = InlineAdapter<MapPatch<T>>;

template <class>
constexpr inline bool is_map_patch_v = false;
template <class Patch>
constexpr inline bool is_map_patch_v<MapPatch<Patch>> = true;
template <class>
constexpr inline bool is_structured_patch_v = false;
template <class Patch>
constexpr inline bool is_structured_patch_v<StructPatch<Patch>> = true;
template <class Patch>
constexpr inline bool is_structured_patch_v<UnionPatch<Patch>> = true;
template <class>
constexpr inline bool is_any_patch_v = false;
template <class Patch>
constexpr inline bool is_any_patch_v<AnyPatch<Patch>> = true;

class MinSafePatchVersionVisitor {
public:
// Shared
template <typename T>
void assign(const T&) {}
void clear() {}
template <typename Patch>
void recurse(const Patch& patch) {
if constexpr (
is_map_patch_v<Patch> || is_structured_patch_v<Patch> ||
is_any_patch_v<Patch>) {
// Since all v2 operations are supported with AnyPatch, skip all other
// patches.
MinSafePatchVersionVisitor visitor;
patch.customVisit(visitor);
version = std::max(version, visitor.version);
}
}

// Container
template <typename T>
void add(const T&) {}
template <typename T>
void putMulti(const T&) {}
template <typename T>
void remove(const T&) {}
template <typename T>
void removeMulti(const T&) {}
template <typename Key, typename ValuePatch>
void patchIfSet(const folly::F14NodeMap<Key, ValuePatch>& patches) {
for (const auto& [k, vp] : patches) {
recurse(vp);
}
}

// Structured
template <typename>
void ensure() {}
template <typename, typename Field>
void ensure(const Field&) {}
template <typename>
void remove() {}
template <typename, typename FieldPatch>
void patchIfSet(const FieldPatch& fieldPatch) {
recurse(fieldPatch);
}

// Thrift Any
template <typename... T>
void patchIfTypeIs(T&&...) {
version = std::max(version, 2);
}
void ensureAny(const type::AnyStruct&) { version = std::max(version, 2); }

int32_t version = 1;
};

template <typename Patch>
int32_t calculateMinSafePatchVersion(const Patch& patch) {
// is_patch_v
MinSafePatchVersionVisitor visitor;
patch.customVisit(visitor);
return visitor.version;
}

} // namespace apache::thrift::op::detail
Loading

0 comments on commit 0169593

Please sign in to comment.