Interface Compensation

  • All Superinterfaces:
    Function<ExpressionRef<RelationalExpression>,​RelationalExpression>
    All Known Subinterfaces:
    Compensation.WithPredicateCompensation
    All Known Implementing Classes:
    Compensation.ForMatch

    public interface Compensation
    extends Function<ExpressionRef<RelationalExpression>,​RelationalExpression>
    Interface for all kinds of compensation. A compensation is the byproduct of expression DAG matching. Matching two graphs Q and M may yield two sub graphs Q_s and M_s that match. Q_s and M_s may be completely equivalent to each other and Q_s can be substituted with M_s freely and vice versa. The requirement that Q_s and M_s have to be semantically equivalent for such a substitution, however, is not very useful. Normally, we try to find a materialized data set such as an index that can be utilized by the planner directly using a scan (requiring a complete match on the M-side, that is M_s equals M) instead of computing the result from the raw base data set. For those purposes, it makes more sense to relax the matching requirement to just require that the materialized view side of matching Q can subsume the query side M which means that executing M at least contains the result that executing Q would yield. That execution, however, may produce also extraneous records. Compensation corrects for that problem by applying certain post-operations such as filtering, distincting or resorting.

    Example in SQL terms:

    Query:

     
       SELECT *
       FROM recordType
       WHERE a = 3 AND b = 5
     
     

    Index:

     
       SELECT *
       FROM recordType
       WHERE a <comparison parameter p0>
       ORDER BY a
     
     

    A match for the two graphs created for both query and index side is:

     
       SELECT *
       FROM recordType
       WHERE a <p0 -> "= 3">
       ORDER BY a
     
     
    Using this graph we can now substitute the scan over the index but we will have to account for the unmatched second predicate in Q:
     
       SELECT *
       FROM index
       WHERE b = 5
     
     
    The query fragment that needs to be inserted to correct for extra records the index scan produces, that is the WHERE x = 5 is compensation. Compensation is computed either during the matching process or is computed after a complete match has been found utilizing helper structures such as PartialMatch and MatchInfo, which are themselves built during matching. Logic in DataAccessRule computes and applies compensation as needed when a complete index match has been found. A query sub graph can have multiple matches that could be utilized. In the example above, another index on b would also match but use b for the index scan and a predicate WHERE a = 3. Both match the query, and in fact both indexes can be utilized together by intersecting the index scans. For those cases, it becomes important to be able to intersect compensations as well such that only the minimally required combined compensation is required. In this example, no compensation is required as the individual indexes being intersected complement each other nicely.
    • Field Detail

      • NO_COMPENSATION

        static final Compensation NO_COMPENSATION
        Named object to indicate that this compensation is in fact no compensation, that is no additional operators need to be injected to compensate for a match.
      • IMPOSSIBLE_COMPENSATION

        static final Compensation IMPOSSIBLE_COMPENSATION
        Identity element for the intersection monoid defined by intersect(Compensation). Example for the usage pattern for that monoid: Let compensations be a Collection of Compensation. You can use Stream.reduce(T, java.util.function.BinaryOperator<T>) to create an intersection of all compensations in that collection.
         {code
         final Compensations intersectedCompensations =
           compensations
             .stream()
             .reduce(Compensation.impossibleCompensation(), Compensation::intersect);
         }
         
        Note that if compensations is empty, the result of the intersection is the impossible compensation.
    • Method Detail

      • isNeeded

        default boolean isNeeded()
        Returns if this compensation object needs to be applied in order to correct the result of a match.
        Returns:
        true if this compensation must be applied, false if this compensation is not needed. Note, by contract it is illegal to call Function.apply(T) on this compensation if this method returns false.
      • union

        @Nonnull
        default Compensation union​(@Nonnull
                                   Compensation otherCompensation)
        Union this compensation with another one passed in.
        Parameters:
        otherCompensation - other compensation to union this compensation with
        Returns:
        the new compensation representing the union of both compensations
      • intersect

        @Nonnull
        default Compensation intersect​(@Nonnull
                                       Compensation otherCompensation)
        Intersect this compensation with another one passed in.
        Parameters:
        otherCompensation - other compensation to intersect this compensation with
        Returns:
        the new compensation representing the intersection of both compensations
      • noCompensation

        @Nonnull
        static Compensation noCompensation()
        Returns a compensation which represents no compensation at all, i.e. it returns an object where isNeeded() returns false. That object cannot be applied.
        Returns:
        a compensation object that represents the absence of the need for compensation
      • impossibleCompensation

        @Nonnull
        static Compensation impossibleCompensation()
        Returns a compensation which represents the impossible compensation, i.e. it returns an object where isNeeded() returns true but that cannot be applied. This object is only needed to define the identity of the intersection monoid on compensations. One can imagine the impossible compensation to stand for the fact that compensation is needed (that is the match subsumes the query) but that the compensation itself cannot be computed.
        Returns:
        a compensation object that represents an impossible compensation
      • ofChildCompensationAndPredicateMap

        @Nonnull
        static Compensation ofChildCompensationAndPredicateMap​(@Nonnull
                                                               Compensation childCompensation,
                                                               @Nonnull
                                                               Map<QueryPredicate,​QueryPredicate> predicateCompensationMap)
        Returns a specific compensation object that uses a mapping between predicates from a query to predicates that are used for the application of the compensation.
        Parameters:
        childCompensation - a compensation that should be applied before the compensation being created in this method
        predicateCompensationMap - map that maps QueryPredicates of the query to QueryPredicates used for compensation
        Returns:
        a new compensation