Package com.apple.foundationdb.record
Interface PlanSerializable
- All Known Subinterfaces:
AggregateValue
,AvailableFields.CopyIfPredicate
,BooleanValue
,Comparisons.Comparison
,Comparisons.ComparisonWithParameter
,CreatesDynamicTypesValue
,IndexableAggregateValue
,IndexKeyValueToPartialRecord.Copier
,IndexScanParameters
,LeafQueryPredicate
,LeafValue
,MessageHelpers.CoercionBiFunction
,Ordering.DirectOrderPreservingValue
,Ordering.InverseOrderPreservingValue
,Ordering.OrderPreservingValue
,PredicateWithValue
,QuantifiedValue
,QueryPredicate
,QueryPredicateWithChild
,RecordQueryPlan
,RecordQueryPlanWithChild
,RecordQueryPlanWithChildren
,RecordQueryPlanWithComparisonKeyValues
,RecordQueryPlanWithComparisons
,RecordQueryPlanWithIndex
,RecordQueryPlanWithMatchCandidate
,RecordQueryPlanWithNoChildren
,RecordQuerySetPlan
,RecordQuerySetPlan.ComparisonKeyFunction
,StreamableAggregateValue
,Type
,Type.Erasable
,Value
,Value.IndexOnlyValue
,Value.InvertableValue<V>
,Value.NondeterministicValue
,Value.NonEvaluableValue
,Value.RangeMatchableValue
,ValueWithChild
- All Known Implementing Classes:
AbstractArrayConstructorValue
,AbstractArrayConstructorValue.LightArrayConstructorValue
,AbstractQueryPredicate
,AbstractValue
,AndOrPredicate
,AndOrValue
,AndPredicate
,ArithmeticValue
,AvailableFields.ConditionalUponPathPredicate
,AvailableFields.TruePredicate
,CollateValue
,Column
,Comparisons.InvertedFunctionComparison
,Comparisons.ListComparison
,Comparisons.MultiColumnComparison
,Comparisons.NullComparison
,Comparisons.OpaqueEqualityComparison
,Comparisons.ParameterComparison
,Comparisons.ParameterComparisonBase
,Comparisons.SimpleComparison
,Comparisons.SimpleComparisonBase
,Comparisons.TextComparison
,Comparisons.TextContainsAllPrefixesComparison
,Comparisons.TextWithMaxDistanceComparison
,Comparisons.ValueComparison
,CompatibleTypeEvolutionPredicate
,CompatibleTypeEvolutionPredicate.FieldAccessTrieNode
,ComposedBitmapIndexQueryPlan
,ConditionSelectorValue
,ConstantObjectValue
,ConstantPredicate
,ConstantValue
,CountValue
,DatabaseObjectDependenciesPredicate
,DatabaseObjectDependenciesPredicate.UsedIndex
,DerivedValue
,EmptyValue
,ExistsPredicate
,ExistsValue
,FieldValue
,FieldValue.FieldPath
,FieldValue.ResolvedAccessor
,FirstOrDefaultValue
,FromOrderedBytesValue
,InComparandSource
,IndexAggregateFunction
,IndexedValue
,IndexEntryObjectValue
,IndexKeyValueToPartialRecord
,IndexKeyValueToPartialRecord.FieldCopier
,IndexKeyValueToPartialRecord.FieldWithValueCopier
,IndexKeyValueToPartialRecord.MessageCopier
,IndexOnlyAggregateValue
,IndexOnlyAggregateValue.MaxEverValue
,IndexOnlyAggregateValue.MinEverValue
,IndexScanComparisons
,IndexScanType
,InOpValue
,InParameterSource
,InSource
,InValuesSource
,LikeOperatorValue
,LiteralValue
,MessageHelpers.CoercionTrieNode
,MessageHelpers.TransformationTrieNode
,MultidimensionalIndexScanComparisons
,NotPredicate
,NotValue
,NullValue
,NumericAggregationValue
,NumericAggregationValue.Avg
,NumericAggregationValue.BitmapConstructAgg
,NumericAggregationValue.Max
,NumericAggregationValue.Min
,NumericAggregationValue.Sum
,ObjectValue
,OfTypeValue
,OrPredicate
,PatternForLikeValue
,PickValue
,Placeholder
,PredicateWithValueAndRanges
,PromoteValue
,PromoteValue.ArrayCoercionBiFunction
,PromoteValue.PrimitiveCoercionBiFunction
,QuantifiedObjectValue
,QuantifiedRecordValue
,Quantifier.Physical
,QueriedValue
,QueryPlanConstraint
,RangeConstraints
,RangeConstraints.CompilableRange
,RankValue
,RecordConstructorValue
,RecordQueryAbstractDataModificationPlan
,RecordQueryAggregateIndexPlan
,RecordQueryChooserPlanBase
,RecordQueryComparatorPlan
,RecordQueryCoveringIndexPlan
,RecordQueryDamPlan
,RecordQueryDefaultOnEmptyPlan
,RecordQueryDeletePlan
,RecordQueryExplodePlan
,RecordQueryFetchFromPartialRecordPlan
,RecordQueryFilterPlan
,RecordQueryFilterPlanBase
,RecordQueryFirstOrDefaultPlan
,RecordQueryFlatMapPlan
,RecordQueryInComparandJoinPlan
,RecordQueryIndexPlan
,RecordQueryInJoinPlan
,RecordQueryInParameterJoinPlan
,RecordQueryInsertPlan
,RecordQueryIntersectionOnKeyExpressionPlan
,RecordQueryIntersectionOnValuesPlan
,RecordQueryIntersectionPlan
,RecordQueryInUnionOnKeyExpressionPlan
,RecordQueryInUnionOnValuesPlan
,RecordQueryInUnionPlan
,RecordQueryInValuesJoinPlan
,RecordQueryLoadByKeysPlan
,RecordQueryMapPlan
,RecordQueryPredicatesFilterPlan
,RecordQueryRangePlan
,RecordQueryRecursiveUnionPlan
,RecordQueryScanPlan
,RecordQueryScoreForRankPlan
,RecordQueryScoreForRankPlan.ScoreForRank
,RecordQuerySelectorPlan
,RecordQuerySetPlan.ComparisonKeyFunction.OnKeyExpression
,RecordQuerySetPlan.ComparisonKeyFunction.OnValues
,RecordQuerySortKey
,RecordQuerySortPlan
,RecordQueryStreamingAggregationPlan
,RecordQueryTextIndexPlan
,RecordQueryTypeFilterPlan
,RecordQueryUnionOnKeyExpressionPlan
,RecordQueryUnionOnValuesPlan
,RecordQueryUnionPlan
,RecordQueryUnionPlanBase
,RecordQueryUnorderedDistinctPlan
,RecordQueryUnorderedPrimaryKeyDistinctPlan
,RecordQueryUnorderedUnionPlan
,RecordQueryUpdatePlan
,RecordTypeKeyComparison.RecordTypeComparison
,RecordTypeValue
,RelOpValue
,RelOpValue.BinaryRelOpValue
,RelOpValue.UnaryRelOpValue
,ScanComparisons
,ScanComparisons.Builder
,SortedInComparandSource
,SortedInParameterSource
,SortedInValuesSource
,TempTableInsertPlan
,TempTableScanPlan
,ThrowsValue
,TimeWindowAggregateFunction
,TimeWindowForFunction
,TimeWindowScanComparisons
,ToOrderedBytesValue
,Type.Any
,Type.AnyRecord
,Type.Array
,Type.Enum
,Type.Enum.EnumValue
,Type.None
,Type.Null
,Type.Primitive
,Type.Record
,Type.Record.Field
,Type.Relation
,UdfValue
,ValuePredicate
,VariadicFunctionValue
,VersionValue
,WindowedValue
Base interface to indicate that a java class is reachable through a
Plan serialization and naturally the inverse, plan deserialization, is done using precompiled protobuf. There is some boilerplate that is necessary to be aware of when extending existing plan structures or adding new plan operators themselves.
All classes that are serializable for plan serialization purposes must implement
The inverse method called
Every class implementing
Now that we have a
The example we use here is for the record layer interface
RecordQueryPlan
and therefore needs to be capable of
serialization/deserialization. In addition to the toProto(PlanSerializationContext)
defined here
each implementor also needs to provide a public static fromProto(PlanSerializationContext, PClass)
method that we can dynamically dispatch to when deserializing a proto message.
Plan serialization and naturally the inverse, plan deserialization, is done using precompiled protobuf. There is some boilerplate that is necessary to be aware of when extending existing plan structures or adding new plan operators themselves.
All classes that are serializable for plan serialization purposes must implement
PlanSerializable
.
That interface complements PlanHashable
which is doing similar things to compute a deterministic and stable
hash code for a plan. Each class SomeClass
implementing PlanSerializable
has a counterpart protobuf
message called PSomeClass
. This convention was chosen in order to allow both classes to be referenced with
just regular imports within the same java class.
PlanSerializable
defines a method Message toProto(PlanSerializationContext)
that implementors must
provide. Note that this method should be covariant if possible. There are situations where that’s not possible in
which case we revert to a less elegant approach. In any case, the serialization framework will only interpret the
result as a proto Message. This method is used to transform from SomeClass to PSomeClass.
The inverse method called
fromProto(...)
requires a little more magic. One would naturally add that method
to PSomeClass
which is unfortunately not possible as PSomeClass
is generated by the protobuf
compiler. We instead add fromProto(...)
as a public static factory method to SomeClass
instead.
The full signature of this method is
@Nonnull
public static SomeClass fromProto(PlanSerializationContext sC,
PSomeClass someClassProto);
Every class implementing
PlanSerializable
is expected to implement this method as well.
Now that we have a
toProto(...)
and a fromProto(...)
, not everything is sorted out yet. Again, we are
in control of toProto(...)
which also happens to be implemented within the record layer class structure.
Therefore, we can easily instantiate the correct proto class and populate its fields. However, the opposite is not
true. If we were to call a fromProto()
method directly we may know the exact record layer class and could
potentially instantiate that class, however, a lot of record layer classes implement a common interface
(e.g. RecordQueryPlan
) and then refer to any kind of specific
plan just by using a reference to the interface or a superclass. We have to build that substitution principle
(we can substitute a reference to a super class or interface with a subclass respectively implementing class)
into the serialization framework.
The example we use here is for the record layer interface
Value
which has dozens of implementors. Value
itself extends quite a few interfaces of its own.
Value
has a protobuf correspondent called
PValue
which is defined as follows (incomplete):
message PValue {
extensions 5000 to max;
oneof specific_value {
PLightArrayConstructorValue light_array_constructor_value = 1;
PAndOrValue and_or_value = 2;
PArithmeticValue arithmetic_value = 3;
PConditionSelectorValue condition_selector_value = 4;
PConstantObjectValue constant_object_value = 5;
PConstantValue constant_value = 6;
PCountValue count_value = 7;
...
PBinaryRelOpValue binary_rel_op_value = 34;
PUnaryRelOpValue unary_rel_op_value = 35;
PVariadicFunctionValue variadic_function_value = 36;
PVersionValue version_value = 37;
}
}
PValue is a protobuf message that only ever holds exactly one actual implementing P-message. We could now provide
a fromProto(...)
in Value to create the correct object of the right implementing class. That, however, is
just boilerplate and quite tedious to maintain. We are effectively doing the dynamic dispatch we get for free on
the toProto(...)
path by hand. If there is no help at this point we were forced to write this code:
if (proto.hasLightArrayConstructorValue()) {
return LightArrayConstructorValue.fromProto(sC, proto.getLightArrayConstructorValue());
} else if (proto.hasAndOrValue()) {
return AndOrValue.fromProto(sC, proto.getAndOrValue());
} else if (proto.hasArithmeticValue()) {
return ArithmeticValue.fromProto(sC, proto.getArithmeticValue());
} else if (...) {
...
} else {
throw new RecordCoreException("you mad?");
}
This sort of logic is hard to maintain over time. Instead, we use an auto-service pattern on the interface
PlanDeserializer
that must also be implemented by every specific implementor of Value
that relates
proto-class and java class (in the example PLightArrayConstructorValue
and
LightArrayConstructorValue
:
@AutoService(PlanDeserializer.class)
public static class Deserializer implements PlanDeserializer<PLightArrayConstructorValue, LightArrayConstructorValue> {
@@Nonnull
@Override
public Class<PLightArrayConstructorValue> getProtoMessageClass() {
return PLightArrayConstructorValue.class;
}
@Nonnull
@Override
public LightArrayConstructorValue fromProto(final PlanSerializationContext serializationContext,
final PLightArrayConstructorValue lightArrayConstructorValueProto) {
return LightArrayConstructorValue.fromProto(serializationContext, lightArrayConstructorValueProto);
}
}
-
Method Summary
-
Method Details
-
toProto
-