docs: public static boolean isNullRejectingExpression(AbstractExpression expr, String tableAlias) {
          ExpressionType exprType = expr.getExpressionType();
          if (exprType == ExpressionType.CONJUNCTION_AND) {
              assert(expr.m_left != null && expr.m_right != null);
              return isNullRejectingExpression(expr.m_left, tableAlias) || isNullRejectingExpression(expr.m_right, tableAlias);
          }
          if (exprType == ExpressionType.CONJUNCTION_OR) {
              assert(expr.m_left != null && expr.m_right != null);
              return isNullRejectingExpression(expr.m_left, tableAlias) && isNullRejectingExpression(expr.m_right, tableAlias);
          }
          if (exprType == ExpressionType.COMPARE_NOTDISTINCT) {
              return false;
          }
          if (exprType == ExpressionType.OPERATOR_NOT) {
              assert(expr.m_left != null);
              // "NOT ( P and Q )" is as null-rejecting as "NOT P or NOT Q"
              // "NOT ( P or Q )" is as null-rejecting as "NOT P and NOT Q"
              // Handling AND and OR expressions requires a "negated" flag to the recursion that tweaks
              // (switches?) the handling of ANDs and ORs to enforce the above equivalences.
              if (expr.m_left.getExpressionType() == ExpressionType.OPERATOR_IS_NULL) {
                  return containsMatchingTVE(expr, tableAlias);
              }
              if (expr.m_left.getExpressionType() == ExpressionType.CONJUNCTION_AND ||
                      expr.m_left.getExpressionType() == ExpressionType.CONJUNCTION_OR) {
                  assert(expr.m_left.m_left != null && expr.m_left.m_right != null);
                  // Need to test for an existing child NOT and skip it.
                  // e.g. NOT (P AND NOT Q) --> (NOT P) OR NOT NOT Q --> (NOT P) OR Q
                  AbstractExpression tempLeft = null;
                  if (expr.m_left.m_left.getExpressionType() != ExpressionType.OPERATOR_NOT) {
                      tempLeft = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr.m_left.m_left, null);
                  }
                  else {
                      assert(expr.m_left.m_left.m_left != null);
                      tempLeft = expr.m_left.m_left.m_left;
                  }
                  AbstractExpression tempRight = null;
                  if (expr.m_left.m_right.getExpressionType() != ExpressionType.OPERATOR_NOT) {
                      tempRight = new OperatorExpression(ExpressionType.OPERATOR_NOT, expr.m_left.m_right, null);
                  }
                  else {
                      assert(expr.m_left.m_right.m_left != null);
                      tempRight = expr.m_left.m_right.m_left;
                  }
                  ExpressionType type = (expr.m_left.getExpressionType() == ExpressionType.CONJUNCTION_AND) ?
                          ExpressionType.CONJUNCTION_OR : ExpressionType.CONJUNCTION_AND;
                  AbstractExpression tempExpr = new OperatorExpression(type, tempLeft, tempRight);
                  return isNullRejectingExpression(tempExpr, tableAlias);
              }
              if (expr.m_left.getExpressionType() == ExpressionType.OPERATOR_NOT) {
                  // It's probably safe to assume that HSQL will have stripped out other double negatives,
                  // (like "NOT T.c IS NOT NULL"). Yet, we could also handle them here
                  assert(expr.m_left.m_left != null);
                  return isNullRejectingExpression(expr.m_left.m_left, tableAlias);
              }
              return isNullRejectingExpression(expr.m_left, tableAlias);
          }
          if (exprType == ExpressionType.COMPARE_NOTDISTINCT) {
              // IS NOT DISTINCT FROM is not NULL rejecting,
              // particularly when applied to pairs of NULL values.
              //TODO: There are subcases that actually are NULL rejecting,
              // with various degrees of easy detectability here, namely...
              // ...IS NOT DISTINCT FROM <non-null-constant>
              // ...IS NOT DISTINCT FROM <non-nullable-column>
              // ...IS NOT DISTINCT FROM <most-expressions-built-of-these>
              // but for now, we are lazy in the planner and possibly slower
              // at runtime, keeping the joins outer and relying more on the
              // runtime filters.
              return false;
          }
          if (exprType == ExpressionType.OPERATOR_IS_NULL) {
              // IS NOT NULL is NULL rejecting -- IS NULL is not
              return false;
          }
          if (expr.hasAnySubexpressionOfClass(OperatorExpression.class)) {
              // COALESCE expression is a sub-expression
              // For example, COALESCE (C1, C2) > 0
              List<OperatorExpression> coalesceExprs = expr.findAllSubexpressionsOfClass(OperatorExpression.class);
              for (OperatorExpression coalesceExpr : coalesceExprs) {
                  if ((coalesceExpr.getExpressionType() == ExpressionType.OPERATOR_ALTERNATIVE) &&
                      containsMatchingTVE(coalesceExpr, tableAlias)) {
                      // This table is part of the COALESCE expression - not NULL-rejecting
                      return false;
                  }
              }
              // If we get there it means that the tableAlias is not part of any of COALESCE expression
              // still need to check the catch all case
          }
          // @TODO ENG_3038 Is it safe to assume for the rest of the expressions that if
          // it contains a TVE with the matching table name then it is NULL rejection expression?
          // Presently, yes, logical expressions are not expected to appear inside other
          // generalized expressions, so since the handling of other kinds of expressions
          // is pretty much "containsMatchingTVE", this fallback should be safe.
          // The only planned developments that might contradict this restriction (AFAIK --paul)
          // would be support for standard pseudo-functions that take logical condition arguments.
          // These should probably be supported as special non-functions/operations for a number
          // of reasons and may need special casing here.
          return containsMatchingTVE(expr, tableAlias);
      }