RexUnknownAs.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you 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.
 */
package org.apache.calcite.rex;

/** Policy for whether a simplified expression may instead return another
 * value.
 *
 * <p>In particular, it deals with converting three-valued logic (TRUE, FALSE,
 * UNKNOWN) to two-valued logic (TRUE, FALSE) for callers that treat the UNKNOWN
 * value the same as TRUE or FALSE.
 *
 * <p>Sometimes the three-valued version of the expression is simpler (has a
 * smaller expression tree) than the two-valued version. In these cases,
 * favor simplicity over reduction to two-valued logic.
 *
 * @see RexSimplify */
public enum RexUnknownAs {
  /** Policy that indicates that the expression is being used in a context
   * Where an UNKNOWN value is treated in the same way as FALSE. Therefore, when
   * simplifying the expression, it is acceptable for the simplified expression
   * to evaluate to FALSE in some situations where the original expression would
   * evaluate to UNKNOWN.
   *
   * <p>SQL predicates ({@code WHERE}, {@code ON}, {@code HAVING} and
   * {@code FILTER (WHERE)} clauses, a {@code WHEN} clause of a {@code CASE}
   * expression, and in {@code CHECK} constraints) all treat UNKNOWN as FALSE.
   *
   * <p>If the simplified expression never returns UNKNOWN, the simplifier
   * should make this clear to the caller, if possible, by marking its type as
   * {@code BOOLEAN NOT NULL}. */
  FALSE,

  /** Policy that indicates that the expression is being used in a context
   * Where an UNKNOWN value is treated in the same way as TRUE. Therefore, when
   * simplifying the expression, it is acceptable for the simplified expression
   * to evaluate to TRUE in some situations where the original expression would
   * evaluate to UNKNOWN.
   *
   * <p>This does not occur commonly in SQL. However, it occurs internally
   * during simplification. For example, "{@code WHERE NOT expression}"
   * evaluates "{@code NOT expression}" in a context that treats UNKNOWN as
   * FALSE; it is useful to consider that "{@code expression}" is evaluated in a
   * context that treats UNKNOWN as TRUE.
   *
   * <p>If the simplified expression never returns UNKNOWN, the simplifier
   * should make this clear to the caller, if possible, by marking its type as
   * {@code BOOLEAN NOT NULL}. */
  TRUE,

  /** Policy that indicates that the expression is being used in a context
   * Where an UNKNOWN value is treated as is. This occurs:
   *
   * <ul>
   *   <li>In any expression whose type is not {@code BOOLEAN}
   *   <li>In {@code BOOLEAN} expressions that are {@code NOT NULL}
   *   <li>In {@code BOOLEAN} expressions where {@code UNKNOWN} should be
   *       returned as is, for example in a {@code SELECT} clause, or within an
   *       expression such as an operand to {@code AND}, {@code OR} or
   *       {@code NOT}
   * </ul>
   *
   * <p>If you are unsure, use UNKNOWN. It is the safest option. */
  UNKNOWN;

  /** Returns {@link #FALSE} if {@code unknownAsFalse} is true,
   * {@link #UNKNOWN} otherwise. */
  public static RexUnknownAs falseIf(boolean unknownAsFalse) {
    return unknownAsFalse ? FALSE : UNKNOWN;
  }

  public boolean toBoolean() {
    switch (this) {
    case FALSE:
      return false;
    case TRUE:
      return true;
    default:
      throw new IllegalArgumentException("unknown");
    }
  }

  public RexUnknownAs negate() {
    switch (this) {
    case TRUE:
      return FALSE;
    case FALSE:
      return TRUE;
    default:
      return UNKNOWN;
    }
  }

  /** Combines this with another {@code RexUnknownAs} in the same way as the
   * three-valued logic of OR.
   *
   * <p>For example, {@code TRUE or FALSE} returns {@code TRUE};
   * {@code FALSE or UNKNOWN} returns {@code UNKNOWN}. */
  public RexUnknownAs or(RexUnknownAs other) {
    switch (this) {
    case TRUE:
      return this;
    case UNKNOWN:
      return other == TRUE ? other : this;
    case FALSE:
    default:
      return other;
    }
  }
}